octocode-mcp
Version:
Model Context Protocol (MCP) server for advanced GitHub repository analysis, code discovery, and npm package exploration. Provides AI assistants with powerful tools to search, analyze, and understand codebases across GitHub and npm ecosystems.
3 lines (2 loc) • 6.1 kB
JavaScript
import t from"crypto";import{C as e}from"./index-DnOJNPC2.js";import"node:process";import"events";import"child_process";import"http";import"https";import"url";import"path";import"fs";import"util";import"stream";import"assert";import"tty";import"os";import"zlib";class r{static instance;config=null;static getInstance(){return this.instance||(this.instance=new r),this.instance}initialize(t){const r=e.getConfig();if(!r.oauth?.enabled)throw new Error("OAuth not configured or disabled");this.config={clientId:t?.clientId||r.oauth.clientId,clientSecret:t?.clientSecret||r.oauth.clientSecret,redirectUri:t?.redirectUri||r.oauth.redirectUri,scopes:t?.scopes||r.oauth.scopes,authorizationUrl:t?.authorizationUrl||r.oauth.authorizationUrl||"https://github.com/login/oauth/authorize",tokenUrl:t?.tokenUrl||r.oauth.tokenUrl||"https://github.com/login/oauth/access_token",userAgent:t?.userAgent||`octocode-mcp/${r.version}`}}generatePKCEParams(){const e=this.generateRandomString(128);return{codeVerifier:e,codeChallenge:this.base64URLEncode(t.createHash("sha256").update(e).digest()),codeChallengeMethod:"S256"}}generateState(){return this.generateRandomString(32)}createAuthorizationUrl(t,e,r){if(!this.config)throw new Error("OAuth not initialized");const i=new URLSearchParams({client_id:this.config.clientId,redirect_uri:this.config.redirectUri,scope:this.config.scopes.join(" "),response_type:"code",state:t,code_challenge:e,code_challenge_method:"S256",...r});return`${this.config.authorizationUrl}?${i.toString()}`}startAuthorizationFlow(t){if(!this.config)throw new Error("OAuth not initialized");const{codeVerifier:e,codeChallenge:r}=this.generatePKCEParams(),i=this.generateState();return{authorizationUrl:this.createAuthorizationUrl(i,r,t),state:i,codeVerifier:e}}async exchangeCodeForToken(t,e,r){if(!this.config)throw new Error("OAuth not initialized");try{const i=await fetch(this.config.tokenUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json","User-Agent":this.config.userAgent},body:new URLSearchParams({client_id:this.config.clientId,client_secret:this.config.clientSecret,code:t,code_verifier:e,grant_type:"authorization_code",redirect_uri:this.config.redirectUri,...r&&{state:r}})});if(!i.ok){const t=await i.text();throw new Error(`Token exchange failed: ${i.status} ${i.statusText} - ${t}`)}const o=await i.json();if(o.error)throw new Error(`OAuth error: ${o.error_description||o.error}`);return{accessToken:o.access_token,refreshToken:o.refresh_token,tokenType:o.token_type||"Bearer",expiresIn:o.expires_in||3600,scope:o.scope||this.config.scopes.join(" ")}}catch(t){throw await this.logOAuthEvent("token_exchange","failure",{error:t instanceof Error?t.message:String(t)}),t}}async refreshToken(t){if(!this.config)throw new Error("OAuth not initialized");try{const e=await fetch(this.config.tokenUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json","User-Agent":this.config.userAgent},body:new URLSearchParams({client_id:this.config.clientId,client_secret:this.config.clientSecret,refresh_token:t,grant_type:"refresh_token"})});if(!e.ok){const t=await e.text();throw new Error(`Token refresh failed: ${e.status} ${e.statusText} - ${t}`)}const r=await e.json();if(r.error)throw new Error(`OAuth error: ${r.error_description||r.error}`);const i={accessToken:r.access_token,refreshToken:r.refresh_token||t,tokenType:r.token_type||"Bearer",expiresIn:r.expires_in||3600,scope:r.scope||this.config.scopes.join(" ")};return await this.logOAuthEvent("token_refresh","success",{expiresIn:i.expiresIn,scopes:i.scope.split(" ")}),i}catch(t){throw await this.logOAuthEvent("token_refresh","failure",{error:t instanceof Error?t.message:String(t)}),t}}async validateToken(t){if(!this.config)throw new Error("OAuth not initialized");try{const r=e.getConfig(),i=r.githubHost?`${r.githubHost}/api/v3`:"https://api.github.com",o=await fetch(`${i}/user`,{headers:{Authorization:`Bearer ${t}`,"User-Agent":this.config.userAgent}});if(!o.ok)return{valid:!1,scopes:[],error:`Token validation failed: ${o.status} ${o.statusText}`};const n=o.headers.get("x-oauth-scopes")?.split(", ")||[],s=o.headers.get("x-ratelimit-reset");return{valid:!0,scopes:n,expiresAt:s?new Date(1e3*parseInt(s)):void 0}}catch(t){return{valid:!1,scopes:[],error:t instanceof Error?t.message:String(t)}}}validateState(e,r){if(!e||!r)return!1;if(e.length!==r.length)return!1;try{return t.timingSafeEqual(Buffer.from(e),Buffer.from(r))}catch{return!1}}async revokeToken(t){if(!this.config)throw new Error("OAuth not initialized");try{const r=e.getConfig(),i=r.githubHost?`${r.githubHost}/login/oauth/revoke`:"https://github.com/login/oauth/revoke",o=await fetch(i,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json","User-Agent":this.config.userAgent},body:new URLSearchParams({client_id:this.config.clientId,client_secret:this.config.clientSecret,token:t})});if(!o.ok)throw new Error(`Token revocation failed: ${o.status} ${o.statusText}`);await this.logOAuthEvent("token_revocation","success",{})}catch(t){throw await this.logOAuthEvent("token_revocation","failure",{error:t instanceof Error?t.message:String(t)}),t}}getConfig(){return this.config?{clientId:this.config.clientId,redirectUri:this.config.redirectUri,scopes:this.config.scopes,authorizationUrl:this.config.authorizationUrl,tokenUrl:this.config.tokenUrl,userAgent:this.config.userAgent}:null}generateRandomString(e){let r="";const i=t.randomBytes(e);for(let t=0;t<e;t++){const e=i[t];void 0!==e&&(r+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~".charAt(e%66))}return r}base64URLEncode(t){return t.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async logOAuthEvent(t,r,i){try{const o=e.getConfig();if(o.enterprise?.auditLogging){const{AuditLogger:e}=await import("./security/auditLogger.js");e.logEvent({action:`oauth_${t}`,outcome:r,source:"auth",details:{...i,clientId:this.config?.clientId}})}}catch{}}}export{r as OAuthManager,r as default};