Skip to content

Add NextAuth Google OAuth auto-setup tooling#151

Open
pappensex wants to merge 1 commit intomainfrom
codex/automate-google-oauth-setup-for-next.js
Open

Add NextAuth Google OAuth auto-setup tooling#151
pappensex wants to merge 1 commit intomainfrom
codex/automate-google-oauth-setup-for-next.js

Conversation

@pappensex
Copy link
Owner

Summary

  • add a NextAuth Google OAuth handler with JWT session access token propagation
  • introduce a setup script that seeds .env.local defaults and writes the auth route, exposed via npm script
  • document the automated Google sign-in setup flow and extend NextAuth types for access tokens

Testing

  • node scripts/setup-google-auth.js
  • npm install next-auth (fails in this environment with registry 403)

Codex Task

Copilot AI review requested due to automatic review settings November 27, 2025 22:45
@vercel
Copy link

vercel bot commented Nov 27, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
transzendenz Error Error Nov 27, 2025 10:46pm
yoni-app Error Error Nov 27, 2025 10:46pm
yoni-app-next Error Error Nov 27, 2025 10:46pm
yoni-app-oznp Error Error Nov 27, 2025 10:46pm
yoni-core Error Error Nov 27, 2025 10:46pm

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 18 to 20
"dependencies": {
"next-auth": "^4.24.8",
"next": "14.2.4",

Choose a reason for hiding this comment

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

P1 Badge Keep package-lock in sync after adding next-auth

The new next-auth dependency was added to package.json without regenerating package-lock.json; the lockfile still has no entry for this package (rg "next-auth" package-lock.json returns nothing), so npm ci or any install with a frozen lock will error out due to the mismatch between package.json and the lockfile, blocking installs until the lock is updated.

Useful? React with 👍 / 👎.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds automated tooling for setting up NextAuth with Google OAuth authentication in the YONI app. It introduces a setup script that generates the necessary NextAuth configuration, environment variables, and route handler, streamlining the Google sign-in integration process.

Key Changes

  • Setup automation script that generates .env.local with OAuth placeholders and a secure NEXTAUTH_SECRET, and creates the NextAuth API route handler
  • NextAuth dependency added to package.json (v4.24.8) with TypeScript type augmentations for JWT access token propagation
  • German documentation in README.md with step-by-step Google Cloud Console setup and troubleshooting guidance

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
scripts/setup-google-auth.js New setup script that initializes .env.local and generates NextAuth route handler with Google provider configuration
package.json Adds next-auth@^4.24.8 dependency and setup:google-auth npm script
next-auth.d.ts TypeScript type augmentation to add accessToken field to Session and JWT interfaces
app/api/auth/[...nextauth]/route.ts NextAuth route handler with Google OAuth provider and JWT-based session with access token propagation
README.md German-language documentation for Google OAuth setup process with troubleshooting section

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +65 to +68
merged.GOOGLE_CLIENT_ID = env.GOOGLE_CLIENT_ID || process.env.GOOGLE_CLIENT_ID || defaultEnv.GOOGLE_CLIENT_ID;
merged.GOOGLE_CLIENT_SECRET = env.GOOGLE_CLIENT_SECRET || process.env.GOOGLE_CLIENT_SECRET || defaultEnv.GOOGLE_CLIENT_SECRET;
merged.NEXTAUTH_URL = env.NEXTAUTH_URL || process.env.NEXTAUTH_URL || defaultEnv.NEXTAUTH_URL;
merged.NEXTAUTH_SECRET = env.NEXTAUTH_SECRET || process.env.NEXTAUTH_SECRET || crypto.randomBytes(32).toString("base64");
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

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

The merging logic here is redundant and potentially confusing. Lines 65-67 overwrite the values that were already set in line 64 from { ...defaultEnv, ...env }. The spread operator already handles the merging correctly by prioritizing env over defaultEnv.

Consider simplifying to:

const merged = { ...defaultEnv, ...env };
if (!merged.NEXTAUTH_SECRET) {
  merged.NEXTAUTH_SECRET = process.env.NEXTAUTH_SECRET || crypto.randomBytes(32).toString("base64");
}

This way, existing values in .env.local take precedence, and process.env values are checked only if needed, while NEXTAUTH_SECRET is generated only if it doesn't exist anywhere.

Suggested change
merged.GOOGLE_CLIENT_ID = env.GOOGLE_CLIENT_ID || process.env.GOOGLE_CLIENT_ID || defaultEnv.GOOGLE_CLIENT_ID;
merged.GOOGLE_CLIENT_SECRET = env.GOOGLE_CLIENT_SECRET || process.env.GOOGLE_CLIENT_SECRET || defaultEnv.GOOGLE_CLIENT_SECRET;
merged.NEXTAUTH_URL = env.NEXTAUTH_URL || process.env.NEXTAUTH_URL || defaultEnv.NEXTAUTH_URL;
merged.NEXTAUTH_SECRET = env.NEXTAUTH_SECRET || process.env.NEXTAUTH_SECRET || crypto.randomBytes(32).toString("base64");
if (!merged.NEXTAUTH_SECRET) {
merged.NEXTAUTH_SECRET = process.env.NEXTAUTH_SECRET || crypto.randomBytes(32).toString("base64");
}

Copilot uses AI. Check for mistakes.
}

