Skip to main content

oauth-integration

OAuth 2.0 integration patterns. Social login, third-party APIs, token storage, refresh, PKCE.

소스 정보

저장소
skeletorflet/opencode-kit
최근 소스 활동
2026년 4월 3일 15:32
감지된 SKILL.md 언어
영어
스타
9
포크
1

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
oauth-integration
description
OAuth 2.0 integration patterns. Social login, third-party APIs, token storage, refresh, PKCE.
# OAuth Integration > Don\'t reinvent auth. Let the provider handle it. --- ## 1. OAuth 2.0 Flow (Authorization Code + PKCE) ``` 1. Your app generates: code_verifier = random 64-char string code_challenge = BASE64URL(SHA256(code_verifier)) 2. Redirect user to provider: GET https://provider.com/oauth/authorize? response_type=code &client_id=YOUR_CLIENT_ID &redirect_uri=https://yourapp.com/callback &scope=read:user email &state=random_state_value &code_challenge=xyz &code_challenge_method=S256 3. Provider redirects to: https://yourapp.com/callback?code=AUTH_CODE&state=same_state 4. Your server exchanges code: POST https://provider.com/oauth/token { code, code_verifier, client_id, client_secret, redirect_uri } → { access_token, refresh_token, expires_in } ``` --- ## 2. Auth.js (NextAuth) Provider Setup ```ts // app/api/auth/[...nextauth]/route.ts import NextAuth from "next-auth"; import GitHub from "next-auth/providers/github"; import Google from "next-auth/providers/google"; export const { handlers, signIn, signOut, auth } = NextAuth({ providers: [ GitHub({ clientId: process.env.GITHUB_CLIENT_ID!, clientSecret: process.env.GITHUB_CLIENT_SECRET!, authorization: { params: { scope: "read:user user:email repo" } }, }), Google({ clientId: process.env.GOOGLE_CLIENT_ID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET!, }), ], callbacks: { async jwt({ token, account }) { // Store provider access token if (account) { token.providerAccessToken = account.access_token; token.providerRefreshToken = account.refresh_token; token.providerTokenExpiry = account.expires_at; } return token; }, async session({ session, token }) { session.providerAccessToken = token.providerAccessToken; return session; }, }, }); ``` --- ## 3. Third-Party API Integration (Accessing User\'s Resources) ```ts // Using provider access token to call their API async function getGitHubRepos(accessToken: string) { const res = await fetch("https://api.github.com/user/repos", { headers: { Authorization: `Bearer ${accessToken}`, Accept: "application/vnd.github.v3+json", }, }); return res.json(); } // In Next.js server action export async function fetchUserRepos() { const session = await auth(); if (!session?.providerAccessToken) throw new Error("Not authenticated"); return getGitHubRepos(session.providerAccessToken); } ``` --- ## 4. Token Storage ``` Where to store tokens: ├── Server-side session (HttpOnly cookie) ← BEST for web ├── Database (encrypted) ← for long-lived integrations ├── In-memory only ← acceptable for short sessions └── localStorage ← AVOID (XSS vulnerable) Database storage for long-lived integrations: model OAuthToken { id String @id userId String provider String // github, google, slack accessToken String // encrypted refreshToken String? // encrypted expiresAt DateTime? scope String } ``` --- ## 5. Token Refresh ```ts async function getValidToken(userId: string, provider: string): Promise<string> { const stored = await db.oauthToken.findUnique({ where: { userId_provider: { userId, provider } } }); if (!stored) throw new Error("Not connected"); // Check if expired (with 5min buffer) if (stored.expiresAt && stored.expiresAt < new Date(Date.now() + 5 * 60_000)) { const refreshed = await refreshToken(provider, decrypt(stored.refreshToken!)); await db.oauthToken.update({ where: { id: stored.id }, data: { accessToken: encrypt(refreshed.access_token), expiresAt: new Date(Date.now() + refreshed.expires_in * 1000), }, }); return refreshed.access_token; } return decrypt(stored.accessToken); } ``` --- ## 6. Common Provider Scopes | Provider | Scope | Access | |----------|-------|--------| | GitHub | `read:user email` | Profile + email | | GitHub | `repo` | Repositories | | Google | `profile email` | Basic profile | | Google | `https://www.googleapis.com/auth/gmail.readonly` | Read email | | Slack | `chat:write` | Post messages | | Slack | `channels:read` | List channels | --- ## 7. Disconnect / Revoke ```ts // Revoke provider token async function disconnectIntegration(userId: string, provider: string) { const token = await getOAuthToken(userId, provider); // Revoke with provider await fetch(`https://api.provider.com/oauth/revoke`, { method: "POST", body: new URLSearchParams({ token: token.accessToken }), }); // Remove from DB await db.oauthToken.delete({ where: { userId_provider: { userId, provider } } }); } ```
GitHub에서 보기