browser-testing-with-s…
Use when testing web applications with visual verification - automates Chrome browser interactions, element selection, and screenshot capture for confirming UI…
Use when implementing GitHub OAuth + GitHub App authentication with Nango - provides two-connection pattern for user login and repo access with webhook handling
$ npx -y skills add AgentWorkforce/relay --skill github-oauth-nango-integration --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/github-oauth-nango-integrationContext preview
The summary Claude sees to decide when to auto-load this skill.
Use when implementing GitHub OAuth + GitHub App authentication with Nango - provides two-connection pattern for user login and repo access with webhook handling
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
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.
GitHub has **two different authentication mechanisms** that serve different purposes:
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:
1. **Login flow** → User authenticates → Store user identity + `nangoConnectionId` 2. **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"
| 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` |
// 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),
});// constants.ts
export const NANGO_INTEGRATION = {
GITHUB_USER: 'github', // Login only
GITHUB_APP_OAUTH: 'github-app-oauth', // Repo access
} as const;// 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 });
});// 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/sLet Claude Code message Codex. Let your Hyperagent talk to your Hermes agent. Give your custom agents a way to message each other.
Repo: AgentWorkforce/relay
Use when testing web applications with visual verification - automates Chrome browser interactions, element selection, and screenshot capture for confirming UI…
Use when coordinating multiple AI agents with Agent Relay's workflow engine and need to pick the right orchestration pattern - covers the 10 core patterns…
Use when creating or improving Claude Code agents. Expert guidance on agent file structure, frontmatter, persona definition, tool access, model selection, and…
Use when creating or publishing Claude Code hooks - covers executable format, event types, JSON I/O, exit codes, security requirements, and PRPM package…
Use when creating or fixing .claude/rules/ files - provides correct paths frontmatter (not globs), glob patterns, and avoids Cursor-specific fields like…
Use when creating new Claude Code skills or improving existing ones - ensures skills are discoverable, scannable, and effective through proper structure, CSO…