Asi oauth-integrations

install
source · Clone the upstream repo
git clone https://github.com/plurigrid/asi
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/plurigrid/asi "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/asi/skills/oauth-integrations" ~/.claude/skills/plurigrid-asi-oauth-integrations && rm -rf "$T"
manifest: plugins/asi/skills/oauth-integrations/SKILL.md
source content

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-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

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
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/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: null
User 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