From 9cebc2ca68afc4a79571c9055788238096580e8c Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Thu, 18 Aug 2022 12:34:05 +0300 Subject: [PATCH] remember me checkbox, slightly more useful email hint --- src/fe/scss/_form.scss | 4 ++++ .../features/login/login.controller.ts | 24 ++++++++++++++++--- .../features/settings/settings.controller.ts | 5 +++- views/login/login.pug | 11 +++++++-- views/settings/general.pug | 4 ++-- views/settings/security.pug | 8 +++---- 6 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/fe/scss/_form.scss b/src/fe/scss/_form.scss index e0a3df3..9ea9b99 100644 --- a/src/fe/scss/_form.scss +++ b/src/fe/scss/_form.scss @@ -32,3 +32,7 @@ input.form-control { border: 1px solid var(--form-border-hover); } } + +.form-checkbox { + margin-top: 0.75rem; +} diff --git a/src/modules/features/login/login.controller.ts b/src/modules/features/login/login.controller.ts index 67a51e6..02021ea 100644 --- a/src/modules/features/login/login.controller.ts +++ b/src/modules/features/login/login.controller.ts @@ -50,10 +50,12 @@ export class LoginController { public async loginRequest( @Req() req: Request, @Res() res: Response, - @Body() body: { username: string; password: string }, + @Body() body: { username: string; password: string; remember: boolean }, @Query('redirectTo') redirectTo?: string, ) { - const { username, password } = this.formUtil.trimmed(body, ['username']); + const { username, password, remember } = this.formUtil.trimmed(body, [ + 'username', + ]); const user = await this.userService.getByUsername(username); // User exists and password matches @@ -73,7 +75,7 @@ export class LoginController { } if (await this.totpService.userHasTOTP(user)) { - const challenge = { type: 'verify', user: user.uuid }; + const challenge = { type: 'verify', user: user.uuid, remember }; req.session.challenge = await this.token.encryptChallenge(challenge); res.redirect( '/login/verify' + (redirectTo ? '?redirectTo=' + redirectTo : ''), @@ -81,6 +83,13 @@ export class LoginController { return; } + // Extend session cookie to a month + if (remember) { + const month = 30 * 24 * 60 * 60 * 1000; + req.session.cookie.maxAge = month; + req.session.cookie.expires = new Date(Date.now() + month); + } + req.session.user = user.uuid; res.redirect(redirectTo ? decodeURIComponent(redirectTo) : '/'); } @@ -114,6 +123,7 @@ export class LoginController { @Query('redirectTo') redirectTo?: string, ) { let user: User; + let remember = false; try { if (!session.challenge) { @@ -129,6 +139,8 @@ export class LoginController { if (!user) { throw new Error('Bad challenge'); } + + remember = challenge.remember; } catch (e: any) { req.flash('message', { error: true, @@ -154,6 +166,12 @@ export class LoginController { return; } + // Extend session cookie to a month + if (remember) { + const month = 30 * 24 * 60 * 60 * 1000; + req.session.cookie.maxAge = month; + } + session.challenge = null; session.user = user.uuid; res.redirect(redirectTo ? decodeURIComponent(redirectTo) : '/'); diff --git a/src/modules/features/settings/settings.controller.ts b/src/modules/features/settings/settings.controller.ts index ad383cb..272b562 100644 --- a/src/modules/features/settings/settings.controller.ts +++ b/src/modules/features/settings/settings.controller.ts @@ -172,7 +172,10 @@ export class SettingsController { @Render('settings/security') public async security(@Req() req: Request) { const mailSplit = req.user.email.split('@'); - const emailHint = `${mailSplit[0].substring(0, 1)}***@${mailSplit[1]}`; + const asterisks = ''.padStart(mailSplit[0].substring(1).length, '*'); + const emailHint = `${mailSplit[0].substring(0, 1)}${asterisks}@${ + mailSplit[1] + }`; const twofactor = await this._totp.userHasTOTP(req.user); return this._form.populateTemplate(req, { user: req.user, diff --git a/views/login/login.pug b/views/login/login.pug index f215c9b..0989418 100644 --- a/views/login/login.pug +++ b/views/login/login.pug @@ -23,11 +23,18 @@ block body input.form-control#username(type="text", name="username", placeholder="Username", autofocus, value=form.username) label.form-label(for="password") Password input.form-control#password(type="password", name="password", placeholder="Password") + div.form-checkbox + input.form-control#remember(type="checkbox", name="remember", checked=form.remember) + label(for="remember") Remember me button.btn.btn-primary(type="submit") Log in div.btn-group.align-self-end a.btn.btn-link(type="button" href="/register" + (query ? ('?' + query) : '')) Create a new account |• a.btn.btn-link(type="button" href="/login/password") Forgot password? div.center-box-addon - p Icy Network is a Single-Sign-On service used by other applications. - p The website may use temporary cookies for storing your login session. + p + b Icy Network is a Single-Sign-On service used by other applications. + p The website may use temporary cookies for storing your login session and ensuring your security. + | This web service is  + a(href="https://gitlab.icynet.eu/IcyNetwork/icynet-auth-server", target="_blank") completely open source + | and can be audited by anyone. diff --git a/views/settings/general.pug b/views/settings/general.pug index 4c93f34..0ec13b4 100644 --- a/views/settings/general.pug +++ b/views/settings/general.pug @@ -36,13 +36,13 @@ block settings form(method="post", action="/account/avatar/delete") input(type="hidden", name="_csrf", value=csrf) button.btn.btn-link#remove-avatar(type="submit") Remove avatar - + form(method="post", data-noscript, action="/account/avatar", enctype="multipart/form-data") div.form-container input(type="hidden", name="_csrf", value=csrf) label.form-label(for="image") Image input.form-control#image(type="file", name="file") - small.form-hint Must be less than 10 MB and 1:1 aspect ratio. Enable JavaScript to custom crop your image. + small.form-hint Must be less than 10 MB and 1:1 aspect ratio (square). Enable JavaScript to custom crop your image. button.btn.btn-primary(type="submit") Change .modal#avatar-modal(data-modal="avatar", style="display: none") .modal__content diff --git a/views/settings/security.pug b/views/settings/security.pug index d9c0d71..704e90f 100644 --- a/views/settings/security.pug +++ b/views/settings/security.pug @@ -17,7 +17,7 @@ block settings h2 Change Password form(method="post", action="/account/security/password", autocomplete="off") div.form-container - input#csrfa(type="hidden", name="_csrf", value=csrf) + input#csrf(type="hidden", name="_csrf", value=csrf) label.form-label(for="password") Current Password input.form-control#password(type="password", name="password") label.form-label(for="new_password") New Password @@ -29,7 +29,7 @@ block settings h2 Change Email Address form(method="post", action="/account/security/email", autocomplete="off") div.form-container - input#csrfb(type="hidden", name="_csrf", value=csrf) + input(type="hidden", name="_csrf", value=csrf) label.form-label(for="current_email") Current Email Address input.form-control#current_email(type="email", name="current_email") small.form-hint Hint: #{emailHint} @@ -42,9 +42,9 @@ block settings p Two-factor authentication is enabled. a.btn.btn-primary(href="/account/two-factor/disable") Disable else - p You can enable two-factor authentication using an authenticator app of your choice, such as + p You can enable two-factor authentication using an authenticator app of your choice, such as b Google Authenticator - | or + | or b andOTP |. a.btn.btn-primary(href="/account/two-factor/activate") Activate