/**
 * Google OAuth Provider
 */
import { createAuthorizationURL, validateAuthorizationCode, refreshAccessToken, parseOAuth2Tokens, } from "../oauth2/utils.js";
import { createRemoteJWKSet, jwtVerify } from "jose";
// Google's JWKS endpoint — cached/rotated automatically by createRemoteJWKSet.
const GOOGLE_JWKS = createRemoteJWKSet(new URL("https://www.googleapis.com/oauth2/v3/certs"));
const GOOGLE_ISSUERS = ["https://accounts.google.com", "accounts.google.com"];
const GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
const GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
const GOOGLE_USERINFO_URL = "https://www.googleapis.com/oauth2/v3/userinfo";
export function google(options) {
    const defaultScopes = ["openid", "email", "profile"];
    return {
        id: "google",
        name: "Google",
        options,
        async createAuthorizationURL({ state, codeVerifier, scopes, redirectURI, loginHint }) {
            const allScopes = options.disableDefaultScope
                ? [...(options.scope || []), ...(scopes || [])]
                : [...defaultScopes, ...(options.scope || []), ...(scopes || [])];
            return createAuthorizationURL({
                authorizationEndpoint: GOOGLE_AUTH_URL,
                clientId: options.clientId,
                redirectURI: options.redirectURI || redirectURI,
                state,
                scopes: [...new Set(allScopes)],
                codeVerifier,
                prompt: options.prompt,
                accessType: options.accessType,
                loginHint,
                additionalParams: {
                    include_granted_scopes: "true",
                    ...(options.hd && { hd: options.hd }),
                },
            });
        },
        async validateAuthorizationCode({ code, redirectURI, codeVerifier }) {
            const result = await validateAuthorizationCode({
                tokenEndpoint: GOOGLE_TOKEN_URL,
                code,
                clientId: options.clientId,
                clientSecret: options.clientSecret,
                redirectURI: options.redirectURI || redirectURI,
                codeVerifier,
            });
            const tokens = parseOAuth2Tokens(result.raw);
            return {
                ...tokens,
                accessToken: result.accessToken,
                refreshToken: result.refreshToken,
                idToken: result.idToken,
                tokenType: result.tokenType,
                raw: result.raw,
            };
        },
        async getUserInfo(tokens) {
            if (!tokens.accessToken) {
                return null;
            }
            const response = await fetch(GOOGLE_USERINFO_URL, {
                headers: {
                    Authorization: `Bearer ${tokens.accessToken}`,
                },
            });
            if (!response.ok) {
                console.error("Failed to fetch Google user info:", await response.text());
                return null;
            }
            const profile = await response.json();
            // Apply custom mapping if provided
            if (options.mapProfileToUser) {
                const mapped = await options.mapProfileToUser(profile);
                return {
                    user: {
                        id: profile.sub,
                        email: mapped.email || profile.email,
                        emailVerified: profile.email_verified,
                        name: mapped.firstName
                            ? `${mapped.firstName}${mapped.lastName ? " " + mapped.lastName : ""}`
                            : profile.name,
                        image: profile.picture,
                        firstName: mapped.firstName || profile.given_name,
                        lastName: mapped.lastName || profile.family_name,
                    },
                    data: profile,
                };
            }
            return {
                user: {
                    id: profile.sub,
                    email: profile.email,
                    emailVerified: profile.email_verified,
                    name: profile.name,
                    image: profile.picture,
                    firstName: profile.given_name,
                    lastName: profile.family_name,
                },
                data: profile,
            };
        },
        async refreshAccessToken(refreshToken) {
            const result = await refreshAccessToken({
                tokenEndpoint: GOOGLE_TOKEN_URL,
                clientId: options.clientId,
                clientSecret: options.clientSecret,
                refreshToken,
            });
            return {
                accessToken: result.accessToken,
                refreshToken: result.refreshToken,
                tokenType: result.tokenType,
                accessTokenExpiresAt: result.expiresIn
                    ? new Date(Date.now() + result.expiresIn * 1000)
                    : undefined,
                raw: result.raw,
            };
        },
        async verifyIdToken(token, nonce) {
            // Verify the JWT SIGNATURE against Google's published keys (JWKS), plus
            // issuer/audience/expiry. A decode-only check (the previous behavior) would
            // accept a forged token with the right claims, so we now cryptographically
            // verify it. `jwtVerify` enforces signature + exp; we check iss/aud/nonce.
            try {
                const { payload } = await jwtVerify(token, GOOGLE_JWKS, {
                    issuer: GOOGLE_ISSUERS,
                    audience: options.clientId,
                });
                // Optional replay protection: if a nonce was supplied, it must match.
                if (nonce && payload.nonce !== nonce) {
                    return false;
                }
                return true;
            }
            catch {
                return false;
            }
        },
    };
}
export default google;
//# sourceMappingURL=google.js.map