feat: release design system engine, studio and component contracts
This commit is contained in:
1 parent
ff1065fb27
commit
3943b946d6
693 files changed
+175546
-3894
No files matched your search
@@ -132,6 +132,7 @@ export interface AuthEngine {
|
||||
beginMfaOtp(
|
||||
mfaToken: string,
|
||||
method: "email-otp" | "sms-otp",
|
||||
options?: { resend?: boolean },
|
||||
): Promise<{ id: string; expiresAt: number } | undefined>;
|
||||
completeMfa(input: {
|
||||
rememberDevice?: boolean;
|
||||
@@ -1711,7 +1712,7 @@ export function createAuthEngine(options: AuthEngineOptions): AuthEngine {
|
||||
return { ok: true, user: publicUser(user), session };
|
||||
},
|
||||
|
||||
async beginMfaOtp(mfaToken, method) {
|
||||
async beginMfaOtp(mfaToken, method, request = {}) {
|
||||
const token = await inspectToken(mfaToken, "login-mfa");
|
||||
if (!token) return undefined;
|
||||
const user = await store.findUserById(token.userId);
|
||||
@@ -1724,12 +1725,18 @@ export function createAuthEngine(options: AuthEngineOptions): AuthEngine {
|
||||
const previousId = token.metadata?.[key];
|
||||
if (typeof previousId === "string") {
|
||||
const previous = await store.findOtp(previousId);
|
||||
if (previous && !previous.usedAt && previous.expiresAt > now())
|
||||
return { id: previous.id, expiresAt: previous.expiresAt };
|
||||
if (request.resend && previous && now() - previous.createdAt < 60_000) return undefined;
|
||||
if (previous && !previous.usedAt && previous.expiresAt > now()) {
|
||||
if (!request.resend) return { id: previous.id, expiresAt: previous.expiresAt };
|
||||
}
|
||||
}
|
||||
const challenge = await issueOtp(token.userId, method, undefined, "mfa");
|
||||
token.metadata = { ...token.metadata, [key]: challenge.id };
|
||||
await store.updateToken(token);
|
||||
if (request.resend && typeof previousId === "string") {
|
||||
const previous = await store.findOtp(previousId);
|
||||
if (previous && !previous.usedAt) await store.updateOtp({ ...previous, usedAt: now() });
|
||||
}
|
||||
return challenge;
|
||||
} catch {
|
||||
return undefined;
|
||||
@@ -2081,12 +2088,17 @@ export function createAuthEngine(options: AuthEngineOptions): AuthEngine {
|
||||
const verification = verificationUnavailable(user);
|
||||
if (verification) return verification;
|
||||
account = await engine.linkOAuth(user.id, providerName, normalizedProfile, tokens);
|
||||
const providerVerifiesDevice = verifiedEmail &&
|
||||
options.verifiedOAuthDeviceProviders?.some((name) => normalizedOAuthProvider(name) === providerName);
|
||||
const deviceCheck = providerVerifiesDevice ? undefined : await newDeviceRequirement(user, {
|
||||
...sessionInput,
|
||||
oauthProvider: providerName,
|
||||
});
|
||||
const providerVerifiesDevice =
|
||||
verifiedEmail &&
|
||||
options.verifiedOAuthDeviceProviders?.some(
|
||||
(name) => normalizedOAuthProvider(name) === providerName,
|
||||
);
|
||||
const deviceCheck = providerVerifiesDevice
|
||||
? undefined
|
||||
: await newDeviceRequirement(user, {
|
||||
...sessionInput,
|
||||
oauthProvider: providerName,
|
||||
});
|
||||
if (deviceCheck) return deviceCheck;
|
||||
if (await hasMfa(user.id)) {
|
||||
const methods = await availableMfaMethods(user.id);
|
||||
|
||||
@@ -505,10 +505,19 @@ export function createAuthHttpHandlers(options: AuthHttpOptions) {
|
||||
if (!validation.ok) return validation.response;
|
||||
const input = validation.value;
|
||||
const method = text(input.method) === "sms-otp" ? "sms-otp" : "email-otp";
|
||||
const challenge = await engine.beginMfaOtp(text(input.mfaToken), method);
|
||||
const challenge = await engine.beginMfaOtp(text(input.mfaToken), method, {
|
||||
resend: input.resend === true,
|
||||
});
|
||||
return challenge
|
||||
? json({ ok: true, ...challenge })
|
||||
: json({ ok: false, error: "MFA transaction is invalid or expired" }, 400);
|
||||
? json({ ok: true, ...challenge, retryAfter: 60 })
|
||||
: json(
|
||||
{
|
||||
ok: false,
|
||||
error:
|
||||
"Unable to request a code. Wait 60 seconds before retrying, or sign in again if this request has expired.",
|
||||
},
|
||||
400,
|
||||
);
|
||||
},
|
||||
|
||||
async completeMfa(ctx: Context): Promise<Response> {
|
||||
|
||||
@@ -148,6 +148,7 @@ export const otpSchema = v.object({
|
||||
});
|
||||
|
||||
export const mfaOtpRequestSchema = v.object({
|
||||
resend: v.boolean().optional(),
|
||||
mfaToken: v
|
||||
.string()
|
||||
.required("MFA transaction is missing")
|
||||
|
||||
Reference in new issue
Block a user