Files
pulse/lib/api/oauth.ts

66 lines
2.2 KiB
TypeScript

/**
* OAuth 2.0 PKCE utilities
*/
function generateRandomString(length: number): string {
const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~'
const values = new Uint8Array(length)
crypto.getRandomValues(values)
return Array.from(values, (x) => charset[x % charset.length]).join('')
}
async function generateCodeChallenge(codeVerifier: string): Promise<string> {
const encoder = new TextEncoder()
const data = encoder.encode(codeVerifier)
const digest = await crypto.subtle.digest('SHA-256', data)
// Convert ArrayBuffer to Base64URL string
return btoa(String.fromCharCode(...new Uint8Array(digest)))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '')
}
export interface OAuthParams {
state: string
codeVerifier: string
codeChallenge: string
}
export async function generateOAuthParams(): Promise<OAuthParams> {
const state = generateRandomString(32)
const codeVerifier = generateRandomString(64)
const codeChallenge = await generateCodeChallenge(codeVerifier)
return {
state,
codeVerifier,
codeChallenge
}
}
export async function initiateOAuthFlow(redirectPath = '/auth/callback') {
if (typeof window === 'undefined') return
const params = await generateOAuthParams()
const redirectUri = `${window.location.origin}${redirectPath}`
// Store PKCE params in sessionStorage for later use
sessionStorage.setItem('oauth_state', params.state)
sessionStorage.setItem('oauth_code_verifier', params.codeVerifier)
const authUrl = process.env.NEXT_PUBLIC_AUTH_URL || 'https://auth.ciphera.net'
const authApiUrl = process.env.NEXT_PUBLIC_AUTH_API_URL || 'https://auth-api.ciphera.net'
// Redirect to OAuth authorize endpoint
const authorizeUrl = new URL(`${authApiUrl}/oauth/authorize`)
authorizeUrl.searchParams.set('client_id', 'analytics-app')
authorizeUrl.searchParams.set('redirect_uri', redirectUri)
authorizeUrl.searchParams.set('response_type', 'code')
authorizeUrl.searchParams.set('state', params.state)
authorizeUrl.searchParams.set('code_challenge', params.codeChallenge)
authorizeUrl.searchParams.set('code_challenge_method', 'S256')
window.location.href = authorizeUrl.toString()
}