home / skills / secondsky / claude-skills / oauth-implementation
This skill helps implement OAuth 2.0 and OpenID Connect securely, guiding flows, PKCE, token handling, and session management for apps.
npx playbooks add skill secondsky/claude-skills --skill oauth-implementationReview the files below or copy the command above to add this skill to your agents.
---
name: oauth-implementation
description: OAuth 2.0 and OpenID Connect authentication with secure flows. Use for third-party integrations, SSO systems, token-based API access, or encountering authorization code flow, PKCE, token refresh, scope management errors.
---
# OAuth Implementation
Implement OAuth 2.0 and OpenID Connect for secure authentication.
## OAuth 2.0 Flows
| Flow | Use Case |
|------|----------|
| Authorization Code | Web apps (most secure) |
| Authorization Code + PKCE | SPAs, mobile apps |
| Client Credentials | Service-to-service |
| Refresh Token | Session renewal |
## Authorization Code Flow (Express)
```javascript
const express = require('express');
const jwt = require('jsonwebtoken');
// Step 1: Redirect to authorization
app.get('/auth/login', (req, res) => {
const state = crypto.randomBytes(16).toString('hex');
req.session.oauthState = state;
const params = new URLSearchParams({
client_id: process.env.CLIENT_ID,
redirect_uri: process.env.REDIRECT_URI,
response_type: 'code',
scope: 'openid profile email',
state
});
res.redirect(`${PROVIDER_URL}/authorize?${params}`);
});
// Step 2: Handle callback
app.get('/auth/callback', async (req, res) => {
if (req.query.state !== req.session.oauthState) {
return res.status(400).json({ error: 'Invalid state' });
}
const tokenResponse = await fetch(`${PROVIDER_URL}/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code: req.query.code,
redirect_uri: process.env.REDIRECT_URI,
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET
})
});
const tokens = await tokenResponse.json();
// Store tokens securely and create session
});
```
## PKCE for Public Clients
```javascript
function generatePKCE() {
const verifier = crypto.randomBytes(32).toString('base64url');
const challenge = crypto
.createHash('sha256')
.update(verifier)
.digest('base64url');
return { verifier, challenge };
}
```
## Security Requirements
- Always use HTTPS
- Validate redirect URIs strictly
- Use PKCE for public clients
- Store tokens in HttpOnly cookies
- Implement token rotation
- Use short-lived access tokens (15 min)
## Additional Implementations
See [references/python-java.md](references/python-java.md) for:
- Python Flask with Authlib OIDC provider
- OpenID Connect discovery and JWKS endpoints
- Java Spring Security OAuth2 server
- Token introspection and revocation
## Never Do
- Store tokens in localStorage
- Use implicit flow
- Skip state parameter validation
- Expose client secrets in frontend
- Use long-lived access tokens
This skill implements OAuth 2.0 and OpenID Connect authentication flows with secure defaults for web, mobile, and service-to-service use. It provides production-ready patterns for authorization code, PKCE, client credentials, and refresh token handling in TypeScript environments. The goal is a secure, auditable auth layer you can plug into SSO, third-party integrations, or token-based APIs.
The skill wires up redirect-based authorization code flow with state validation and token exchange, including server-side storage of tokens and session creation. For public clients it generates PKCE verifier/challenge pairs and validates token rotation and refresh cycles. It enforces security controls like HTTPS-only cookies, strict redirect URI checks, short-lived access tokens, and optional JWKS discovery for ID token validation.
Should I ever use implicit flow?
No. Implicit flow is deprecated and insecure; use authorization code with PKCE for public clients instead.
Where should I store access and refresh tokens?
Store tokens server-side or in HttpOnly, Secure cookies. Never store tokens in localStorage or expose client secrets in the frontend.