function ensureRoute() {
fs.mkdirSync(path.dirname(routePath), { recursive: true });
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

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

The script always overwrites the route file without checking if it already exists or has been customized. This could unexpectedly remove custom NextAuth configurations (like additional providers, custom callbacks, or events).

Consider either:

  1. Checking if the file exists and prompting the user before overwriting
  2. Only creating the file if it doesn't exist
  3. Adding a comment in the output warning users that the file was overwritten

Example:

if (fs.existsSync(routePath)) {
  console.warn(`Warning: Overwriting existing NextAuth route at ${routePath}`);
}
Suggested change
fs.mkdirSync(path.dirname(routePath), { recursive: true });
fs.mkdirSync(path.dirname(routePath), { recursive: true });
if (fs.existsSync(routePath)) {
console.warn(`Warning: Overwriting existing NextAuth route at ${routePath}`);
}

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +24
});

export { handler as GET, handler as POST };
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

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

The access token from the Google provider should be typed as string | undefined since not all OAuth accounts guarantee an access token. Consider adding a type guard or null check before assignment to ensure type safety:

async jwt({ token, account }) {
  if (account?.access_token) {
    token.accessToken = account.access_token;
  }
  return token;
}

This also prevents setting accessToken to undefined explicitly when account.access_token is not present.

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +20
callbacks: {
async jwt({ token, account }) {
if (account) token.accessToken = account.access_token;
return token;
},
async session({ session, token }) {
session.accessToken = token.accessToken;
return session;
},
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

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

Storing the OAuth access token in the JWT and exposing it in the session could be a security risk depending on how the session is used client-side. The Google access token grants access to the user's Google resources.

Consider:

  1. Only include the access token if it's needed for specific API calls
  2. Store it server-side (e.g., in a database) rather than in the JWT if possible
  3. Add a comment warning developers about the security implications
  4. Consider adding token expiration handling since Google access tokens expire

If the access token must be in the session, ensure it's only used server-side and never exposed to client-side JavaScript.

Copilot uses AI. Check for mistakes.
5. **Login testen**
- `npm run dev` starten und `http://localhost:3000/api/auth/signin` öffnen → Google auswählen → zurück zur App.

Fehlerhilfe: 400 = Redirect-URI-Mismatch, 403 = App im Testing Mode (E-Mail whitelisten), "Missing NEXTAUTH_URL/Secret" = `.env.local` prüfen.
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

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

[nitpick] The error help message mentions "Missing NEXTAUTH_URL/Secret" but doesn't specify which file to check or what the actual error message would look like.

Consider being more specific:

Fehlerhilfe: 
- 400 = Redirect-URI-Mismatch in Google Cloud Console
- 403 = App im Testing Mode (E-Mail whitelisten)
- "NEXTAUTH_SECRET is not set" = prüfe `.env.local`
- "NEXTAUTH_URL is not set" = setze `NEXTAUTH_URL` in `.env.local`

This helps users match the actual error messages they see with the correct solution.

Suggested change
Fehlerhilfe: 400 = Redirect-URI-Mismatch, 403 = App im Testing Mode (E-Mail whitelisten), "Missing NEXTAUTH_URL/Secret" = `.env.local` prüfen.
Fehlerhilfe:
  • 400 = Redirect-URI-Mismatch in Google Cloud Console
  • 403 = App im Testing Mode (E-Mail whitelisten)
  • "NEXTAUTH_SECRET is not set" = prüfe .env.local
  • "NEXTAUTH_URL is not set" = setze NEXTAUTH_URL in .env.local

Copilot uses AI. Check for mistakes.
Comment on lines +82 to +88
ensureEnv();
ensureRoute();

console.log("\nNEXT STEPS:");
console.log("1) Add your Google OAuth client values to .env.local (if placeholders remain).");
console.log("2) Ensure NEXTAUTH_URL matches your deployment domain.");
console.log("3) Run 'npm install' to install the next-auth dependency if it is not already present.");
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

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

The script doesn't include any error handling for file system operations. If there are permission issues, disk space problems, or other I/O errors, the script will crash with an unhelpful stack trace.

Consider wrapping the main operations in try-catch blocks:

function main() {
  try {
    ensureEnv();
    ensureRoute();
    
    console.log("\nNEXT STEPS:");
    // ... rest of instructions
  } catch (error) {
    console.error("BLOCKER: Setup failed:", error.message);
    process.exit(1);
  }
}

This provides clearer feedback when things go wrong and follows the BLOCKER/STABIL pattern used in other scripts.

Suggested change
ensureEnv();
ensureRoute();
console.log("\nNEXT STEPS:");
console.log("1) Add your Google OAuth client values to .env.local (if placeholders remain).");
console.log("2) Ensure NEXTAUTH_URL matches your deployment domain.");
console.log("3) Run 'npm install' to install the next-auth dependency if it is not already present.");
try {
ensureEnv();
ensureRoute();
console.log("\nNEXT STEPS:");
console.log("1) Add your Google OAuth client values to .env.local (if placeholders remain).");
console.log("2) Ensure NEXTAUTH_URL matches your deployment domain.");
console.log("3) Run 'npm install' to install the next-auth dependency if it is not already present.");
} catch (error) {
console.error("BLOCKER: Setup failed:", error.message);
process.exit(1);
}

Copilot uses AI. Check for mistakes.
3. **.env.local vervollständigen**
- Ersetze die Platzhalter für `GOOGLE_CLIENT_ID`/`GOOGLE_CLIENT_SECRET`.
- Setze `NEXTAUTH_URL` bei Deployments (z. B. `https://DEINE-DOMAIN.com`).
4. **Dependency sicherstellen**
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

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

[nitpick] The word "sicherstellen" should be "Stelle sicher" for consistency with the imperative form used in other steps. Step 4 currently reads "Dependency sicherstellen" which mixes the infinitive form with the step number format.

For consistency with steps 1-3 which use imperative verbs (Gehe, Kopiere, Ersetze, Setze), consider:

4. **Dependency installieren**
   - Stelle sicher, dass `next-auth` installiert ist (`npm install`), falls dein lokales Setup keinen Zugriff auf das Paket hatte.

Or keep the current heading but it should match the pattern of the other steps.

Suggested change
4. **Dependency sicherstellen**
4. **Dependency installieren**

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +48
return content
.split(/\r?\n/)
.filter((line) => line.trim() && !line.trim().startsWith("#"))
.reduce((acc, line) => {
const [key, ...rest] = line.split("=");
acc[key] = rest.join("=");
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

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

The parseEnv function doesn't handle environment variable values that contain = signs correctly when the value is quoted. For example, a line like KEY="value=with=equals" would only capture "value as the value.

While the current implementation using rest.join("=") does handle unquoted values with equals signs, it's fragile for edge cases. Consider documenting this limitation or adding support for quoted values if needed.

Suggested change
return content
.split(/\r?\n/)
.filter((line) => line.trim() && !line.trim().startsWith("#"))
.reduce((acc, line) => {
const [key, ...rest] = line.split("=");
acc[key] = rest.join("=");
// Regex: key = value, value may be quoted (single or double), may contain = inside quotes
const lineRegex = /^\s*([\w.-]+)\s*=\s*(?:(['"])([\s\S]*?)\2|([^\n#]*))\s*$/;
return content
.split(/\r?\n/)
.filter((line) => line.trim() && !line.trim().startsWith("#"))
.reduce((acc, line) => {
const match = line.match(lineRegex);
if (match) {
const key = match[1];
let value = match[3] !== undefined ? match[3] : match[4] || "";
// Unescape quotes if value was quoted
if (match[2] === '"') {
value = value.replace(/\\"/g, '"');
} else if (match[2] === "'") {
value = value.replace(/\\'/g, "'");
}
acc[key] = value;
}

Copilot uses AI. Check for mistakes.
Comment on lines +64 to +70
const merged = { ...defaultEnv, ...env };
merged.GOOGLE_CLIENT_ID = env.GOOGLE_CLIENT_ID || process.env.GOOGLE_CLIENT_ID || defaultEnv.GOOGLE_CLIENT_ID;
merged.GOOGLE_CLIENT_SECRET = env.GOOGLE_CLIENT_SECRET || process.env.GOOGLE_CLIENT_SECRET || defaultEnv.GOOGLE_CLIENT_SECRET;
merged.NEXTAUTH_URL = env.NEXTAUTH_URL || process.env.NEXTAUTH_URL || defaultEnv.NEXTAUTH_URL;
merged.NEXTAUTH_SECRET = env.NEXTAUTH_SECRET || process.env.NEXTAUTH_SECRET || crypto.randomBytes(32).toString("base64");

fs.writeFileSync(envPath, formatEnv(merged));
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

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

The script unconditionally overwrites .env.local with only the NextAuth-related variables, which will delete any other environment variables that were previously in the file (like OPENAI_API_KEY, REVALIDATE_SECRET, or DATABASE_URL mentioned in other scripts).

Consider preserving all existing environment variables and only adding/updating the NextAuth-specific ones:

const merged = { ...env, ...defaultEnv };
// Only set defaults if not already present
if (!merged.GOOGLE_CLIENT_ID) merged.GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID || defaultEnv.GOOGLE_CLIENT_ID;
// ... etc

This way, running the setup script won't accidentally delete other configuration.

Suggested change
const merged = { ...defaultEnv, ...env };
merged.GOOGLE_CLIENT_ID = env.GOOGLE_CLIENT_ID || process.env.GOOGLE_CLIENT_ID || defaultEnv.GOOGLE_CLIENT_ID;
merged.GOOGLE_CLIENT_SECRET = env.GOOGLE_CLIENT_SECRET || process.env.GOOGLE_CLIENT_SECRET || defaultEnv.GOOGLE_CLIENT_SECRET;
merged.NEXTAUTH_URL = env.NEXTAUTH_URL || process.env.NEXTAUTH_URL || defaultEnv.NEXTAUTH_URL;
merged.NEXTAUTH_SECRET = env.NEXTAUTH_SECRET || process.env.NEXTAUTH_SECRET || crypto.randomBytes(32).toString("base64");
fs.writeFileSync(envPath, formatEnv(merged));
// Start with all existing env variables
// Only add/update NextAuth-related variables
env.GOOGLE_CLIENT_ID = env.GOOGLE_CLIENT_ID || process.env.GOOGLE_CLIENT_ID || defaultEnv.GOOGLE_CLIENT_ID;
env.GOOGLE_CLIENT_SECRET = env.GOOGLE_CLIENT_SECRET || process.env.GOOGLE_CLIENT_SECRET || defaultEnv.GOOGLE_CLIENT_SECRET;
env.NEXTAUTH_URL = env.NEXTAUTH_URL || process.env.NEXTAUTH_URL || defaultEnv.NEXTAUTH_URL;
env.NEXTAUTH_SECRET = env.NEXTAUTH_SECRET || process.env.NEXTAUTH_SECRET || crypto.randomBytes(32).toString("base64");
fs.writeFileSync(envPath, formatEnv(env));

Copilot uses AI. Check for mistakes.
Comment on lines +71 to +78
const created = existingContent ? "Updated" : "Created";
console.log(`${created} ${envPath} with Google OAuth + NextAuth defaults.`);
}

function ensureRoute() {
fs.mkdirSync(path.dirname(routePath), { recursive: true });
fs.writeFileSync(routePath, routeContent);
console.log(`Wrote NextAuth handler to ${routePath}.`);
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

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

[nitpick] The console output messages in the setup script don't follow the established pattern used in other scripts. Other scripts in this repository use German messages with prefixes like "BLOCKER:", "STABIL:", and emojis (✨, ❌, ✅).

Consider updating the messages to match the existing pattern:

console.log(`✅ ${created} ${envPath} mit Google OAuth + NextAuth defaults.`);

and

console.log(`✅ NextAuth Handler geschrieben: ${routePath}.`);

This maintains consistency with check-env.js and scan-secrets.js.

Suggested change
const created = existingContent ? "Updated" : "Created";
console.log(`${created} ${envPath} with Google OAuth + NextAuth defaults.`);
}
function ensureRoute() {
fs.mkdirSync(path.dirname(routePath), { recursive: true });
fs.writeFileSync(routePath, routeContent);
console.log(`Wrote NextAuth handler to ${routePath}.`);
const created = existingContent ? "Aktualisiert" : "Erstellt";
console.log(`${created} ${envPath} mit Google OAuth + NextAuth defaults.`);
}
function ensureRoute() {
fs.mkdirSync(path.dirname(routePath), { recursive: true });
fs.writeFileSync(routePath, routeContent);
console.log(` NextAuth Handler geschrieben: ${routePath}.`);

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant