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에서 보기