Skip to content

Conversation

@BenjaminOddou
Copy link
Contributor

This PR is the first step to address issue #80 by providing a non-interactive PAT login flow, which is required to integrate external authentication systems like Supabase.

What this PR does

This PR updates the authentication logic to support two distinct modes:

Standard Github OAuth (standard) and Github PAT (Personal Access Token) for non-interactive logins, which can be triggered programmatically.

This allows a host application (e.g., a site using Supabase) to bypass the GitHub login screen and authenticate a user automatically if they are already logged into the main app.

@vercel
Copy link

vercel bot commented Nov 16, 2025

@BenjaminOddou is attempting to deploy a commit to the Nuxt Team on Vercel.

A member of the Team first needs to authorize it.

@BenjaminOddou BenjaminOddou changed the title feat: Add GitHub PAT authentication feat(auth): Add GitHub PAT authentication Nov 16, 2025
@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 16, 2025

npm i https://pkg.pr.new/nuxt-content/studio/nuxt-studio@103

commit: 8d9efc6

@BenjaminOddou BenjaminOddou marked this pull request as ready for review November 16, 2025 21:22
Copy link

@vercel vercel bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional Suggestion:

When using PAT authentication without OAuth credentials, the session secret will be generated from undefined values, resulting in a predictable, identical secret for all PAT-only deployments. This is a critical security vulnerability.

View Details
📝 Patch Details
diff --git a/src/module/src/module.ts b/src/module/src/module.ts
index 9eabe27..6895d00 100644
--- a/src/module/src/module.ts
+++ b/src/module/src/module.ts
@@ -166,6 +166,7 @@ export default defineNuxtModule<ModuleOptions>({
         sessionSecret: createHash('md5').update([
           options.auth?.github?.clientId,
           options.auth?.github?.clientSecret,
+          options.auth?.github?.pat,
         ].join('')).digest('hex'),
         // @ts-expect-error todo fix github type issue
         github: options.auth?.github,

Analysis

Predictable session secret when using PAT-only authentication

What fails: Session secret generation in createNuxtModule() uses only OAuth credentials (clientId/clientSecret) for session encryption. When PAT-only authentication is configured (no OAuth), both values are undefined. In JavaScript, [undefined, undefined].join('') produces an empty string, resulting in all PAT-only deployments generating the identical MD5 hash: d41d8cd98f00b204e9800998ecf8427e.

How to reproduce:

# Deployment 1: PAT-only mode
STUDIO_GITHUB_CLIENT_ID="" \
STUDIO_GITHUB_CLIENT_SECRET="" \
STUDIO_GITHUB_PAT="token1" \
node -e "
  const crypto = require('crypto')
  const secret1 = crypto.createHash('md5').update([undefined, undefined].join('')).digest('hex')
  console.log('Secret1:', secret1)
"

# Deployment 2: Different PAT, same OAuth config
STUDIO_GITHUB_CLIENT_ID="" \
STUDIO_GITHUB_CLIENT_SECRET="" \
STUDIO_GITHUB_PAT="token2" \
node -e "
  const crypto = require('crypto')
  const secret2 = crypto.createHash('md5').update([undefined, undefined].join('')).digest('hex')
  console.log('Secret2:', secret2)
"

Both deployments output the same secret: d41d8cd98f00b204e9800998ecf8427e

Result: Session cookies become interchangeable across different PAT-only deployments. An attacker with access to one deployment can forge session cookies valid for any other PAT-only deployment.

Expected: Each deployment should generate a unique session secret based on its configuration. PAT-only deployments should include the PAT value in the secret generation to ensure uniqueness.

Fix applied: Include options.auth?.github?.pat in the sessionSecret hash generation at line 166-169 in src/module/src/module.ts. This ensures PAT-only deployments with different PAT values generate different session secrets, while maintaining backward compatibility with OAuth-only and mixed configurations.

Comment on lines +150 to +154
console.warn('Nuxt Studio: Could not find primary email for PAT user.')
}
}
catch (e) {
console.error('Nuxt Studio: Failed to fetch emails for PAT user.', e)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
console.warn('Nuxt Studio: Could not find primary email for PAT user.')
}
}
catch (e) {
console.error('Nuxt Studio: Failed to fetch emails for PAT user.', e)
throw createError({
statusCode: 500,
message: 'Could not get GitHub user email',
data: { accessToken: '***' },
})
}
}
catch (e) {
throw createError({ statusCode: 500, message: 'Failed to fetch emails for PAT user.', data: e })

The PAT authentication flow doesn't enforce the emailRequired configuration, allowing users to be created with empty emails while the OAuth flow correctly rejects such logins.

View Details

Analysis

Inconsistent emailRequired enforcement in GitHub PAT authentication flow

What fails: PAT authentication flow allows session creation with empty email when emailRequired=true, while OAuth flow correctly rejects such logins.

How to reproduce:

  1. Configure GitHub authentication with PAT (Personal Access Token)
  2. Set emailRequired=true in the configuration
  3. Authenticate with a GitHub account that has no primary email set
  4. With PAT flow: User logs in successfully with empty email string
  5. With OAuth flow: User receives 500 error "Could not get GitHub user email"

Result:

  • PAT flow: Creates session with email: '' (line 171 before fix: email: user.email ?? '')
  • OAuth flow: Throws error with status 500 and message "Could not get GitHub user email" (lines 273-279)

Expected: Both flows should enforce the same emailRequired contract - rejecting authentication when no email can be found.

Root cause: In the PAT flow (lines 137-159 before fix), when emailRequired=true and no primary email is found:

  • Line 153: Only logs a warning instead of throwing an error
  • Line 154: catch block only logs an error instead of throwing
  • This allows the session to be created with an empty email

The OAuth flow (lines 269-279) correctly throws an error with createError() when the same condition occurs.

Fix applied: Modified PAT flow to match OAuth flow behavior by throwing errors instead of logging warnings when no primary email is found and emailRequired=true.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant