feat: Make OAuth configuration environment-aware via NEXT_PUBLIC_APP_URL

- Convert client-metadata.json to dynamic API route reading from env vars
- Remove BLUESKY_CLIENT_ID and BLUESKY_REDIRECT_URI env vars
- All OAuth URLs now derived from NEXT_PUBLIC_APP_URL
- Implement production OAuth client (removes TODO/placeholder)
- Update .prod.env with production settings for www.ponderants.com
- Use https:// for production URLs
- Simplify environment configuration (single source of truth)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-09 15:08:04 +00:00
parent 95eeef0deb
commit 5247c142a4
4 changed files with 66 additions and 37 deletions

View File

@@ -15,7 +15,7 @@ let clientInstance: NodeOAuthClient | null = null;
* Get or create the singleton OAuth client instance.
*
* In development, uses the localhost client exception (no keys needed).
* In production, uses backend service with private keys (TODO).
* In production, uses client metadata served from /client-metadata.json.
*
* The client handles:
* - OAuth authorization flow with PKCE
@@ -29,11 +29,8 @@ export async function getOAuthClient(): Promise<NodeOAuthClient> {
}
const isDev = process.env.NODE_ENV === 'development';
const callbackUrl = process.env.BLUESKY_REDIRECT_URI;
if (!callbackUrl) {
throw new Error('BLUESKY_REDIRECT_URI environment variable is required');
}
const appUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000';
const callbackUrl = `${appUrl}/api/auth/callback`;
if (isDev) {
// Development: Use localhost loopback client
@@ -64,13 +61,31 @@ export async function getOAuthClient(): Promise<NodeOAuthClient> {
console.log('[OAuth] ✓ Development client initialized');
} else {
// Production: Backend service with keys
// TODO: Implement when deploying to production
// See plans/oauth-dpop-implementation.md for details
throw new Error(
'Production OAuth client not yet implemented. ' +
'See plans/oauth-dpop-implementation.md for production setup instructions.'
);
// Production: Use client metadata from /client-metadata.json endpoint
const clientId = `${appUrl}/client-metadata.json`;
console.log('[OAuth] Initializing production client');
console.log('[OAuth] client_id:', clientId);
console.log('[OAuth] callback_url:', callbackUrl);
clientInstance = new NodeOAuthClient({
clientMetadata: {
client_id: clientId,
client_name: 'Ponderants',
client_uri: appUrl,
redirect_uris: [callbackUrl],
scope: 'atproto transition:generic',
grant_types: ['authorization_code', 'refresh_token'],
response_types: ['code'],
token_endpoint_auth_method: 'none',
application_type: 'web',
dpop_bound_access_tokens: true,
},
stateStore: createStateStore(),
sessionStore: createSessionStore(),
});
console.log('[OAuth] ✓ Production client initialized');
}
return clientInstance;