ai-wpa/lib/google-oauth.ts

83 lines
2.0 KiB
TypeScript

import { OAuth2Client } from 'google-auth-library'
const client = new OAuth2Client(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
process.env.GOOGLE_REDIRECT_URI ||
`${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:4023'}/api/auth/google/callback`
)
export interface GoogleUserInfo {
id: string
email: string
name: string
picture?: string
given_name?: string
family_name?: string
verified_email: boolean
}
export const getGoogleAuthURL = () => {
const scopes = ['openid', 'profile', 'email']
return client.generateAuthUrl({
access_type: 'offline',
scope: scopes,
prompt: 'consent',
state: 'google_oauth',
})
}
export const getGoogleUser = async (code: string): Promise<GoogleUserInfo> => {
const { tokens } = await client.getToken(code)
if (!tokens.access_token) {
throw new Error('No access token received from Google')
}
// Get user info from Google
const response = await fetch(
`https://www.googleapis.com/oauth2/v2/userinfo?access_token=${tokens.access_token}`
)
if (!response.ok) {
throw new Error('Failed to fetch user info from Google')
}
const userInfo = (await response.json()) as GoogleUserInfo
if (!userInfo.verified_email) {
throw new Error('Google email not verified')
}
return userInfo
}
export const verifyGoogleToken = async (token: string): Promise<GoogleUserInfo> => {
try {
const ticket = await client.verifyIdToken({
idToken: token,
audience: process.env.GOOGLE_CLIENT_ID,
})
const payload = ticket.getPayload()
if (!payload) {
throw new Error('Invalid Google token payload')
}
return {
id: payload.sub,
email: payload.email!,
name: payload.name!,
picture: payload.picture,
given_name: payload.given_name,
family_name: payload.family_name,
verified_email: payload.email_verified || false,
}
} catch (error) {
throw new Error('Failed to verify Google token')
}
}
export { client as googleOAuthClient }