-
Notifications
You must be signed in to change notification settings - Fork 15
feat(auth): Add GitHub PAT authentication #103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@BenjaminOddou is attempting to deploy a commit to the Nuxt Team on Vercel. A member of the Team first needs to authorize it. |
commit: |
There was a problem hiding this 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.
| 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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| 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:
- Configure GitHub authentication with PAT (Personal Access Token)
- Set
emailRequired=truein the configuration - Authenticate with a GitHub account that has no primary email set
- With PAT flow: User logs in successfully with empty email string
- 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.
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.