remember me checkbox, slightly more useful email hint

This commit is contained in:
Evert Prants 2022-08-18 12:34:05 +03:00
parent 545a71e57d
commit 9cebc2ca68
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
6 changed files with 44 additions and 12 deletions

View File

@ -32,3 +32,7 @@ input.form-control {
border: 1px solid var(--form-border-hover); border: 1px solid var(--form-border-hover);
} }
} }
.form-checkbox {
margin-top: 0.75rem;
}

View File

@ -50,10 +50,12 @@ export class LoginController {
public async loginRequest( public async loginRequest(
@Req() req: Request, @Req() req: Request,
@Res() res: Response, @Res() res: Response,
@Body() body: { username: string; password: string }, @Body() body: { username: string; password: string; remember: boolean },
@Query('redirectTo') redirectTo?: string, @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); const user = await this.userService.getByUsername(username);
// User exists and password matches // User exists and password matches
@ -73,7 +75,7 @@ export class LoginController {
} }
if (await this.totpService.userHasTOTP(user)) { 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); req.session.challenge = await this.token.encryptChallenge(challenge);
res.redirect( res.redirect(
'/login/verify' + (redirectTo ? '?redirectTo=' + redirectTo : ''), '/login/verify' + (redirectTo ? '?redirectTo=' + redirectTo : ''),
@ -81,6 +83,13 @@ export class LoginController {
return; 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; req.session.user = user.uuid;
res.redirect(redirectTo ? decodeURIComponent(redirectTo) : '/'); res.redirect(redirectTo ? decodeURIComponent(redirectTo) : '/');
} }
@ -114,6 +123,7 @@ export class LoginController {
@Query('redirectTo') redirectTo?: string, @Query('redirectTo') redirectTo?: string,
) { ) {
let user: User; let user: User;
let remember = false;
try { try {
if (!session.challenge) { if (!session.challenge) {
@ -129,6 +139,8 @@ export class LoginController {
if (!user) { if (!user) {
throw new Error('Bad challenge'); throw new Error('Bad challenge');
} }
remember = challenge.remember;
} catch (e: any) { } catch (e: any) {
req.flash('message', { req.flash('message', {
error: true, error: true,
@ -154,6 +166,12 @@ export class LoginController {
return; 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.challenge = null;
session.user = user.uuid; session.user = user.uuid;
res.redirect(redirectTo ? decodeURIComponent(redirectTo) : '/'); res.redirect(redirectTo ? decodeURIComponent(redirectTo) : '/');

View File

@ -172,7 +172,10 @@ export class SettingsController {
@Render('settings/security') @Render('settings/security')
public async security(@Req() req: Request) { public async security(@Req() req: Request) {
const mailSplit = req.user.email.split('@'); 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); const twofactor = await this._totp.userHasTOTP(req.user);
return this._form.populateTemplate(req, { return this._form.populateTemplate(req, {
user: req.user, user: req.user,

View File

@ -23,11 +23,18 @@ block body
input.form-control#username(type="text", name="username", placeholder="Username", autofocus, value=form.username) input.form-control#username(type="text", name="username", placeholder="Username", autofocus, value=form.username)
label.form-label(for="password") Password label.form-label(for="password") Password
input.form-control#password(type="password", name="password", placeholder="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 button.btn.btn-primary(type="submit") Log in
div.btn-group.align-self-end 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="/register" + (query ? ('?' + query) : '')) Create a new account
|• |•
a.btn.btn-link(type="button" href="/login/password") Forgot password? a.btn.btn-link(type="button" href="/login/password") Forgot password?
div.center-box-addon div.center-box-addon
p Icy Network is a Single-Sign-On service used by other applications. p
p The website may use temporary cookies for storing your login session. 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.

View File

@ -42,7 +42,7 @@ block settings
input(type="hidden", name="_csrf", value=csrf) input(type="hidden", name="_csrf", value=csrf)
label.form-label(for="image") Image label.form-label(for="image") Image
input.form-control#image(type="file", name="file") 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 button.btn.btn-primary(type="submit") Change
.modal#avatar-modal(data-modal="avatar", style="display: none") .modal#avatar-modal(data-modal="avatar", style="display: none")
.modal__content .modal__content

View File

@ -17,7 +17,7 @@ block settings
h2 Change Password h2 Change Password
form(method="post", action="/account/security/password", autocomplete="off") form(method="post", action="/account/security/password", autocomplete="off")
div.form-container 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 label.form-label(for="password") Current Password
input.form-control#password(type="password", name="password") input.form-control#password(type="password", name="password")
label.form-label(for="new_password") New Password label.form-label(for="new_password") New Password
@ -29,7 +29,7 @@ block settings
h2 Change Email Address h2 Change Email Address
form(method="post", action="/account/security/email", autocomplete="off") form(method="post", action="/account/security/email", autocomplete="off")
div.form-container 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 label.form-label(for="current_email") Current Email Address
input.form-control#current_email(type="email", name="current_email") input.form-control#current_email(type="email", name="current_email")
small.form-hint Hint: #{emailHint} small.form-hint Hint: #{emailHint}