@@ -129,7 +129,7 @@ export const getProviders = () => {
129129 } else {
130130 if ( ! user . hashedPassword ) {
131131 return null ;
132- }
132+ }
133133
134134 if ( ! bcrypt . compareSync ( password , user . hashedPassword ) ) {
135135 return null ;
@@ -241,24 +241,7 @@ const nextAuthResult = NextAuth({
241241 callbacks : {
242242 async signIn ( { account } ) {
243243 const matchingProvider = account
244- ? getProviders ( ) . find ( ( p ) => {
245- // NextAuth/Auth.js provider factories (e.g. Bitbucket,
246- // GitHub, GitLab) hardcode a default `id` at the top of
247- // the returned object and nest the caller's options
248- // (including any `id` override) under `.options`. At
249- // runtime the framework merges options over the
250- // top-level defaults, so the effective provider id can
251- // live under either field depending on whether the
252- // caller passed an override. Read `.options.id` first
253- // and fall back to the top-level `id`.
254- const config = (
255- typeof p . provider === 'function'
256- ? ( p . provider as unknown as ( ) => unknown ) ( )
257- : p . provider
258- ) as { id ?: string ; options ?: { id ?: string } } ;
259- const providerId = config . options ?. id ?? config . id ;
260- return providerId === account . provider ;
261- } )
244+ ? getProviders ( ) . find ( ( p ) => getEffectiveProviderId ( p . provider ) === account . provider )
262245 : undefined ;
263246
264247 // Refuse OAuth signin for providers configured purely for account
@@ -282,7 +265,6 @@ const nextAuthResult = NextAuth({
282265 // new orphan identity with no UserToOrg row.
283266 const isAccountLinkingAttempt = matchingProvider ?. purpose === 'account_linking' ;
284267 const session = await auth ( ) ;
285-
286268 if ( isAccountLinkingAttempt && session === null ) {
287269 return false ;
288270 }
@@ -417,18 +399,29 @@ export const auth = cache(async (): Promise<Session | null> => {
417399 return nextAuthResult . auth ( ) ;
418400} ) ;
419401
402+ // NextAuth/Auth.js provider factories (e.g. Bitbucket, GitHub, GitLab) hardcode
403+ // a default `id` at the top of the returned object and nest the caller's
404+ // options (including any `id` override) under `.options`. At runtime the
405+ // framework merges options over the top-level defaults, so the effective
406+ // provider id can live under either field depending on whether the caller
407+ // passed an override. Read `.options.id` first and fall back to the top-level
408+ // `id`. The function form of `Provider` is part of the NextAuth type union but
409+ // unused in this codebase; we handle it for type completeness.
410+ const getEffectiveProviderId = ( provider : Provider ) : string | undefined => {
411+ const config = (
412+ typeof provider === 'function'
413+ ? ( provider as unknown as ( ) => unknown ) ( )
414+ : provider
415+ ) as { id ?: string ; options ?: { id ?: string } } ;
416+ return config . options ?. id ?? config . id ;
417+ }
418+
420419/**
421420 * Returns the issuer URL for a given auth.js account
422421 */
423422const getIssuerUrlForAccount = async ( account : { provider : string ; } ) => {
424- const providers = getProviders ( ) ;
425- const matchingProvider = providers . find ( ( provider ) => {
426- if ( typeof provider . provider === "function" ) {
427- const providerInfo = provider . provider ( ) ;
428- return providerInfo . id === account . provider ;
429- } else {
430- return provider . provider . id === account . provider ;
431- }
432- } ) ;
423+ const matchingProvider = getProviders ( ) . find (
424+ ( p ) => getEffectiveProviderId ( p . provider ) === account . provider
425+ ) ;
433426 return matchingProvider ?. issuerUrl ;
434427}
0 commit comments