d6a45f5be36053cc4e2b96f2ac6fde479b1fdf50
[squeep-authentication-module] / lib / template / login-html.js
1 'use strict';
2
3 const { TemplateHelper: th } = require('@squeep/html-template-helper');
4
5 /**
6 * Login form.
7 */
8 function indieAuthSection(ctx, options) {
9 const indieAuthBlurb = (options.indieAuthBlurb || []).map((x) => '\t'.repeat(6) + x).join('\n');
10 const showIndieAuthForm = options.authnEnabled.includes('indieAuth');
11 return showIndieAuthForm ? `\t\t\t<section class="indieauth">
12 \t\t\t\t<h2>Login</h2>
13 \t\t\t\t<form action="" method="POST">
14 \t\t\t\t\t<fieldset>
15 \t\t\t\t\t\t<legend>IndieAuth</legend>
16 \t\t\t\t\t\t<label for="me">Profile URL:</label>
17 \t\t\t\t\t\t<input id="me" name="me" type="url" size="40" placeholder="https://example.com/my_profile_url" value="" autofocus>
18 \t\t\t\t\t\t<input type="hidden" name="me_auto_scheme">
19 \t\t\t\t\t\t<button>Login</button>
20 ${indieAuthBlurb}
21 \t\t\t\t\t</fieldset>
22 \t\t\t\t</form>
23 \t\t\t</section>`
24 : '';
25 }
26
27
28 /**
29 * Default all URL inputs to https if scheme not specified,
30 * and set a flag if that happened.
31 * From https://aaronparecki.com/2019/05/13/2/https
32 */
33 function indieAuthURLTrySecureFirstScript(ctx, options) {
34 const showIndieAuthForm = options.authnEnabled.includes('indieAuth');
35 return showIndieAuthForm ? `
36 <script>
37 document.addEventListener('DOMContentLoaded', function() {
38 function addDefaultScheme(target) {
39 if (target.value.match(/^(?!https?:).+\\..+/)) {
40 const autoSchemeField = document.querySelector('input[name=' + target.getAttribute('name') + '_auto_scheme]');
41 let scheme;
42 if (autoSchemeField) {
43 scheme = 'https://';
44 autoSchemeField.value = '1';
45 } else {
46 scheme = 'http://';
47 }
48 target.value = scheme + target.value;
49 }
50 }
51 const elements = document.querySelectorAll('input[type=url]');
52 Array.prototype.forEach.call(elements, function (el, i) {
53 el.addEventListener('blur', function(e) {
54 addDefaultScheme(e.target);
55 });
56 el.addEventListener('keydown', function(e) {
57 if (e.keyCode === 13) {
58 addDefaultScheme(e.target);
59 }
60 });
61 });
62 });
63 </script>` : '';
64 }
65
66 const userAuthn = ['argon2', 'pam', 'DEBUG_ANY'];
67 function userSection(ctx, options) {
68 const userBlurb = (options.userBlurb || []).map((x) => '\t'.repeat(6) + x).join('\n');
69 const secure = (ctx.clientProtocol || '').toLowerCase() === 'https';
70 const showUserForm = options.authnEnabled.filter((x) => userAuthn.includes(x)).length
71 && (secure || !options.secureAuthOnly);
72 return showUserForm ? `\t\t\t<section class="user">
73 \t\t\t\t<form action="" method="POST">
74 \t\t\t\t\t<fieldset>
75 \t\t\t\t\t\t<legend>User Account</legend>
76 \t\t\t\t\t\t<label for="identifier">Username:</label>
77 \t\t\t\t\t\t<input id="identifier" name="identifier" value="">
78 \t\t\t\t\t\t<br>
79 \t\t\t\t\t\t<label for="credential">Password:</label>
80 \t\t\t\t\t\t<input id="credential" name="credential" type="password" value="">
81 \t\t\t\t\t\t<br>
82 \t\t\t\t\t\t<button>Login</button>
83 ${userBlurb}
84 \t\t\t\t\t</fieldset>
85 \t\t\t\t</form>
86 \t\t\t</section>`
87 : '';
88 }
89
90
91 /**
92 * Render login form for both local and profile authentication.
93 * @param {Object} ctx
94 * @param {String[]=} ctx.errors
95 * @param {String} ctx.clientProtocol
96 * @param {Object} options
97 * @param {Boolean} options.authenticator.secureAuthOnly
98 * @param {String[]=} options.authenticator.loginBlurb
99 * @param {String[]=} options.authenticator.indieAuthBlurb
100 * @param {String[]=} options.authenticator.userBlurb
101 * @param {Object} options.manager
102 * @param {String} options.manager.pageTitle
103 * @param {String=} options.manager.logoUrl
104 * @param {Object} options.dingus
105 * @param {String} options.dingus.selfBaseUrl
106 * @returns {String}
107 */
108 module.exports = (ctx, options) => {
109 const htmlOptions = {
110 pageTitle: options.manager.pageTitle,
111 logoUrl: options.manager.logoUrl,
112 footerEntries: options.manager.footerEntries,
113 secureAuthOnly: options.authenticator.secureAuthOnly,
114 authnEnabled: options.authenticator.authnEnabled,
115 indieAuthBlurb: options.authenticator.indieAuthBlurb,
116 userBlurb: options.authenticator.userBlurb,
117 };
118 const mainContent = [
119 ...(options.authenticator.loginBlurb || []),
120 indieAuthURLTrySecureFirstScript(ctx, htmlOptions),
121 indieAuthSection(ctx, htmlOptions),
122 userSection(ctx, htmlOptions),
123 ];
124 return th.htmlPage(2, ctx, htmlOptions, mainContent);
125 };