Secure OAuth 2.0 implementation patterns for social login and API authorization in web applications
# OAuth 2.0 Authentication Patterns for Google Antigravity
Implement secure OAuth 2.0 authentication with Google Antigravity's Gemini 3 engine. This guide covers authorization code flow, PKCE, token management, and multi-provider social login patterns.
## OAuth Configuration
```typescript
// lib/oauth/config.ts
export interface OAuthProvider {
name: string;
clientId: string;
clientSecret: string;
authorizationUrl: string;
tokenUrl: string;
userInfoUrl: string;
scopes: string[];
redirectUri: string;
}
export const oauthProviders: Record<string, OAuthProvider> = {
google: {
name: 'Google',
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
authorizationUrl: 'https://accounts.google.com/o/oauth2/v2/auth',
tokenUrl: 'https://oauth2.googleapis.com/token',
userInfoUrl: 'https://www.googleapis.com/oauth2/v2/userinfo',
scopes: ['openid', 'email', 'profile'],
redirectUri: `${process.env.APP_URL}/api/auth/callback/google`,
},
github: {
name: 'GitHub',
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
authorizationUrl: 'https://github.com/login/oauth/authorize',
tokenUrl: 'https://github.com/login/oauth/access_token',
userInfoUrl: 'https://api.github.com/user',
scopes: ['read:user', 'user:email'],
redirectUri: `${process.env.APP_URL}/api/auth/callback/github`,
},
};
```
## Authorization Code Flow with PKCE
```typescript
// lib/oauth/pkce.ts
import { randomBytes, createHash } from 'crypto';
export function generateCodeVerifier(): string {
return randomBytes(32)
.toString('base64url')
.replace(/[^a-zA-Z0-9]/g, '')
.substring(0, 128);
}
export function generateCodeChallenge(verifier: string): string {
return createHash('sha256')
.update(verifier)
.digest('base64url');
}
export function generateState(): string {
return randomBytes(16).toString('hex');
}
// lib/oauth/client.ts
import { oauthProviders, type OAuthProvider } from './config';
import { generateCodeVerifier, generateCodeChallenge, generateState } from './pkce';
export class OAuthClient {
private provider: OAuthProvider;
constructor(providerName: string) {
this.provider = oauthProviders[providerName];
if (!this.provider) {
throw new Error(`Unknown OAuth provider: ${providerName}`);
}
}
getAuthorizationUrl(options?: { state?: string; codeVerifier?: string }): {
url: string;
state: string;
codeVerifier: string;
} {
const state = options?.state ?? generateState();
const codeVerifier = options?.codeVerifier ?? generateCodeVerifier();
const codeChallenge = generateCodeChallenge(codeVerifier);
const params = new URLSearchParams({
client_id: this.provider.clientId,
redirect_uri: this.provider.redirectUri,
response_type: 'code',
scope: this.provider.scopes.join(' '),
state,
code_challenge: codeChallenge,
code_challenge_method: 'S256',
});
return {
url: `${this.provider.authorizationUrl}?${params.toString()}``,
state,
codeVerifier,
};
}
async exchangeCode(code: string, codeVerifier: string): Promise<TokenResponse> {
const response = await fetch(this.provider.tokenUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
client_id: this.provider.clientId,
client_secret: this.provider.clientSecret,
code,
redirect_uri: this.provider.redirectUri,
code_verifier: codeVerifier,
}),
});
if (!response.ok) {
throw new Error('Token exchange failed');
}
return response.json();
}
async getUserInfo(accessToken: string): Promise<OAuthUser> {
const response = await fetch(this.provider.userInfoUrl, {
headers: { Authorization: `Bearer ${accessToken}` },
});
if (!response.ok) {
throw new Error('Failed to fetch user info');
}
return response.json();
}
}
interface TokenResponse {
access_token: string;
refresh_token?: string;
expires_in: number;
token_type: string;
}
interface OAuthUser {
id: string;
email: string;
name?: string;
picture?: string;
}
```
## Callback Handler
```typescript
// app/api/auth/callback/[provider]/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
import { OAuthClient } from '@/lib/oauth/client';
import { createSession } from '@/lib/session';
import { findOrCreateUser } from '@/lib/users';
export async function GET(
request: NextRequest,
{ params }: { params: { provider: string } }
) {
const searchParams = request.nextUrl.searchParams;
const code = searchParams.get('code');
const state = searchParams.get('state');
const error = searchParams.get('error');
if (error) {
return NextResponse.redirect(new URL('/login?error=oauth_denied', request.url));
}
const cookieStore = cookies();
const storedState = cookieStore.get('oauth_state')?.value;
const codeVerifier = cookieStore.get('oauth_verifier')?.value;
if (!code || !state || state !== storedState || !codeVerifier) {
return NextResponse.redirect(new URL('/login?error=invalid_state', request.url));
}
try {
const client = new OAuthClient(params.provider);
const tokens = await client.exchangeCode(code, codeVerifier);
const oauthUser = await client.getUserInfo(tokens.access_token);
const user = await findOrCreateUser({
email: oauthUser.email,
name: oauthUser.name,
avatar: oauthUser.picture,
provider: params.provider,
providerId: oauthUser.id,
});
const response = NextResponse.redirect(new URL('/dashboard', request.url));
await createSession(user.id, response);
response.cookies.delete('oauth_state');
response.cookies.delete('oauth_verifier');
return response;
} catch (error) {
console.error('OAuth callback error:', error);
return NextResponse.redirect(new URL('/login?error=oauth_failed', request.url));
}
}
```
## Best Practices
Google Antigravity's Gemini 3 engine recommends these OAuth patterns: Always use PKCE even for server-side applications. Validate state parameters to prevent CSRF attacks. Store tokens securely and implement proper refresh logic. Use short-lived access tokens with refresh tokens for better security. Implement proper logout that revokes tokens at the provider level.This OAuth prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their oauth implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For OAuth projects, consider mentioning your framework version, coding style, and any specific libraries you're using.