import { OAuthProvider, OAuthTokens, OAuthProfile, AuthorizationUrlParams, TokenExchangeParams, ProviderConfig } from "./OAuthProvider"; import { OAuthProviderBase } from "./OAuthProviderBase"; /** * GitHub OAuth 2.0 provider implementation * * Implements OAuth flow for GitHub: * - Authorization URL: https://github.com/login/oauth/authorize * - Token exchange: POST https://github.com/login/oauth/access_token * - User profile: GET https://api.github.com/user * * Default scopes: ['user:email'] */ export class GitHubProvider extends OAuthProviderBase implements OAuthProvider { public readonly name = "github"; private static readonly AUTHORIZATION_URL = "https://github.com/login/oauth/authorize"; private static readonly TOKEN_URL = "https://github.com/login/oauth/access_token"; private static readonly USER_PROFILE_URL = "https://api.github.com/user"; private static readonly DEFAULT_SCOPES = ["user:email"]; constructor(config: ProviderConfig) { super(config); } /** * Generate GitHub authorization URL */ getAuthorizationUrl(params: AuthorizationUrlParams): string { const scopes = params.scope.length > 0 ? params.scope : GitHubProvider.DEFAULT_SCOPES; const queryParams = { client_id: this.config.clientId, redirect_uri: params.redirectUri, state: params.state, scope: scopes.join(" "), }; return `${GitHubProvider.AUTHORIZATION_URL}?${this.buildQueryString(queryParams)}`; } /** * Exchange authorization code for access token */ async exchangeCodeForToken(params: TokenExchangeParams): Promise { const body = this.buildQueryString({ client_id: this.config.clientId, client_secret: this.config.clientSecret, code: params.code, redirect_uri: params.redirectUri, }); const response = await this.makeHttpRequest(GitHubProvider.TOKEN_URL, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json", }, body, }); return { accessToken: response.access_token, refreshToken: response.refresh_token, scope: response.scope, expiresIn: response.expires_in, }; } /** * Fetch user profile from GitHub */ async getUserProfile(accessToken: string): Promise { const response = await this.makeHttpRequest(GitHubProvider.USER_PROFILE_URL, { method: "GET", headers: { Authorization: `Bearer ${accessToken}`, Accept: "application/json", }, }); return { id: String(response.id), email: response.email, name: response.name, avatarUrl: response.avatar_url, raw: response, }; } }