Skip to content

Instantly share code, notes, and snippets.

@HarleySalas
Created July 24, 2024 14:06
Show Gist options
  • Save HarleySalas/144ae22a13c8d630fec2cc0fdc8570ed to your computer and use it in GitHub Desktop.
Save HarleySalas/144ae22a13c8d630fec2cc0fdc8570ed to your computer and use it in GitHub Desktop.
import 'server-only'
import { fetchJson } from '@/lib/utils/fetchJson'
import { Google, Yandex, generateState, generateCodeVerifier } from 'arctic'
import { cookies } from 'next/headers'
import type { Customer } from '@/payload-types'
import type { NextRequest } from 'next/server'
type ExchangeCodeResponse = {
data: Pick<Customer, 'provider' | 'providerAccountId' | 'metadata'>
email: string
}
const callbackUrl = (provider: keyof typeof authProviders): string => {
return process.env.NODE_ENV === 'production'
? `${process.env.NEXT_PUBLIC_APP_URL}/api/v1/auth/callback/${provider}`
: `http://redirectmeto.com/${process.env.NEXT_PUBLIC_APP_URL}/api/v1/auth/callback/${provider}`
}
const google = new Google(
process.env.GOOGLE_CLIENT_ID!,
process.env.GOOGLE_CLIENT_SECRET!,
callbackUrl('google'),
)
const yandex = new Yandex(process.env.YANDEX_CLIENT_ID!, process.env.YANDEX_CLIENT_SECRET!, {
redirectURI: `${process.env.NEXT_PUBLIC_APP_URL}/api/v1/auth/callback/yandex`,
})
const mailruRedirectURI = `${process.env.NEXT_PUBLIC_APP_URL}/api/v1/auth/callback/mailru`
export const authProviders = {
google: {
cookieName: 'google_oauth_state',
createAuthorizationURL: async (options?: any) => {
const state = generateState()
const codeVerifier = generateCodeVerifier()
const url = await google.createAuthorizationURL(state, codeVerifier, options)
cookies().set(authProviders.google.cookieName, state, {
path: '/',
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
maxAge: 60 * 10,
sameSite: 'lax',
})
cookies().set('codeVerifier', codeVerifier, {
path: '/',
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
maxAge: 60 * 10,
sameSite: 'lax',
})
return url.toString()
},
exchangeCodeForUser: async (request: NextRequest): Promise<ExchangeCodeResponse> => {
const code = request.nextUrl.searchParams.get('code')
if (!code) throw new Error('Authorization code not found.')
const codeVerifier = cookies().get('codeVerifier')?.value ?? ''
try {
const tokens = await google.validateAuthorizationCode(code, codeVerifier)
const googleUser = await fetchJson('https://openidconnect.googleapis.com/v1/userinfo', {
headers: {
Authorization: `Bearer ${tokens.accessToken}`,
},
})
return {
data: {
provider: 'google',
providerAccountId: String(googleUser.sub),
metadata: googleUser,
},
email: googleUser.email,
}
} catch (error) {
console.error(error)
throw new Error('Failed to validate authorization code.')
}
},
},
yandex: {
cookieName: 'yandex_oauth_state',
createAuthorizationURL: async (options?: any) => {
const state = generateState()
const url = await yandex.createAuthorizationURL(state, options)
cookies().set(authProviders.yandex.cookieName, state, {
path: '/',
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
maxAge: 60 * 10,
sameSite: 'lax',
})
return `${url.toString()}&force_confirm=true`
},
exchangeCodeForUser: async (request: NextRequest): Promise<ExchangeCodeResponse> => {
const code = request.nextUrl.searchParams.get('code')
if (!code) throw new Error('Authorization code not found.')
try {
const tokens = await yandex.validateAuthorizationCode(code)
const yandexUser = await fetchJson('https://login.yandex.ru/info', {
headers: {
Authorization: `OAuth ${tokens.accessToken}`,
},
})
return {
data: {
provider: 'yandex',
providerAccountId: String(yandexUser.id),
metadata: yandexUser,
},
email: yandexUser.default_email,
}
} catch (error) {
console.error(error)
throw new Error('Failed to validate authorization code.')
}
},
},
mailru: {
cookieName: 'mailru_oauth_state',
createAuthorizationURL: async (options?: any) => {
const state = generateState()
const url = new URL(
`https://oauth.mail.ru/login?client_id=${process.env
.MAILRU_CLIENT_ID!}&response_type=code&scope=userinfo&redirect_uri=${mailruRedirectURI}&state=${state}`,
)
cookies().set(authProviders.mailru.cookieName, state, {
path: '/',
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
maxAge: 60 * 10,
sameSite: 'lax',
})
return url.toString()
},
exchangeCodeForUser: async (request: NextRequest): Promise<ExchangeCodeResponse> => {
const code = request.nextUrl.searchParams.get('code')
if (!code) throw new Error('Authorization code not found.')
const url = `https://oauth.mail.ru/token?grant_type=authorization_code&code=${code}&redirect_uri=${mailruRedirectURI}`
const basicAuth = Buffer.from(
`${process.env.MAILRU_CLIENT_ID}:${process.env.MAILRU_CLIENT_SECRET}`,
).toString('base64')
const tokens = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Basic ${basicAuth}`,
},
})
.then((res) => res.json())
.catch((err) => console.error(err))
console.log(`TOKENS: ${JSON.stringify(tokens)}`)
const mailruUser = await fetchJson(
`https://oauth.mail.ru/userinfo?access_token=${tokens.access_token}`,
)
console.log(mailruUser)
return {
data: {
provider: 'mailru',
providerAccountId: String(mailruUser.id),
metadata: mailruUser,
},
email: String(mailruUser.email),
}
},
},
} as const
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment