better-auth
Version:
The most comprehensive authentication framework for TypeScript.
111 lines (109 loc) • 3.86 kB
JavaScript
import { setSessionCookie } from "../../cookies/index.mjs";
import { APIError } from "../../api/index.mjs";
import { toBoolean } from "../../utils/boolean.mjs";
import * as z from "zod";
import { createAuthEndpoint } from "@better-auth/core/api";
import { createRemoteJWKSet, jwtVerify } from "jose";
//#region src/plugins/one-tap/index.ts
const oneTapCallbackBodySchema = z.object({ idToken: z.string().meta({ description: "Google ID token, which the client obtains from the One Tap API" }) });
const oneTap = (options) => ({
id: "one-tap",
endpoints: { oneTapCallback: createAuthEndpoint("/one-tap/callback", {
method: "POST",
body: oneTapCallbackBodySchema,
metadata: { openapi: {
summary: "One tap callback",
description: "Use this endpoint to authenticate with Google One Tap",
responses: {
200: {
description: "Successful response",
content: { "application/json": { schema: {
type: "object",
properties: {
session: { $ref: "#/components/schemas/Session" },
user: { $ref: "#/components/schemas/User" }
}
} } }
},
400: { description: "Invalid token" }
}
} }
}, async (ctx) => {
const { idToken } = ctx.body;
let payload;
try {
const { payload: verifiedPayload } = await jwtVerify(idToken, createRemoteJWKSet(new URL("https://www.googleapis.com/oauth2/v3/certs")), {
issuer: ["https://accounts.google.com", "accounts.google.com"],
audience: options?.clientId || ctx.context.options.socialProviders?.google?.clientId
});
payload = verifiedPayload;
} catch {
throw new APIError("BAD_REQUEST", { message: "invalid id token" });
}
const { email, email_verified, name, picture, sub } = payload;
if (!email) return ctx.json({ error: "Email not available in token" });
const user = await ctx.context.internalAdapter.findUserByEmail(email);
if (!user) {
if (options?.disableSignup) throw new APIError("BAD_GATEWAY", { message: "User not found" });
const newUser = await ctx.context.internalAdapter.createOAuthUser({
email,
emailVerified: typeof email_verified === "boolean" ? email_verified : toBoolean(email_verified),
name,
image: picture
}, {
providerId: "google",
accountId: sub
});
if (!newUser) throw new APIError("INTERNAL_SERVER_ERROR", { message: "Could not create user" });
const session$1 = await ctx.context.internalAdapter.createSession(newUser.user.id);
await setSessionCookie(ctx, {
user: newUser.user,
session: session$1
});
return ctx.json({
token: session$1.token,
user: {
id: newUser.user.id,
email: newUser.user.email,
emailVerified: newUser.user.emailVerified,
name: newUser.user.name,
image: newUser.user.image,
createdAt: newUser.user.createdAt,
updatedAt: newUser.user.updatedAt
}
});
}
if (!await ctx.context.internalAdapter.findAccount(sub)) {
const accountLinking = ctx.context.options.account?.accountLinking;
if (accountLinking?.enabled && (accountLinking.trustedProviders?.includes("google") || email_verified)) await ctx.context.internalAdapter.linkAccount({
userId: user.user.id,
providerId: "google",
accountId: sub,
scope: "openid,profile,email",
idToken
});
else throw new APIError("UNAUTHORIZED", { message: "Google sub doesn't match" });
}
const session = await ctx.context.internalAdapter.createSession(user.user.id);
await setSessionCookie(ctx, {
user: user.user,
session
});
return ctx.json({
token: session.token,
user: {
id: user.user.id,
email: user.user.email,
emailVerified: user.user.emailVerified,
name: user.user.name,
image: user.user.image,
createdAt: user.user.createdAt,
updatedAt: user.user.updatedAt
}
});
}) },
options
});
//#endregion
export { oneTap };
//# sourceMappingURL=index.mjs.map