Skip to content

Instantly share code, notes, and snippets.

@mobyjames
Created May 7, 2024 17:04
Show Gist options
  • Save mobyjames/556552f039b176bb930a380f7f2092ae to your computer and use it in GitHub Desktop.
Save mobyjames/556552f039b176bb930a380f7f2092ae to your computer and use it in GitHub Desktop.
passwordless auth sample
export async function beginAuthentication(email: string | null): Promise<boolean> {
if (!email || email.length === 0 || !email.includes('@')) {
throw ApiError.required('email');
}
email = email.trim();
const existingUser = await User.findOne({ email });
let authKey = getRandomDigitString(4);
const authKeyExpiration = dateWithMinutesFromNow(10);
if (existingUser) {
await User.updateOne(
{ _id: existingUser._id },
{
authKey,
authKeyExpiration,
}
);
} else {
await createUser({ email, authKey, authKeyExpiration });
}
if (DEV_MODE) {
console.log(`Auth Key: ${authKey}`);
} else {
try {
await sendEmail(
email,
`Your [GAME NAME] sign-in code: ${authKey}`,
`Enter this code in the game to finish signing in:\n\n${authKey}`,
'magic-login',
{ loginCode: authKey }
);
} catch (error) {
console.error(error);
throw ApiError.single({ field: 'email', code: ErrorCode.AuthEmailSendFailed, message: 'could not be sent' });
}
}
return true;
}
export async function authenticate(email: string | null, authKey: string | null): Promise<TokenData> {
if (!email || email.length === 0) {
throw ApiError.required('email');
}
if (!authKey || authKey.length === 0) {
throw ApiError.required('authKey');
}
authKey = authKey.trim();
const existingUser = await User.findOne({ email, authKey });
if (!existingUser) {
throw ApiError.invalidValue('authKey');
}
const authKeyExpiration = existingUser.authKeyExpiration ?? MIN_DATE;
if (minutesSince(authKeyExpiration) > 0) {
throw ApiError.single({ field: 'authKey', code: ErrorCode.ExpiredAuthKey, message: 'has expired' });
}
// clear authentication key
const $set: any = { authKey: null, authKeyExpiration: null };
await User.updateOne({ _id: existingUser._id }, { $set });
return createToken(existingUser);
}
@mobyjames
Copy link
Author

Workflow:

  1. User enters email into UI and client calls an API that invokes the first function beginAuthentication.
  2. User gets email
  3. User enters code into UI and submits
  4. Client calls another API that invokes the second function authenticate
  5. Client gets back a token to use for further API calls

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment