Improve authentication recovery and mobile gameplay
This commit is contained in:
@@ -8,10 +8,12 @@ import {
|
||||
authDestination,
|
||||
passwordUpdateSchema,
|
||||
profileUpdateSchema,
|
||||
initialPasswordSchema,
|
||||
} from "@/shared/account";
|
||||
import { createHash } from "node:crypto";
|
||||
import { recoveryCookie, verifyRecoveryGrant } from "./password-recovery";
|
||||
import { requestOrigin } from "./request-origin";
|
||||
import { issueInitialPassword } from "@/shared/initial-password";
|
||||
|
||||
type Context = {
|
||||
jsonBody: (r: NextRequest) => Promise<unknown>;
|
||||
@@ -24,6 +26,40 @@ export async function accountApi(
|
||||
path: string[],
|
||||
context: Context,
|
||||
) {
|
||||
if (
|
||||
path[0] === "auth" &&
|
||||
path[1] === "initial-password" &&
|
||||
path.length === 2
|
||||
) {
|
||||
const account = await user();
|
||||
if (!account?.email || !account.email_confirmed_at)
|
||||
return json({ error: "Log in to manage your account" }, 401);
|
||||
if (request.method === "GET") {
|
||||
return json({
|
||||
needed: required(
|
||||
await admin().rpc("legacy_password_needed", { p_user: account.id }),
|
||||
),
|
||||
});
|
||||
}
|
||||
if (request.method !== "POST")
|
||||
return json({ error: "Method not allowed" }, 405);
|
||||
const body = initialPasswordSchema.parse(await context.jsonBody(request));
|
||||
await context.limit(`initial-password:${account.id}`, 3, 600);
|
||||
await issueInitialPassword(account.id, body.password, {
|
||||
claim: async (id) =>
|
||||
required(await admin().rpc("claim_legacy_password", { p_user: id })),
|
||||
update: async (id, password) => {
|
||||
const { error } = await admin().auth.admin.updateUserById(id, {
|
||||
password,
|
||||
});
|
||||
if (error)
|
||||
throw new Error(
|
||||
"Password setup could not be confirmed. Use password recovery before signing out.",
|
||||
);
|
||||
},
|
||||
});
|
||||
return json({ ok: true });
|
||||
}
|
||||
if (path[0] === "profile" && path.length === 1) {
|
||||
const account = await user();
|
||||
if (!account) return json({ error: "Log in to manage your account" }, 401);
|
||||
@@ -63,6 +99,10 @@ export async function accountApi(
|
||||
const origin = requestOrigin(request, process.env.MINEGEO_URL);
|
||||
const next = authDestination("next" in body ? body.next : null);
|
||||
const callback = new URL("/auth/callback", origin);
|
||||
callback.searchParams.set(
|
||||
"type",
|
||||
body.action === "recover" ? "recovery" : "signup",
|
||||
);
|
||||
callback.searchParams.set(
|
||||
"next",
|
||||
body.action === "recover"
|
||||
@@ -107,18 +147,9 @@ export async function accountApi(
|
||||
"Check your email to confirm your account. If you already have an account, log in or reset your password.",
|
||||
});
|
||||
}
|
||||
const { error } =
|
||||
body.action === "recover"
|
||||
? await client.auth.resetPasswordForEmail(body.email, {
|
||||
redirectTo: callback.toString(),
|
||||
})
|
||||
: await client.auth.signInWithOtp({
|
||||
email: body.email,
|
||||
options: {
|
||||
shouldCreateUser: false,
|
||||
emailRedirectTo: callback.toString(),
|
||||
},
|
||||
});
|
||||
const { error } = await client.auth.resetPasswordForEmail(body.email, {
|
||||
redirectTo: callback.toString(),
|
||||
});
|
||||
if (
|
||||
error &&
|
||||
!["user_not_found", "signup_disabled"].includes(error.code ?? "")
|
||||
@@ -126,9 +157,7 @@ export async function accountApi(
|
||||
throw new Error(error.message);
|
||||
return json({
|
||||
message:
|
||||
body.action === "recover"
|
||||
? "If an account exists for this email, a password reset link is on its way. Check your inbox."
|
||||
: "If an account exists for this email, a sign-in link is on its way. Check your inbox.",
|
||||
"If an account exists for this email, a password reset link is on its way. Check your inbox.",
|
||||
});
|
||||
}
|
||||
if (path[1] !== "password") return json({ error: "Not found" }, 404);
|
||||
|
||||
Reference in New Issue
Block a user