Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Account creation #48

Draft
wants to merge 19 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions emailTemplates/verification.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<html>
<head> </head>
<body>
<p>Hi there!</p>
<p>You are receiving this message to verify the email associated with your new SCCS account! Your username is: <strong>${username}</strong>.</p>
<p>
To access SCCS services, confirm your email by clicking:
<!-- insert link with proper id and key -->
<a href="${domain}/account/reset?id=${resetId}&key=${resetKey}">this link</a>. Or, copy and
paste this link into your browser:
</p>
<p>${domain}/account/reset?id=${resetId}&key=${resetKey}</p>
<p>
(This link will stay active for seven days; after that, you'll have to
adic2023 marked this conversation as resolved.
Show resolved Hide resolved
<a href="${domain}/account/forgot">request a new link</a>.)
</p>
<p>
Enjoy your new SCCS account! We hope you'll find our services useful. If you have any
questions, comments, or suggestionsx, feel free to contact us at
<a href="mailto:[email protected]">[email protected]</a>
</p>
</body>
</html>
1 change: 1 addition & 0 deletions src/functions/createAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { generateEmail } from '../util/emailTemplates';
import { addLdap, searchAsyncMultiple } from '../util/ldapUtils';
import { logger } from '../util/logging';
import { createPasswordResetRequest } from '../util/passwordReset';
import { verifyAccountRequest } from '../util/accountVerify'

export interface CreateAccountData {
username: string;
Expand Down
44 changes: 44 additions & 0 deletions src/integration/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,50 @@ export const PasswordResetRequestModel = mongoose.model<PasswordResetRequest>(
passwordResetRequestSchema,
);

// new interface for email verification
export interface VerifyEmailRequest {
// verification email links generate two keys: the ID, which is stored plain, and the key, which is
// hashed with argon2 before being stored.
// good chance the rest of this shit is wrong in this implementation:
// Both are provided to the user and they make a request;
// then we look up the request for the ID and compare hashed keys, basically exactly like a normal
// username/password login flow.
_id: string;
key: string;
user: string;
timestamp: Date;
suppressEmail?: boolean;
}

const verifyEmailRequestSchema = new mongoose.Schema<VerifyEmailRequest>({
_id: {
type: String,
required: true,
},
key: {
type: String,
required: true,
},
user: {
type: String,
required: true,
index: true, // we'll search by user to invalidate previous reset requests
},
adic2023 marked this conversation as resolved.
Show resolved Hide resolved
timestamp: {
type: Date,
expires: 0,
},
suppressEmail: {
type: Boolean,
required: false,
},
Comment on lines +119 to +122
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't necessary for email verification requests, since there isn't really a "confirmation" of the email being verified like there is for password resets. We have this option in the password reset model because there's two scenarios where we do a password reset: 1) explicitly creating a new account and 2) a user-requested password reset. In scenario 2 we send a confirmation email after the change happens (see here), but not in scenario 1.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, why was this marked as resolved? This hasn't been fixed.

});

export const VerifyEmailRequestModel = mongoose.model<VerifyEmailRequest>(
'VerifyEmail',
verifyEmailRequestSchema,
);

export interface MinecraftWhitelist {
_id: string;
mcUuid: string;
Expand Down
30 changes: 30 additions & 0 deletions src/util/accountVerify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import argon2 from 'argon2';
import { nanoid } from 'nanoid';

import { VerifyEmailRequestModel } from '../integration/models';
import { logger } from './logging';

const USERNAME_REGEX = /^[a-z][-a-z0-9]*$/;

export const verifyAccountRequest = async (
uid: string,
expireHours = 1,
suppressEmail = false,
): Promise<[string, string]> => {
logger.debug(`Creating account verification ID/key pair for ${uid}`);
const verifyId = nanoid();
const verifyKey = nanoid();

const expireDate = new Date();
expireDate.setHours(expireDate.getHours() + expireHours);

await new VerifyEmailRequestModel({
_id: verifyId,
key: await argon2.hash(verifyKey, { raw: false }),
user: uid,
timestamp: expireDate,
suppressEmail: suppressEmail,
}).save();

return [verifyId, verifyKey];
};
Loading