or run

tessl search
Log in

oauth-integrations

tessl install github:jezweb/claude-skills --skill oauth-integrations

github.com/jezweb/claude-skills

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.

Review Score

93%

Validation Score

15/16

Implementation Score

88%

Activation Score

100%

OAuth Integrations for Edge Environments

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.

HeaderRequirement
User-AgentREQUIRED - Returns 403 without it
Acceptapplication/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

IssueSolution
Callback URLMust be EXACT - no wildcards, no subdirectory matching
Token exchange returns form-encodedAdd 'Accept': 'application/json' header
Tokens don't expireNo 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 ValueWho Can Sign In
commonAny Microsoft account (personal + work)
organizationsWork/school accounts only
consumersPersonal 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/callback
  4. Certificates & secrets → New client secret

Token Lifetimes

Token TypeDefault LifetimeNotes
Access token60-90 minutesConfigurable via token lifetime policies
Refresh token90 daysRevoked on password change
ID token60 minutesSame 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-AgentAdd 'User-Agent': 'AppName/1.0' (REQUIRED)
Using MSAL.js in WorkersManual OAuth + jose for JWT validation
Microsoft scope without User.ReadAdd User.Read scope
Fetching email from token claims onlyUse Graph /me endpoint

Error Reference

GitHub Errors

ErrorCauseFix
403 ForbiddenMissing User-Agent headerAdd User-Agent header
email: nullUser has private emailFetch /user/emails with user:email scope

Microsoft Errors

ErrorCauseFix
AADSTS50058Silent auth failedUse interactive flow
AADSTS700084Refresh token expiredRe-authenticate user
403 on Graph /meMissing User.Read scopeAdd 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