Claude-skill-registry bknd-oauth-setup
Use when configuring OAuth or social login providers in a Bknd application. Covers Google OAuth, GitHub OAuth, custom OAuth providers, callback URLs, environment variables, and frontend OAuth integration.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/bknd-oauth-setup" ~/.claude/skills/majiayu000-claude-skill-registry-bknd-oauth-setup && rm -rf "$T"
skills/data/bknd-oauth-setup/SKILL.mdOAuth/Social Login Setup
Configure OAuth authentication providers (Google, GitHub, or custom) in Bknd.
Prerequisites
- Bknd project with auth enabled (
)bknd-setup-auth - OAuth app credentials from provider (client_id, client_secret)
- For Google: Google Cloud Console project with OAuth 2.0 credentials
- For GitHub: GitHub OAuth App in Developer Settings
- For custom: Provider's authorization server endpoints
When to Use UI Mode
- Testing OAuth login flow via admin panel
- Viewing enabled OAuth strategies
- Checking callback URL configuration
UI steps: Admin Panel > Auth > Strategies
When to Use Code Mode
- Configuring OAuth providers in
bknd.config.ts - Setting up multiple OAuth providers
- Implementing frontend OAuth buttons
- Configuring custom OAuth providers (Slack, Discord, etc.)
Built-in OAuth Providers
Bknd has pre-configured support for:
| Provider | Type | Scopes |
|---|---|---|
| OIDC | openid, email, profile |
| OAuth2 | read:user, user:email |
Google OAuth Setup
1. Create Google OAuth Credentials
- Go to Google Cloud Console
- Create project or select existing
- Navigate: APIs & Services > Credentials
- Click Create Credentials > OAuth client ID
- Application type: Web application
- Add authorized redirect URI:
(Replace with your production URL)http://localhost:7654/api/auth/google/callback - Copy Client ID and Client Secret
2. Configure Bknd
// bknd.config.ts import { defineConfig } from "bknd"; export default defineConfig({ auth: { enabled: true, jwt: { secret: process.env.JWT_SECRET!, expires: 604800, }, strategies: { google: { type: "oauth", enabled: true, config: { name: "google", client: { client_id: process.env.GOOGLE_CLIENT_ID!, client_secret: process.env.GOOGLE_CLIENT_SECRET!, }, }, }, }, }, });
3. Environment Variables
# .env GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com GOOGLE_CLIENT_SECRET=your-google-client-secret JWT_SECRET=your-256-bit-secret
GitHub OAuth Setup
1. Create GitHub OAuth App
- Go to GitHub Developer Settings
- Click New OAuth App
- Fill in:
- Application name: Your app name
- Homepage URL:
http://localhost:7654 - Authorization callback URL:
http://localhost:7654/api/auth/github/callback
- Click Register application
- Copy Client ID and generate Client Secret
2. Configure Bknd
// bknd.config.ts import { defineConfig } from "bknd"; export default defineConfig({ auth: { enabled: true, jwt: { secret: process.env.JWT_SECRET!, expires: 604800, }, strategies: { github: { type: "oauth", enabled: true, config: { name: "github", client: { client_id: process.env.GITHUB_CLIENT_ID!, client_secret: process.env.GITHUB_CLIENT_SECRET!, }, }, }, }, }, });
3. Environment Variables
# .env GITHUB_CLIENT_ID=your-github-client-id GITHUB_CLIENT_SECRET=your-github-client-secret JWT_SECRET=your-256-bit-secret
Multiple OAuth Providers
Enable multiple providers simultaneously:
// bknd.config.ts import { defineConfig } from "bknd"; export default defineConfig({ auth: { enabled: true, jwt: { secret: process.env.JWT_SECRET!, expires: 604800, }, strategies: { password: { type: "password", enabled: true, config: { hashing: "bcrypt", rounds: 4, }, }, google: { type: "oauth", enabled: true, config: { name: "google", client: { client_id: process.env.GOOGLE_CLIENT_ID!, client_secret: process.env.GOOGLE_CLIENT_SECRET!, }, }, }, github: { type: "oauth", enabled: true, config: { name: "github", client: { client_id: process.env.GITHUB_CLIENT_ID!, client_secret: process.env.GITHUB_CLIENT_SECRET!, }, }, }, }, }, });
Custom OAuth Provider
For providers not built-in (Slack, Discord, Microsoft, etc.):
// bknd.config.ts import { defineConfig } from "bknd"; export default defineConfig({ auth: { enabled: true, strategies: { slack: { type: "custom_oauth", enabled: true, config: { name: "slack", type: "oauth2", // "oauth2" or "oidc" client: { client_id: process.env.SLACK_CLIENT_ID!, client_secret: process.env.SLACK_CLIENT_SECRET!, token_endpoint_auth_method: "client_secret_basic", }, as: { issuer: "https://slack.com", authorization_endpoint: "https://slack.com/oauth/v2/authorize", token_endpoint: "https://slack.com/api/oauth.v2.access", userinfo_endpoint: "https://slack.com/api/users.identity", scopes_supported: ["openid", "profile", "email"], }, }, }, }, }, });
Custom OAuth config options:
| Property | Type | Required | Description |
|---|---|---|---|
| string | Yes | Strategy identifier |
| | | Yes | Protocol type |
| string | Yes | OAuth client ID |
| string | Yes | OAuth client secret |
| string | Yes | Auth method () |
| string | Yes | Authorization server issuer URL |
| string | Yes | OAuth authorize URL |
| string | Yes | Token exchange URL |
| string | No | User info URL |
| string[] | No | Available scopes |
OAuth Endpoints
Once configured, Bknd exposes these endpoints:
| Method | Path | Description |
|---|---|---|
| POST | | Start OAuth login (cookie mode) |
| POST | | Start OAuth registration (cookie mode) |
| GET | | Get OAuth URL (token mode) |
| GET | | OAuth callback handler |
Frontend Integration
Cookie-Based Flow (Recommended for SSR)
// Redirect user to OAuth provider function loginWithGoogle() { // POST redirects to Google, callback sets cookie, redirects to pathSuccess const form = document.createElement("form"); form.method = "POST"; form.action = "http://localhost:7654/api/auth/google/login"; document.body.appendChild(form); form.submit(); }
Token-Based Flow (SPA)
import { Api } from "bknd"; const api = new Api({ host: "http://localhost:7654", storage: localStorage, }); async function loginWithGoogle() { // Get OAuth URL const { data } = await api.auth.login("google"); if (data?.url) { // Store challenge for callback verification sessionStorage.setItem("oauth_challenge", data.challenge); // Redirect to Google window.location.href = data.url; } } // On callback page, complete the flow async function handleOAuthCallback() { const params = new URLSearchParams(window.location.search); const code = params.get("code"); const challenge = sessionStorage.getItem("oauth_challenge"); if (code && challenge) { // Complete OAuth flow const response = await fetch( `http://localhost:7654/api/auth/google/callback?code=${code}`, { headers: { "X-State-Challenge": challenge, "X-State-Action": "login", }, } ); const { user, token } = await response.json(); api.setToken(token); sessionStorage.removeItem("oauth_challenge"); } }
React OAuth Buttons
import { useAuth } from "@bknd/react"; function OAuthButtons() { const { login } = useAuth(); async function handleGoogleLogin() { const result = await login("google"); if (result.data?.url) { window.location.href = result.data.url; } } async function handleGitHubLogin() { const result = await login("github"); if (result.data?.url) { window.location.href = result.data.url; } } return ( <div> <button onClick={handleGoogleLogin}> Continue with Google </button> <button onClick={handleGitHubLogin}> Continue with GitHub </button> </div> ); }
OAuth Callback Page (React)
import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { Api } from "bknd"; const api = new Api({ host: import.meta.env.VITE_API_URL, storage: localStorage, }); function OAuthCallback() { const navigate = useNavigate(); const [error, setError] = useState<string | null>(null); useEffect(() => { const params = new URLSearchParams(window.location.search); const code = params.get("code"); const challenge = sessionStorage.getItem("oauth_challenge"); if (!code) { setError("No authorization code received"); return; } if (!challenge) { setError("OAuth session expired. Please try again."); return; } fetch(`${import.meta.env.VITE_API_URL}/api/auth/google/callback?code=${code}`, { headers: { "X-State-Challenge": challenge, "X-State-Action": "login", }, }) .then((res) => res.json()) .then(({ user, token }) => { api.setToken(token); sessionStorage.removeItem("oauth_challenge"); navigate("/dashboard"); }) .catch((err) => { setError("Authentication failed. Please try again."); }); }, [navigate]); if (error) { return ( <div> <p>{error}</p> <a href="/login">Back to login</a> </div> ); } return <div>Completing sign in...</div>; }
Callback URL Configuration
Development:
http://localhost:7654/api/auth/{provider}/callback
Production:
https://api.yourapp.com/api/auth/{provider}/callback
Update redirect URIs in provider dashboard when deploying.
Cookie Settings for OAuth
Configure cookie behavior for OAuth flow:
{ auth: { cookie: { secure: process.env.NODE_ENV === "production", // HTTPS only in prod httpOnly: true, sameSite: "lax", // Required for OAuth redirects pathSuccess: "/dashboard", // Redirect after login pathLoggedOut: "/login", // Redirect after logout }, }, }
Common Pitfalls
Callback URL Mismatch
Problem:
redirect_uri_mismatch error from provider
Fix: Ensure callback URL exactly matches provider dashboard:
# Provider dashboard (Google/GitHub) http://localhost:7654/api/auth/google/callback # Must match Bknd host configuration host: "http://localhost:7654"
Missing Environment Variables
Problem: OAuth login fails silently
Fix: Verify all required env vars are set:
# Required for Google OAuth GOOGLE_CLIENT_ID=... GOOGLE_CLIENT_SECRET=... # Required for GitHub OAuth GITHUB_CLIENT_ID=... GITHUB_CLIENT_SECRET=... # Always required JWT_SECRET=...
CORS Issues (SPA)
Problem: CORS error when calling OAuth endpoints
Fix: Configure CORS on backend:
{ server: { cors: { origin: ["http://localhost:3000"], // Your frontend URL credentials: true, }, }, }
Strategy Conflict
Problem: "User signed up with different strategy"
Cause: User has existing account with different auth method
Solution: Users can only have ONE strategy. Guide them to use their original login method or implement account linking (requires custom code).
OAuth Cookie Not Set
Problem: Cookie not received after OAuth callback
Fix: Ensure secure cookie settings:
{ auth: { cookie: { secure: false, // Set false for localhost (no HTTPS) sameSite: "lax", // Required for OAuth redirects }, }, }
Invalid Scopes
Problem: Provider rejects scope request
Fix: Use only scopes from
scopes_supported:
// Google OIDC defaults scopes_supported: ["openid", "email", "profile"] // GitHub OAuth2 defaults scopes_supported: ["read:user", "user:email"]
Verification
Test OAuth setup:
1. Check available strategies:
curl http://localhost:7654/api/auth/strategies
Response should include your OAuth providers:
{ "strategies": ["password", "google", "github"], "basepath": "/api/auth" }
2. Test OAuth URL generation:
curl http://localhost:7654/api/auth/google/login
Response:
{ "url": "https://accounts.google.com/o/oauth2/v2/auth?...", "challenge": "...", "action": "login" }
3. Complete full OAuth flow:
- Visit the OAuth URL in browser
- Complete provider login
- Verify redirect to
pathSuccess - Check
returns userapi/auth/me
DOs and DON'Ts
DO:
- Store client secrets in environment variables
- Use HTTPS in production
- Set
for cookie (required for OAuth redirects)sameSite: "lax" - Match callback URLs exactly in provider dashboard
- Test OAuth flow in incognito (avoid cached sessions)
- Set appropriate JWT expiration
DON'T:
- Hardcode client secrets in code
- Use
cookie on localhost (no HTTPS)secure: true - Mix up client_id and client_secret
- Forget to update callback URLs for production
- Expect users to have multiple strategies (one per user)
- Skip the challenge verification in token flow
Related Skills
- bknd-setup-auth - Initialize authentication system
- bknd-login-flow - Login/logout functionality
- bknd-registration - User registration setup
- bknd-session-handling - Manage user sessions
- bknd-create-role - Define user roles for OAuth users