Loading...
Loading...
Implement OAuth 2.0 authentication with GitHub and Microsoft Entra (Azure AD) in Cloudflare Workers and other edge environments. Covers provider-specific quirks, required headers, scope requirements, and token handling without MSAL. Use when: implementing GitHub OAuth, Microsoft/Azure AD authentication, handling OAuth callbacks, or troubleshooting 403 errors in OAuth flows.
npx skill4agent add jezweb/claude-skills oauth-integrations| Header | Requirement |
|---|---|
| REQUIRED - Returns 403 without it |
| |
const resp = await fetch('https://api.github.com/user', {
headers: {
Authorization: `Bearer ${accessToken}`,
'User-Agent': 'MyApp/1.0', // Required!
'Accept': 'application/vnd.github+json',
},
});/useremail: nullif (!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;
}user:emailconst 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 }),
});| Issue | Solution |
|---|---|
| Callback URL | Must be EXACT - no wildcards, no subdirectory matching |
| Token exchange returns form-encoded | Add |
| Tokens don't expire | No refresh flow needed, but revoked = full re-auth |
jose// 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';User.Read/me// 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 Value | Who Can Sign In |
|---|---|
| Any Microsoft account (personal + work) |
| Work/school accounts only |
| Personal Microsoft accounts only |
| Specific organization only |
/callback/admin/callback| 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 |
offline_access| If Claude suggests... | Use instead... |
|---|---|
| GitHub fetch without User-Agent | Add |
| Using MSAL.js in Workers | Manual OAuth + jose for JWT validation |
| Microsoft scope without User.Read | Add |
| Fetching email from token claims only | Use Graph |
| Error | Cause | Fix |
|---|---|---|
| 403 Forbidden | Missing User-Agent header | Add User-Agent header |
| User has private email | Fetch |
| 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 |