UNPKG

@tezx/github-oauth2

Version:

GitHub OAuth2 middleware integration for the TezX server framework — supports GitHub sign-in, token verification, and session handling.

109 lines (108 loc) 4.23 kB
import { generateUUID } from "tezx/helper"; export function getGithubOAuthURL({ authClient, scopes = ["read:user", "user:email"], state, allowSignup = true, }) { return (ctx, next) => { const generatedState = state || `req-${generateUUID()}`; ctx.setHeader("state", generatedState); const scopeParam = encodeURIComponent(scopes.join(" ")); const signupParam = allowSignup ? "true" : "false"; const url = `https://github.com/login/oauth/authorize` + `?client_id=${authClient.clientId}` + `&redirect_uri=${encodeURIComponent(authClient.redirectUri)}` + `&scope=${scopeParam}` + `&state=${generatedState}` + `&allow_signup=${signupParam}`; ctx.github = { ...ctx.github, oauth_url: url }; if (next) { return next(); } return ctx.redirect(url); }; } export function GitHubOauthClient(config) { return config; } export function verifyGithubToken({ authClient, onError, onSuccess, Callbacks, }) { return async (ctx, next) => { try { const q = ctx.req.query; if (q?.error) { ctx.setStatus = 500; if (onError) return onError(q.error, ctx); throw new Error(q.error); } if (!q.code) { ctx.setStatus = 400; const msg = "Missing authorization code"; if (onError) return onError(msg, ctx); throw new Error(msg); } const tokenRes = await fetch("https://github.com/login/oauth/access_token", { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json", }, body: JSON.stringify({ client_id: authClient.clientId, client_secret: authClient.clientSecret, code: q.code, redirect_uri: authClient.redirectUri, }), }); const tokens = await tokenRes.json(); if (!tokens.access_token) { throw new Error("Failed to get access token"); } const userRes = await fetch("https://api.github.com/user", { headers: { Authorization: `Bearer ${tokens.access_token}`, "User-Agent": "custom-oauth-client", }, }); const user = await userRes.json(); if (!user.email) { const emailRes = await fetch("https://api.github.com/user/emails", { headers: { Authorization: `Bearer ${tokens.access_token}`, "User-Agent": "custom-oauth-client", }, }); const emails = await emailRes.json(); const primaryEmail = emails.find((e) => e.primary && e.verified); user.email = primaryEmail?.email; } ctx.github = { ...ctx.github, user: user }; const callback = Callbacks?.(ctx); if (callback?.signIn) { const allowed = await callback.signIn(user); if (!allowed) { ctx.setStatus = 403; if (onError) return onError("Sign-in rejected", ctx); throw new Error("Sign-in rejected"); } } let finalToken = tokens; if (callback?.jwt) { finalToken = await callback.jwt(tokens, user); } if (callback?.session) { const session = await callback.session({ token: finalToken }, user); ctx.session = session; } await onSuccess?.(tokens); return await next(); } catch (error) { ctx.setStatus = 500; if (onError) return onError(error.message, ctx); throw new Error(error.message); } }; }