oauth-integrations
Install this skill
npx skills add jezweb/claude-skillsWorks across Claude Code, Cursor, Codex, Copilot & Antigravity
This skill facilitates manual OAuth implementation for GitHub and Microsoft Entra ID within edge-compute environments like Cloudflare Workers. Standard SDKs such as MSAL.js often fail in these runtimes because they rely on Node.js-specific modules or browser-only APIs. This skill provides the logic to construct authorization URLs, perform token exchanges via fetch, and handle provider-specific nuances such as GitHub's strict User-Agent requirements or private email retrieval. It guides developers through the specific header configurations needed for JSON responses and explains how to map user identity data from Microsoft Graph versus GitHub's REST API. By following these patterns, you can maintain secure, state-managed authentication flows while keeping your edge deployment lightweight and dependency-free, avoiding the common pitfalls associated with server-side SDKs in restricted V8 isolate environments.
When to Use This Skill
- β’Adding GitHub-based sign-in to a Cloudflare Worker application
- β’Authenticating internal enterprise users via Azure AD at the edge
- β’Building custom API gateways that require social login verification
- β’Securing serverless functions without loading heavy authentication libraries
How to Invoke This Skill
Example prompts that trigger this skill in Claude Code, Cursor, or Antigravity:
- βHow do I implement GitHub OAuth in a Cloudflare Worker?
- βWhy does MSAL.js fail in my edge function?
- βHow to handle GitHub private emails in an auth flow?
- βConstructing an OAuth callback handler for Microsoft Entra
- βWhat headers are required for GitHub API authentication?
Pro Tips
- π‘Always validate the `state` parameter in OAuth callbacks to prevent Cross-Site Request Forgery (CSRF) attacks.
- π‘Store sensitive credentials (client ID, client secret) securely as environment variables, never hardcoded in your source.
- π‘Consider using a dedicated OAuth library or framework for edge environments if available, to abstract away common boilerplate and security considerations.
What this skill does
- β’Construct manual OAuth authorization URL strings for GitHub and Entra ID
- β’Perform backend-to-backend token exchanges using the Fetch API
- β’Handle GitHub private email resolution via the /user/emails endpoint
- β’Map Microsoft identity attributes from the Graph /me endpoint
- β’Implement token refresh logic for persistent Microsoft session management
When not to use it
- βWhen your runtime supports full Node.js modules and standard MSAL.js
- βWhen you require a UI-based identity management flow instead of a headless API integration
Example workflow
- Redirect the user to the provider authorization endpoint with necessary scopes
- Capture the code returned to your redirect URL
- POST the code and client secrets to the provider's token exchange endpoint
- Store the returned access token securely
- Fetch user identity profile using the token and appropriate headers
Prerequisites
- βConfigured OAuth application on GitHub or Azure Portal
- βValid Client ID and Client Secret
- βDefined Redirect URI
- βBasic understanding of JWT and OAuth 2.0 flows
Pitfalls & limitations
- !Failing to include a User-Agent header which causes GitHub to return a 403
- !Using wildcards in Redirect URIs which results in immediate validation failure
- !Forgetting the User.Read scope in Microsoft Entra leading to empty user profiles
FAQ
How it compares
Generic prompts often suggest standard SDKs like MSAL that break in edge runtimes; this skill provides specific, runtime-compatible logic for manual OAuth handling.
π Full skill instructions β original source: jezweb/claude-skills
Implement GitHub and Microsoft OAuth in Cloudflare Workers and other edge runtimes.
## GitHub OAuth
### Required Headers
GitHub API has strict requirements that differ from other providers.
| Header | Requirement |
|--------|-------------|
|
User-Agent | **REQUIRED** - Returns 403 without it ||
Accept | application/vnd.github+json recommended |const resp = await fetch('https://api.github.com/user', {
headers: {
Authorization: Bearer ${accessToken},
'User-Agent': 'MyApp/1.0', // Required!
'Accept': 'application/vnd.github+json',
},
});### Private Email Handling
GitHub users can set email to private (
/user returns email: null).if (!userData.email) {
const emails = await fetch('https://api.github.com/user/emails', { headers })
.then(r => r.json());
userData.email = emails.find(e => e.primary && e.verified)?.email;
}Requires
user:email scope.### Token Exchange
Token exchange returns form-encoded by default. Add Accept header for JSON:
const tokenResponse = await fetch('https://github.com/login/oauth/access_token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json', // Get JSON response
},
body: new URLSearchParams({ code, client_id, client_secret, redirect_uri }),
});### GitHub OAuth Notes
| Issue | Solution |
|-------|----------|
| Callback URL | Must be EXACT - no wildcards, no subdirectory matching |
| Token exchange returns form-encoded | Add
'Accept': 'application/json' header || Tokens don't expire | No refresh flow needed, but revoked = full re-auth |
## Microsoft Entra (Azure AD) OAuth
### Why MSAL Doesn't Work in Workers
MSAL.js depends on:
- Browser APIs (localStorage, sessionStorage, DOM)
- Node.js crypto module
Cloudflare's V8 isolate runtime has neither. Use manual OAuth instead:
1. Manual OAuth URL construction
2. Direct token exchange via fetch
3. JWT validation with
jose library### Required Scopes
// For user identity (email, name, profile picture)
const scope = 'openid email profile User.Read';
// For refresh tokens (long-lived sessions)
const scope = 'openid email profile User.Read offline_access';**Critical**:
User.Read is required for Microsoft Graph /me endpoint. Without it, token exchange succeeds but user info fetch returns 403.### User Info Endpoint
// Microsoft Graph /me endpoint
const resp = await fetch('https://graph.microsoft.com/v1.0/me', {
headers: { Authorization: Bearer ${accessToken} },
});
// Email may be in different fields
const email = data.mail || data.userPrincipalName;### Tenant Configuration
| Tenant Value | Who Can Sign In |
|--------------|-----------------|
|
common | Any Microsoft account (personal + work) ||
organizations | Work/school accounts only ||
consumers | Personal Microsoft accounts only ||
{tenant-id} | Specific organization only |### Azure Portal Setup
1. App Registration β New registration
2. Platform: **Web** (not SPA) for server-side OAuth
3. Redirect URIs: Add both
/callback and /admin/callback4. Certificates & secrets β New client secret
### Token Lifetimes
| Token Type | Default Lifetime | Notes |
|------------|------------------|-------|
| Access token | 60-90 minutes | Configurable via token lifetime policies |
| Refresh token | 90 days | Revoked on password change |
| ID token | 60 minutes | Same as access token |
**Best Practice**: Always request
offline_access scope and implement refresh token flow for sessions longer than 1 hour.## Common Corrections
| If Claude suggests... | Use instead... |
|----------------------|----------------|
| GitHub fetch without User-Agent | Add
'User-Agent': 'AppName/1.0' (REQUIRED) || Using MSAL.js in Workers | Manual OAuth + jose for JWT validation |
| Microsoft scope without User.Read | Add
User.Read scope || Fetching email from token claims only | Use Graph
/me endpoint |## Error Reference
### GitHub Errors
| Error | Cause | Fix |
|-------|-------|-----|
| 403 Forbidden | Missing User-Agent header | Add User-Agent header |
|
email: null | User has private email | Fetch /user/emails with user:email scope |### Microsoft Errors
| Error | Cause | Fix |
|-------|-------|-----|
| AADSTS50058 | Silent auth failed | Use interactive flow |
| AADSTS700084 | Refresh token expired | Re-authenticate user |
| 403 on Graph /me | Missing User.Read scope | Add User.Read to scopes |
## Reference
- GitHub API: https://docs.github.com/en/rest
- GitHub OAuth: https://docs.github.com/en/apps/oauth-apps
- Microsoft Graph permissions: https://learn.microsoft.com/en-us/graph/permissions-reference
- AADSTS error codes: https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes
---
---
globs: ["**/*github*", "**/oauth/**/*.ts", "**/auth/**/*.ts", "**/callback*.ts"]
---
# GitHub API & OAuth
GitHub API has strict requirements that differ from Google/Microsoft.
## Required Headers
| If Claude suggests... | Use instead... |
|----------------------|----------------|
| Fetch without User-Agent |
'User-Agent': 'AppName/1.0' (REQUIRED - 403 without) || No Accept header |
'Accept': 'application/vnd.github+json' |const resp = await fetch('https://api.github.com/user', {
headers: {
Authorization: Bearer ${accessToken},
'User-Agent': 'MyApp/1.0',
'Accept': 'application/vnd.github+json',
},
});## Private Email Handling
GitHub users can set email to private (
/user returns email: null).if (!userData.email) {
const emails = await fetch('https://api.github.com/user/emails', { headers }).then(r => r.json());
userData.email = emails.find(e => e.primary && e.verified)?.email;
}Requires
user:email scope.## OAuth Specifics
| Issue | Solution |
|-------|----------|
| Callback URL | Must be EXACT - no wildcards, no subdirectory matching |
| Token exchange returns form-encoded | Add
'Accept': 'application/json' header || Tokens don't expire | No refresh flow needed, but revoked = full re-auth |
## Token Exchange
const tokenResponse = await fetch('https://github.com/login/oauth/access_token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json',
},
body: new URLSearchParams({ code, client_id, client_secret, redirect_uri }),
});---
---
globs: ["**/oauth*.ts", "**/auth*.ts", "**/microsoft*.ts", "**/azure*.ts", "**/entra*.ts", "**/callback*.ts"]
---
# Microsoft OAuth in Cloudflare Workers
Patterns for implementing Microsoft Entra (Azure AD) OAuth in Cloudflare Workers without MSAL.
## Corrections
| If Claude suggests... | Use instead... |
|----------------------|----------------|
| Using MSAL.js in Workers | Manual OAuth + jose for JWT validation |
| Scope
openid email profile for user info | Add User.Read scope || Fetching email from token claims only | Use Microsoft Graph
/me endpoint |## Why MSAL Doesn't Work in Workers
MSAL.js depends on:
- Browser APIs (localStorage, sessionStorage, DOM)
- Node.js crypto module
Cloudflare's V8 isolate runtime has neither. The workaround is simpler and more secure:
1. Manual OAuth URL construction
2. Direct token exchange via fetch
3. JWT validation with
jose library## Required Scopes
// For user identity (email, name, profile picture)
const scope = 'openid email profile User.Read';
// For refresh tokens (long-lived sessions)
const scope = 'openid email profile User.Read offline_access';**Critical**:
User.Read is required for Microsoft Graph /me endpoint. Without it, token exchange succeeds but user info fetch returns 403.## User Info Endpoint
// Microsoft Graph /me endpoint
const resp = await fetch('https://graph.microsoft.com/v1.0/me', {
headers: { Authorization: Bearer ${accessToken} },
});
// Email may be in different fields
const email = data.mail || data.userPrincipalName;## Tenant Configuration
| Tenant Value | Who Can Sign In |
|--------------|-----------------|
|
common | Any Microsoft account (personal + work) ||
organizations | Work/school accounts only ||
consumers | Personal Microsoft accounts only ||
{tenant-id} | Specific organization only |## Azure Portal Setup
1. App Registration β New registration
2. Platform: **Web** (not SPA) for server-side OAuth
3. Redirect URIs: Add both
/callback and /admin/callback4. Certificates & secrets β New client secret
## Token Lifetimes
| Token Type | Default Lifetime | Notes |
|------------|------------------|-------|
| Access token | 60-90 minutes | Configurable via token lifetime policies |
| Refresh token | 90 days | Revoked on password change, can be extended |
| ID token | 60 minutes | Same as access token |
**Best Practice**: Always request
offline_access scope and implement refresh token flow for sessions longer than 1 hour.π **Source**: https://learn.microsoft.com/en-us/entra/identity-platform/access-tokens#token-lifetime
## Reference
- Graph API permissions: https://learn.microsoft.com/en-us/graph/permissions-reference
- AADSTS error codes: https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes
How to Use This Skill Unit
Option A: Project-Specific (Recommended)
- Click "Download" above
- In your project, create the directory:
.agent/skills/oauth-integrations/ - Save the file as
SKILL.md - The agent will automatically discover the skill based on its description.
Option B: Global Installation (All Agents)
Save the file to these locations to make it available across all projects:
- Claude Code:
~/.claude/skills/jezweb/claude-skills/oauth-integrations/SKILL.md - Cursor:
~/.cursor/skills/jezweb/claude-skills/oauth-integrations/SKILL.md - Antigravity:
~/.gemini/antigravity/skills/jezweb/claude-skills/oauth-integrations/SKILL.md
π Install with CLI:npx skills add jezweb/claude-skills