From auth-identity
Wire Sign in with Google end-to-end: Google Cloud OAuth client + consent screen + redirect URIs + scopes, then via Supabase Auth's Google provider (and a note on the Auth.js / direct path). Step-by-step with verification checkpoints.
How this skill is triggered — by the user, by Claude, or both
Slash command
/auth-identity:google-sso-setupThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> **Invoked by:** the `auth-identity` team's identity-setup agent. Also consulted by `ravenclaude-core/security-reviewer` when reviewing any OAuth client registration change.
Invoked by: the
auth-identityteam's identity-setup agent. Also consulted byravenclaude-core/security-reviewerwhen reviewing any OAuth client registration change.When to invoke: new project needs Google SSO; switching from password auth to Google-first; adding Google as a second provider to an existing Supabase project; debugging a redirect-URI mismatch or consent-screen error.
Output: working Google SSO flow (OAuth client registered, Supabase provider enabled, redirect URIs set, scopes justified, test login passes) + security checklist signed off.
This skill authenticates the person (verifies their Google identity). It does not scope data access. Once the user is authenticated and auth.uid() is available, data-row scoping hands off to the data-platform plugin's RLS skills (rls-policy-authoring, jwt-embed-issuance). Do not duplicate RLS or embed-JWT mechanics here.
| Path | When to use |
|---|---|
| Supabase Auth → Google provider (recommended) | You are already using Supabase (Postgres + RLS). One config screen, managed token storage, auth.uid() available directly in RLS policies. |
| Auth.js (formerly NextAuth) + Google provider | You need framework-level flexibility (multiple providers, custom session shape, non-Supabase backend). More code, more config. |
| Google Identity Services SDK (direct) | Rare. Only when you want maximum control and are not using any managed auth layer. Avoid unless there is a strong reason. |
For this stack (Next.js + Supabase), default to the Supabase Auth path.
Platform: console.cloud.google.com [unverified — verify against current console UI]
openid, email, profile — nothing more. Adding extra scopes (Drive, Calendar, etc.) triggers extended verification and delays publishing.example.com). This controls what redirect URIs are allowed.https://app.example.com (and http://localhost:3000 for local dev).Verification checkpoint: the consent screen shows no "needs verification" banner for the three scopes above (
openid,profile). If it does, you added a sensitive scope by mistake.
[unverified — verify against current Supabase dashboard UI at supabase.com/docs/guides/auth/social-login/auth-google]
https://<project-ref>.supabase.co/auth/v1/callback
Copy it for Step 3.Back in Google Cloud Console → Credentials → your OAuth client:
https://<project-ref>.supabase.co/auth/v1/callbackhttp://localhost:54321/auth/v1/callback (Supabase local emulator default) [unverified — confirm your local port]Common error:
redirect_uri_mismatch— the URI in the OAuth flow does not exactly match one in the authorized list. Trailing slashes,httpvshttps, and port numbers all matter. Copy-paste exactly.
The following is a conceptual sketch. Security-review before shipping any auth code to production.
// lib/supabase/client.ts
import { createBrowserClient } from "@supabase/ssr";
export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
);
}
// components/GoogleSignInButton.tsx
"use client";
import { createClient } from "@/lib/supabase/client";
export function GoogleSignInButton() {
const supabase = createClient();
async function signInWithGoogle() {
await supabase.auth.signInWithOAuth({
provider: "google",
options: {
redirectTo: `${location.origin}/auth/callback`,
// scopes default to openid, email, profile — do not expand unless required
},
});
}
return <button onClick={signInWithGoogle}>Sign in with Google</button>;
}
// app/auth/callback/route.ts (Next.js App Router)
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";
import { NextRequest, NextResponse } from "next/server";
export async function GET(request: NextRequest) {
const requestUrl = new URL(request.url);
const code = requestUrl.searchParams.get("code");
const next = requestUrl.searchParams.get("next") ?? "/dashboard";
if (code) {
const cookieStore = cookies();
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{ cookies: { getAll: () => cookieStore.getAll(), setAll: (c) => c.forEach(({ name, value, options }) => cookieStore.set(name, value, options)) } },
);
await supabase.auth.exchangeCodeForSession(code);
}
return NextResponse.redirect(new URL(next, requestUrl.origin));
}
Security note: tokens are stored in HttpOnly cookies by
@supabase/ssr— do not extract them to localStorage. Seesession-and-token-managementskill and thenever-store-tokens-in-localstoragebest-practice.
auth.uid()// app/dashboard/page.tsx (server component)
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";
export default async function DashboardPage() {
const cookieStore = cookies();
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{ cookies: { getAll: () => cookieStore.getAll(), setAll: () => {} } },
);
const { data: { user } } = await supabase.auth.getUser();
if (!user) redirect("/login");
// user.id == auth.uid() in Postgres RLS policies
// Pass to data-platform RLS via the Supabase client — the JWT in the cookie
// carries auth.uid() automatically.
return <div>Welcome, {user.email}</div>;
}
The authenticated user's user.id maps to auth.uid() in Postgres RLS policies. Data scoping (which rows this user can read) is governed by the data-platform plugin's rls-policy-authoring skill — not by this plugin.
| Scope | When to include |
|---|---|
openid | Always — required for OIDC / ID token issuance |
email | Always — your app needs the user's email |
profile | Usually — name + avatar |
https://www.googleapis.com/auth/drive | Only if your app genuinely reads/writes Google Drive. Triggers Google's extended verification. |
| Any other Google API scope | Only if you have a concrete feature requirement. Minimise scope. |
If you are not using Supabase or need framework flexibility:
npm install next-auth [unverified — confirm current package name; may be next-auth@beta for v5][...nextauth]/route.ts with GoogleProvider({ clientId, clientSecret }).https://app.example.com/api/auth/callback/google.getServerSession / auth() (v5) in server components.cookies option with HttpOnly flags.The token-storage and PKCE mechanics in oauth-oidc-flow-design and session-and-token-management skills apply regardless of which path you take.
redirect_uri_mismatchopenid email profile without a documented feature requirementlocalStorage after the OAuth callback — use HttpOnly cookies (Supabase SSR handles this automatically)signInWithOAuth called server-side — it triggers a browser redirect; call it from a client component or a route handleropenid email profileNEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY in .env.local (never committed)SUPABASE_SERVICE_ROLE_KEY (if used) only in server-side env, never NEXT_PUBLIC_user.id non-nullauth.uid() resolves correctly in a test RLS queryravenclaude-core/security-reviewer)../oauth-oidc-flow-design/SKILL.md — why Authorization Code + PKCE, not Implicit; flow selection by client type../session-and-token-management/SKILL.md — HttpOnly cookie storage, refresh rotation, logout../protect-spa-and-api/SKILL.md — route guards, API token verification, CORS + CSRF../../templates/supabase-google-sso-setup.md — copy-paste walkthrough../../best-practices/prefer-managed-auth-over-rolling-your-own.md../../../data-platform/skills/rls-policy-authoring/SKILL.md — how auth.uid() drives data scoping (data authorization, not auth-identity's concern)../../../ravenclaude-core/agents/security-reviewer.md — mandatory review for any auth code going to productionnpx claudepluginhub mcorbett51090/ravenclaude --plugin auth-identityGuides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.
Dispatches multiple subagents concurrently for independent tasks without shared state. Use when facing 2+ unrelated failures or subsystems that can be investigated in parallel.