better-auth
Version:
The most comprehensive authentication library for TypeScript.
208 lines (201 loc) • 6.69 kB
JavaScript
'use strict';
const z = require('zod/v4');
const betterCall = require('better-call');
const socialProviders_index = require('../../shared/better-auth.Bafolo-S.cjs');
const cookies_index = require('../../cookies/index.cjs');
require('../../shared/better-auth.BIMq4RPW.cjs');
require('../../shared/better-auth.DiSjtgs9.cjs');
require('../../shared/better-auth.CXhVNgXP.cjs');
require('defu');
const jose = require('jose');
require('../../shared/better-auth.C1hdVENX.cjs');
require('@better-auth/utils/hash');
require('@better-auth/utils/base64');
require('../../crypto/index.cjs');
require('@noble/ciphers/chacha');
require('@noble/ciphers/utils');
require('@noble/ciphers/webcrypto');
require('@noble/hashes/scrypt');
require('@better-auth/utils');
require('@better-auth/utils/hex');
require('@noble/hashes/utils');
require('../../shared/better-auth.CYeOI8C-.cjs');
require('@better-auth/utils/random');
require('@better-fetch/fetch');
require('../../shared/better-auth.C-R0J0n1.cjs');
require('../../shared/better-auth.ANpbi45u.cjs');
require('../../shared/better-auth.D3mtHEZg.cjs');
require('@better-auth/utils/hmac');
require('@better-auth/utils/binary');
require('jose/errors');
function _interopNamespaceCompat(e) {
if (e && typeof e === 'object' && 'default' in e) return e;
const n = Object.create(null);
if (e) {
for (const k in e) {
n[k] = e[k];
}
}
n.default = e;
return n;
}
const z__namespace = /*#__PURE__*/_interopNamespaceCompat(z);
function toBoolean(value) {
return value === "true" || value === true;
}
const oneTap = (options) => ({
id: "one-tap",
endpoints: {
oneTapCallback: socialProviders_index.createAuthEndpoint(
"/one-tap/callback",
{
method: "POST",
body: z__namespace.object({
idToken: z__namespace.string().meta({
description: "Google ID token, which the client obtains from the One Tap API"
})
}),
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 JWKS = jose.createRemoteJWKSet(
new URL("https://www.googleapis.com/oauth2/v3/certs")
);
const { payload: verifiedPayload } = await jose.jwtVerify(
idToken,
JWKS,
{
issuer: ["https://accounts.google.com", "accounts.google.com"],
audience: options?.clientId || ctx.context.options.socialProviders?.google?.clientId
}
);
payload = verifiedPayload;
} catch (error) {
throw new betterCall.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 betterCall.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
},
ctx
);
if (!newUser) {
throw new betterCall.APIError("INTERNAL_SERVER_ERROR", {
message: "Could not create user"
});
}
const session2 = await ctx.context.internalAdapter.createSession(
newUser.user.id,
ctx
);
await cookies_index.setSessionCookie(ctx, {
user: newUser.user,
session: session2
});
return ctx.json({
token: session2.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
}
});
}
const account = await ctx.context.internalAdapter.findAccount(sub);
if (!account) {
const accountLinking = ctx.context.options.account?.accountLinking;
const shouldLinkAccount = accountLinking?.enabled && (accountLinking.trustedProviders?.includes("google") || email_verified);
if (shouldLinkAccount) {
await ctx.context.internalAdapter.linkAccount({
userId: user.user.id,
providerId: "google",
accountId: sub,
scope: "openid,profile,email",
idToken
});
} else {
throw new betterCall.APIError("UNAUTHORIZED", {
message: "Google sub doesn't match"
});
}
}
const session = await ctx.context.internalAdapter.createSession(
user.user.id,
ctx
);
await cookies_index.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
}
});
}
)
}
});
exports.oneTap = oneTap;