mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 21:05:19 +00:00
feat(core): support one time password (#9798)
This commit is contained in:
@@ -14,7 +14,9 @@ import {
|
||||
import type { Request, Response } from 'express';
|
||||
|
||||
import {
|
||||
Cache,
|
||||
Config,
|
||||
CryptoHelper,
|
||||
EarlyAccessRequired,
|
||||
EmailTokenNotFound,
|
||||
InternalServerError,
|
||||
@@ -49,6 +51,8 @@ interface MagicLinkCredential {
|
||||
token: string;
|
||||
}
|
||||
|
||||
const OTP_CACHE_KEY = (otp: string) => `magic-link-otp:${otp}`;
|
||||
|
||||
@Throttle('strict')
|
||||
@Controller('/api/auth')
|
||||
export class AuthController {
|
||||
@@ -57,7 +61,9 @@ export class AuthController {
|
||||
private readonly auth: AuthService,
|
||||
private readonly models: Models,
|
||||
private readonly config: Config,
|
||||
private readonly runtime: Runtime
|
||||
private readonly runtime: Runtime,
|
||||
private readonly cache: Cache,
|
||||
private readonly crypto: CryptoHelper
|
||||
) {
|
||||
if (config.node.dev) {
|
||||
// set DNS servers in dev mode
|
||||
@@ -190,13 +196,20 @@ export class AuthController {
|
||||
}
|
||||
}
|
||||
|
||||
const ttlInSec = 30 * 60;
|
||||
const token = await this.models.verificationToken.create(
|
||||
TokenType.SignIn,
|
||||
email
|
||||
email,
|
||||
ttlInSec
|
||||
);
|
||||
|
||||
const otp = this.crypto.otp();
|
||||
// TODO(@forehalo): this is a temporary solution, we should not rely on cache to store the otp
|
||||
const cacheKey = OTP_CACHE_KEY(otp);
|
||||
await this.cache.set(cacheKey, token, { ttl: ttlInSec * 1000 });
|
||||
|
||||
const magicLink = this.url.link(callbackUrl, {
|
||||
token,
|
||||
token: otp,
|
||||
email,
|
||||
...(redirectUrl
|
||||
? {
|
||||
@@ -205,7 +218,12 @@ export class AuthController {
|
||||
: {}),
|
||||
});
|
||||
|
||||
const result = await this.auth.sendSignInEmail(email, magicLink, !user);
|
||||
const result = await this.auth.sendSignInEmail(
|
||||
email,
|
||||
magicLink,
|
||||
otp,
|
||||
!user
|
||||
);
|
||||
|
||||
if (result.rejected.length) {
|
||||
throw new InternalServerError('Failed to send sign-in email.');
|
||||
@@ -247,9 +265,16 @@ export class AuthController {
|
||||
|
||||
validators.assertValidEmail(email);
|
||||
|
||||
const cacheKey = OTP_CACHE_KEY(token);
|
||||
const cachedToken = await this.cache.get<string>(cacheKey);
|
||||
|
||||
if (!cachedToken) {
|
||||
throw new InvalidEmailToken();
|
||||
}
|
||||
|
||||
const tokenRecord = await this.models.verificationToken.verify(
|
||||
TokenType.SignIn,
|
||||
token,
|
||||
cachedToken,
|
||||
{
|
||||
credential: email,
|
||||
}
|
||||
|
||||
@@ -322,9 +322,14 @@ export class AuthService implements OnApplicationBootstrap {
|
||||
});
|
||||
}
|
||||
|
||||
async sendSignInEmail(email: string, link: string, signUp: boolean) {
|
||||
async sendSignInEmail(
|
||||
email: string,
|
||||
link: string,
|
||||
otp: string,
|
||||
signUp: boolean
|
||||
) {
|
||||
return signUp
|
||||
? await this.mailer.sendSignUpMail(email, { url: link })
|
||||
: await this.mailer.sendSignInMail(email, { url: link });
|
||||
? await this.mailer.sendSignUpMail(email, { url: link, otp })
|
||||
: await this.mailer.sendSignInMail(email, { url: link, otp });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user