Skills Plugins MCP Prompt Model 博客 我的中心
开发编程 #github #web

github-oauth-nango-integration

Use when implementing GitHub OAuth + GitHub App authentication with Nango - provides two-connection pattern for user login and repo access with webhook handling

DeepseekModel 官方收录技能 质量 优秀 · 90 v1.0.0

获取

https://deepseekmodel.com/api/download.php?id=agentworkforce-relay-claude-skills-github-oauth-nango-integration-skill-md&format=skill
下载 .skill 标准格式,含 system_prompt 与 model_config,导入任意 Agent 框架即可使用
.skill 文件中 system_prompt 字段的实际内容。
name github-oauth-nango-integration description Use when implementing GitHub OAuth + GitHub App authentication with Nango - provides two-connection pattern for user login and repo access with webhook handling GitHub OAuth + Nango Integration Overview Implements dual-connection OAuth pattern: one for user identity ( github integration), another for repository access ( github-app-oauth integration). This separation enables secure login while maintaining granular repo permissions through GitHub App installations. When to Use Setting up GitHub OAuth login via Nango Implementing GitHub App installation webhooks Reconciling OAuth users with GitHub App installations Building apps that need both user auth and repo access Handling Nango sync webhooks for GitHub data Why Two Connections? GitHub has two different authentication mechanisms that serve different purposes: GitHub OAuth App ( github integration) What it is : Traditional OAuth for user identity What it gives you : User profile (name, email, avatar, GitHub ID) What it DOESN'T give you : Access to repositories Use for : Login, "Sign in with GitHub" GitHub App ( github-app-oauth integration) What it is : Installable app with granular repo permissions What it gives you : Access to specific repos the user installed it on What it DOESN'T give you : User identity (it knows the installation, not who's using it) Use for : Reading PRs, commits, files; posting comments; webhooks The Reconciliation Problem OAuth App alone: "User john@example.com logged in" → but which repos can they access? GitHub App alone: "Installation #12345 has access to repo X" → but who is the user? Solution : Two separate OAuth flows linked by user ID: Login flow → User authenticates → Store user identity + nangoConnectionId Repo flow → Same user authorizes app → Store repos + link via ownerId This lets you answer: "User john@example.com can access repos X, Y, Z" Quick Reference Connection Type Nango Integration Purpose Stored In User Login github Authentication, identity users.nangoConnectionId Repo Access github-app-oauth PR operations, file access repos.nangoConnectionId Flow Endpoint Webhook Type Login GET /auth/nango-session auth + github Repo Connect GET /auth/github-app-session auth + github-app-oauth Data Sync N/A (scheduled) sync Implementation 1. Database Schema // users table - stores login connection export const users = pgTable ( 'users' , { id : uuid ( 'id' ). primaryKey (). defaultRandom (), githubId : text ( 'github_id' ). unique (). notNull (), githubUsername : text ( 'github_username' ). notNull (), email : text ( 'email' ), avatarUrl : text ( 'avatar_url' ), nangoConnectionId : text ( 'nango_connection_id' ), // Permanent login connection incomingConnectionId : text ( 'incoming_connection_id' ), // Temp polling connection pendingInstallationRequest : timestamp ( 'pending_installation_request' ), // Org approval wait }); // repos table - stores per-repo app connection export const repos = pgTable ( 'repos' , { id : uuid ( 'id' ). primaryKey (). defaultRandom (), githubRepoId : text ( 'github_repo_id' ). unique (). notNull (), fullName : text ( 'full_name' ). notNull (), installationId : uuid ( 'installation_id' ). references ( () => githubInstallations. id ), ownerId : uuid ( 'owner_id' ). references ( () => users. id ), nangoConnectionId : text ( 'nango_connection_id' ), // App connection for this repo }); // github_installations - tracks app installations export const githubInstallations = pgTable ( 'github_installations' , { id : uuid ( 'id' ). primaryKey (). defaultRandom (), installationId : text ( 'installation_id' ). unique (). notNull (), accountType : text ( 'account_type' ), // 'user' | 'organization' accountLogin : text ( 'account_login' ), installedById : uuid ( 'installed_by_id' ). references ( () => users. id ), }); 2. Constants // constants.ts export const NANGO_INTEGRATION = { GITHUB_USER : 'github' , // Login only GITHUB_APP_OAUTH : 'github-app-oauth' , // Repo access } as const ; 3. Login Flow Routes // GET /auth/nango-session - Create login OAuth session app. get ( '/auth/nango-session' , async (c) => { const tempUserId = randomUUID (); const { sessionToken } = await nangoClient. createConnectSession ({ end_user : { id : tempUserId }, allowed_integrations : [ NANGO_INTEGRATION . GITHUB_USER ], }); return c. json ({ sessionToken, tempUserId }); }); // GET /auth/nango/status/:connectionId - Poll login completion app. get ( '/auth/nango/status/:connectionId' , async (c) => { const { connectionId } = c. req . param (); // Check if user exists with this incoming connection const user = await userRepo. findByIncomingConnectionId (connectionId); if (!user) { return c. json ({ ready : false }); } // Issue JWT and return const token = authService. issueToken (user); await userRepo. clearIncomingConnectionId (user. id ); return c. json ({ ready : true , token, user }); }); 4. App OAuth Flow Routes // GET /auth/github-app-session - Create app OAuth session (authenticated) app. get ( '/auth/github-app-session' , authMiddleware, async (c) => { const user = c. get ( 'user' ); const { sessionToken } = await nangoClient. createConnectSession ({ end_user : { id : user. id , email : user. email }, allowed_integrations : [ NANGO_INTEGRATION . GITHUB_APP_OAUTH ], }); return c. json ({ sessionToken }); }); // GET /auth/github-app/status/:connectionId - Poll repo sync app. get ( '/auth/github-app/status/:connectionId' , authMiddleware, async (c) => { const user = c. get ( 'user' ); // Check for pending org approval if (user. pendingInstallationRequest ) { return c. json ({ ready : false , pendingApproval : true }); } // Check if repos synced const repos = await repoRepo. findByOwnerId (user. id ); return c. json ({ ready : repos. length > 0 , repos }); }); 5. Auth Webhook Handler // auth-webhook-service.ts export async function handleAuthWebhook ( payload : NangoAuthWebhook ): Promise < boolean > { const { connectionId, providerConfigKey, endUser } = payload; if (providerConfigKey === NANGO_INTEGRATION . GITHUB_USER ) { return handleLoginWebhook (connectionId, endUser); } if (providerConfigKey === NANGO_INTEGRATION . GITHUB_APP_OAUTH ) { return handleAppOAuthWebhook (connectionId, endUser); } return false ; } async function handleLoginWebhook ( connectionId : string , endUser ?: EndUser ) { // Fetch GitHub user info via Nango const githubUser = await nangoService. getGitHubUser (connectionId); // Check if user exists const existingUser = await userRepo. findByGitHubId ( String (githubUser. id )); if (existingUser) { // Returning user - store temp connection for polling await userRepo. update (existingUser. id , { incomingConnectionId : connectionId, }); // Delete duplicate connection later await nangoService. deleteConnection (connectionId); } else { // New user - create record const user = await userRepo. create ({ githubId : String (githubUser. id ), githubUsername : githubUser. login , email : githubUser. email , avatarUrl : githubUser. avatar_url , nangoConnectionId : connectionId, incomingConnectionId : connectionId, }); // Update connection with real user ID await nangoService. patchConnection (connectionId, { end_user : { id : user. id , email : user. email }, }); } return true ; } async function handleAppOAuthWebhook ( connectionId : string , endUser ?: EndUser ) { const userId = endUser?. id ; if (!userId) throw new Error ( 'No user ID in app OAuth webhook' ); const user = await userRepo. findById (userId); if (!user) throw new Error ( 'User not found' ); try { // Fetch repos user has access to const repos = await githubService. getInstallationReposRaw (connectionId); // Sync repos to database for ( const repo of repos) { await repoRepo. upsert ({ githubRepoId : String (repo. id ), fullName : repo. full_name , ownerId : user. id , nangoConnectionId : connectionId, }); } // Trigger Nango syncs await nangoService. triggerSync (connectionId, [ 'pull-requests' , 'commits' ]); } catch (error) { if (error. status === 403 ) { // Org approval pending await userRepo. update (user. id , { pendingInstallationRequest : new Date (), }); return true ; // Graceful degradation } throw error; } return true ; } 6. Webhook Route with Signature Verification // webhooks.ts app. post ( '/api/webhooks/nango' , async (c) => { const signature = c. req . header ( 'X-Nango-Signature' ); const body = await c. req . text (); // Verify signature const expectedSignature = createHmac ( 'sha256' , NANGO_SECRET_KEY ). update (body). digest ( 'hex' ); if (signature !== expectedSignature) { return c. json ({ error : 'Invalid signature' }, 401 ); } const payload = JSON . parse (body); if (payload. type === 'auth' ) { const success = await handleAuthWebhook (payload); return c. json ({ success }); } if (payload. type === 'sync' ) { await processSyncWebhook (payload); return c. json ({ success : true }); } return c. json ({ success : false }); }); 7. Frontend Integration // Login flow async function handleLogin ( ) { const res = await fetch ( '/api/auth/nango-session' ); const { sessionToken } = await res. json (); const nango = new Nango ({ connectSessionToken : sessionToken }); nango. openConnectUI ({ onEvent : async (event) => { if (event. type === 'connect' ) { // Poll for completion const result = await pollForAuth (event. payload . connectionId ); if (result. ready ) { localStorage . setItem ( 'token' , result. token ); navigate ( '/dashboard' ); } } }, }); } // Repo connection flow (after login) async function handleConnectRepos ( ) { const res = await fetch ( '/api/auth/github-app-session' , { headers : { Authorization : `Bearer ${token} ` }, }); const { sessionToken } = await res. json (); const nango = new Nango ({ connectSessionToken : sessionToken }); nango. openConnectUI ({ onEvent : async (event) => { if (event. type === 'connect' ) { const result = await pollForRepos (event. payload . connectionId ); if (result. pendingApproval ) { showMessage ( 'Waiting for org admin approval...' ); } else if (result. ready ) { setRepos (result. repos ); } } }, }); } Complete Flow Diagram USER LOGIN: Frontend → GET /auth/nango-session → Nango.openConnectUI(sessionToken) → User authorizes GitHub → Nango webhook (type: auth, providerConfigKey: github) → Backend creates/updates user → Frontend polls /auth/nango/status/:connectionId → Returns JWT token REPO CONNECTION (authenticated): Frontend → GET /auth/github-app-session (with JWT) → Nango.openConnectUI(sessionToken) → User authorizes GitHub App → Nango webhook (type: auth, providerConfigKey: github-app-oauth) → Backend fetches repos, syncs to DB → Frontend polls /auth/github-app/status/:connectionId → Returns repos list DATA SYNCS (background): Nango → Scheduled sync every 4 hours
Agent 识别该技能的关键词,点击任意一个即可复制。

该技能未提供触发词。

下载的 .skill 包内含以下字段。
字段 说明
format格式标识(skill/v1)
skill_id技能唯一 ID
name技能名称
version版本号
description技能描述
category所属分类(数组)
trigger_words触发词列表
tags标签列表
source来源标识
source_url来源链接(本页地址)
exported_at导出时间(每次下载生成)
system_prompt系统提示词正文
model_config模型参数:provider / model / temperature / max_tokens / top_p
examples示例
install_guide各平台导入说明(Coze / Dify / Claude / 自定义框架)
同一份技能可按不同平台格式导出。
.skill 标准格式,含 system_prompt 与 model_config,导入任意 Agent 框架即可使用 下载
.skillpro 增强格式,额外含脚本 / 工具 / 依赖 / 钩子占位 下载
.json 纯 JSON 导出,只含 system_prompt 与模型参数 下载
Coze 带 frontmatter 的 Markdown,Coze 平台导入用 下载
Dify Dify DSL,创建应用后直接导入 下载

每日精选 Skill 推荐,免费送到你邮箱

输入邮箱,每天接收一个精选 AI Agent 技能推荐。完全免费,持续更新。

提交后我们会发送一封确认邮件,点击邮件里的链接才会开始收信。

完全免费,取消任意时间。我们不会发送垃圾邮件。