mirror of
https://github.com/codeflash-ai/codeflash-internal.git
synced 2026-05-04 18:25:18 +00:00
fix: lazily instantiate Auth0Client to fix CI build failure (#2600)
## Summary - Auth0Client was constructed at module import time, crashing during `next build` static analysis of `/_not-found` when `AUTH0_DOMAIN` isn't set in CI - Wraps the client in a lazy Proxy that defers construction to first method call - Zero API change — all callers still do `auth0.getSession()`, `auth0.handleAuth()`, etc. ## Context This broke in #2598 when the layout restructure caused `/_not-found` to evaluate the root layout's auth0 import during build. The `build` CI check has been failing on all PRs since. ## Test plan - [ ] `build` CI check passes (was failing on #2598, #2593, #2599) - [ ] Auth flows still work at runtime (login, logout, callback)
This commit is contained in:
parent
3b1398973e
commit
f9d78e5cf2
1 changed files with 101 additions and 84 deletions
|
|
@ -16,104 +16,121 @@ function redirectTo(path: string): NextResponse {
|
|||
|
||||
// Auth0 v4 expects AUTH0_DOMAIN; derive from v3's AUTH0_ISSUER_BASE_URL if needed
|
||||
const auth0Domain =
|
||||
process.env.AUTH0_DOMAIN ??
|
||||
process.env.AUTH0_ISSUER_BASE_URL?.replace(/^https?:\/\//, "")
|
||||
process.env.AUTH0_DOMAIN ?? process.env.AUTH0_ISSUER_BASE_URL?.replace(/^https?:\/\//, "")
|
||||
|
||||
export const auth0 = new Auth0Client({
|
||||
domain: auth0Domain,
|
||||
authorizationParameters: {
|
||||
scope: "openid profile email offline_access",
|
||||
},
|
||||
signInReturnToPath: APP_ROUTES.BASE,
|
||||
async beforeSessionSaved(session, idToken) {
|
||||
// Decode the ID token to get claims like nickname, name, picture
|
||||
if (idToken) {
|
||||
try {
|
||||
const payload = JSON.parse(Buffer.from(idToken.split(".")[1], "base64url").toString())
|
||||
return {
|
||||
...session,
|
||||
user: {
|
||||
...session.user,
|
||||
nickname: payload.nickname ?? session.user.nickname,
|
||||
name: payload.name ?? session.user.name,
|
||||
picture: payload.picture ?? session.user.picture,
|
||||
},
|
||||
// Lazily instantiate Auth0Client so the module can be imported during
|
||||
// `next build` static analysis (e.g. /_not-found) where env vars aren't set.
|
||||
let _auth0: Auth0Client | undefined
|
||||
|
||||
function getAuth0Client(): Auth0Client {
|
||||
if (!_auth0) {
|
||||
_auth0 = new Auth0Client({
|
||||
domain: auth0Domain,
|
||||
authorizationParameters: {
|
||||
scope: "openid profile email offline_access",
|
||||
},
|
||||
signInReturnToPath: APP_ROUTES.BASE,
|
||||
async beforeSessionSaved(session, idToken) {
|
||||
// Decode the ID token to get claims like nickname, name, picture
|
||||
if (idToken) {
|
||||
try {
|
||||
const payload = JSON.parse(Buffer.from(idToken.split(".")[1], "base64url").toString())
|
||||
return {
|
||||
...session,
|
||||
user: {
|
||||
...session.user,
|
||||
nickname: payload.nickname ?? session.user.nickname,
|
||||
name: payload.name ?? session.user.name,
|
||||
picture: payload.picture ?? session.user.picture,
|
||||
},
|
||||
}
|
||||
} catch {
|
||||
// If decoding fails, return session as-is
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// If decoding fails, return session as-is
|
||||
}
|
||||
}
|
||||
return session
|
||||
},
|
||||
async onCallback(error, context, session) {
|
||||
if (error) {
|
||||
console.error("[Auth] Error in callback:", error)
|
||||
const errorMessage = error.message || ""
|
||||
return session
|
||||
},
|
||||
async onCallback(error, context, session) {
|
||||
if (error) {
|
||||
console.error("[Auth] Error in callback:", error)
|
||||
const errorMessage = error.message || ""
|
||||
|
||||
if (errorMessage.includes("allowlist-fail")) {
|
||||
const re = /allowlist-fail\s(.*)\s(.*)\)/
|
||||
const match = errorMessage.match(re)
|
||||
if (match != null) {
|
||||
const userId = match[1]
|
||||
const userNickname = match[2]
|
||||
return redirectTo(`/waitlist?username=${userNickname}&userid=${userId}`)
|
||||
if (errorMessage.includes("allowlist-fail")) {
|
||||
const re = /allowlist-fail\s(.*)\s(.*)\)/
|
||||
const match = errorMessage.match(re)
|
||||
if (match != null) {
|
||||
const userId = match[1]
|
||||
const userNickname = match[2]
|
||||
return redirectTo(`/waitlist?username=${userNickname}&userid=${userId}`)
|
||||
}
|
||||
}
|
||||
|
||||
return redirectTo("/login?error=callback_failed")
|
||||
}
|
||||
}
|
||||
|
||||
return redirectTo("/login?error=callback_failed")
|
||||
}
|
||||
if (!session) {
|
||||
return redirectTo("/waitlist")
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
return redirectTo("/waitlist")
|
||||
}
|
||||
const user = session.user
|
||||
console.log(`[Auth] Processing login for user: ${user.sub}`)
|
||||
|
||||
const user = session.user
|
||||
console.log(`[Auth] Processing login for user: ${user.sub}`)
|
||||
if (!user.sub || !user.nickname) {
|
||||
console.error("[Auth] Missing required user fields")
|
||||
return redirectTo(context.returnTo || APP_ROUTES.BASE)
|
||||
}
|
||||
|
||||
if (!user.sub || !user.nickname) {
|
||||
console.error("[Auth] Missing required user fields")
|
||||
return redirectTo(context.returnTo || APP_ROUTES.BASE)
|
||||
}
|
||||
try {
|
||||
// Save user to database
|
||||
console.log("[Auth] Saving user to database...")
|
||||
await createOrUpdateUser(user.sub, user.nickname, user.email ?? null, user.name ?? null)
|
||||
console.log("[Auth] User saved successfully")
|
||||
|
||||
try {
|
||||
// Save user to database
|
||||
console.log("[Auth] Saving user to database...")
|
||||
await createOrUpdateUser(user.sub, user.nickname, user.email ?? null, user.name ?? null)
|
||||
console.log("[Auth] User saved successfully")
|
||||
// Track login
|
||||
await trackUserLogin({
|
||||
userId: user.sub,
|
||||
username: user.nickname,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
})
|
||||
|
||||
// Track login
|
||||
await trackUserLogin({
|
||||
userId: user.sub,
|
||||
username: user.nickname,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
})
|
||||
// Check onboarding
|
||||
const completedOnboarding = await hasCompletedOnboarding(user.sub)
|
||||
console.log(`[Auth] Onboarding completed: ${completedOnboarding}`)
|
||||
|
||||
// Check onboarding
|
||||
const completedOnboarding = await hasCompletedOnboarding(user.sub)
|
||||
console.log(`[Auth] Onboarding completed: ${completedOnboarding}`)
|
||||
const intendedDestination = context.returnTo || APP_ROUTES.BASE
|
||||
|
||||
const intendedDestination = context.returnTo || APP_ROUTES.BASE
|
||||
// Check if the path is codeflash/auth/[token]
|
||||
const isAuthPath =
|
||||
intendedDestination.startsWith("/codeflash/auth") ||
|
||||
intendedDestination.includes("/codeflash/auth")
|
||||
|
||||
// Check if the path is codeflash/auth/[token]
|
||||
const isAuthPath =
|
||||
intendedDestination.startsWith("/codeflash/auth") ||
|
||||
intendedDestination.includes("/codeflash/auth")
|
||||
if (!completedOnboarding && !isAuthPath) {
|
||||
return redirectTo("/onboarding")
|
||||
}
|
||||
|
||||
if (!completedOnboarding && !isAuthPath) {
|
||||
return redirectTo("/onboarding")
|
||||
}
|
||||
return redirectTo(intendedDestination)
|
||||
} catch (err) {
|
||||
console.error("[Auth] Error in onCallback:", err)
|
||||
return redirectTo(context.returnTo || APP_ROUTES.BASE)
|
||||
}
|
||||
},
|
||||
routes: {
|
||||
login: "/auth/login",
|
||||
logout: "/auth/logout",
|
||||
callback: "/auth/callback",
|
||||
},
|
||||
appBaseUrl: process.env.APP_BASE_URL || process.env.AUTH0_BASE_URL,
|
||||
})
|
||||
}
|
||||
return _auth0
|
||||
}
|
||||
|
||||
return redirectTo(intendedDestination)
|
||||
} catch (err) {
|
||||
console.error("[Auth] Error in onCallback:", err)
|
||||
return redirectTo(context.returnTo || APP_ROUTES.BASE)
|
||||
}
|
||||
// Proxy preserves the `auth0.getSession()` API while deferring construction
|
||||
export const auth0: Auth0Client = new Proxy({} as Auth0Client, {
|
||||
get(_, prop) {
|
||||
const client = getAuth0Client()
|
||||
const value = (client as any)[prop]
|
||||
return typeof value === "function" ? value.bind(client) : value
|
||||
},
|
||||
routes: {
|
||||
login: "/auth/login",
|
||||
logout: "/auth/logout",
|
||||
callback: "/auth/callback",
|
||||
},
|
||||
appBaseUrl: process.env.APP_BASE_URL || process.env.AUTH0_BASE_URL,
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue