forked from TanStack/tanstack.com
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgithub.server.ts
More file actions
111 lines (95 loc) · 2.7 KB
/
github.server.ts
File metadata and controls
111 lines (95 loc) · 2.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/**
* GitHub Auth Utilities
*
* Helper functions for checking GitHub OAuth scopes and tokens.
*/
import { env } from '~/utils/env'
import {
buildGitHubAuthUrl,
generateOAuthState,
getOAuthAccountRepository,
} from './index.server'
const REPO_SCOPE = 'public_repo'
function hasRepoScopeInString(tokenScope: string | null): boolean {
if (!tokenScope) return false
const scopes = tokenScope.split(/[,\s]+/)
return scopes.includes(REPO_SCOPE) || scopes.includes('repo')
}
/**
* Check if a user has the public_repo scope for GitHub
*/
export async function hasGitHubRepoScope(userId: string): Promise<boolean> {
const repo = getOAuthAccountRepository()
const account = await repo.findByUserId(userId, 'github')
return hasRepoScopeInString(account?.tokenScope ?? null)
}
/**
* Get a user's GitHub access token
*/
export async function getGitHubToken(userId: string): Promise<string | null> {
const repo = getOAuthAccountRepository()
const account = await repo.findByUserId(userId, 'github')
return account?.accessToken ?? null
}
/**
* Get a user's GitHub username from their access token
*/
export async function getGitHubUsername(
accessToken: string,
): Promise<string | null> {
const response = await fetch('https://api.github.com/user', {
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: 'application/vnd.github.v3+json',
},
})
if (!response.ok) return null
const profile = await response.json()
return profile.login ?? null
}
export interface GitHubAuthState {
hasGitHubAccount: boolean
hasRepoScope: boolean
accessToken: string | null
}
/**
* Get complete GitHub auth state for a user
*/
export async function getGitHubAuthState(
userId: string,
): Promise<GitHubAuthState> {
const repo = getOAuthAccountRepository()
const account = await repo.findByUserId(userId, 'github')
if (!account) {
return {
hasGitHubAccount: false,
hasRepoScope: false,
accessToken: null,
}
}
return {
hasGitHubAccount: true,
hasRepoScope: hasRepoScopeInString(account.tokenScope),
accessToken: account.accessToken,
}
}
/**
* Build a GitHub re-auth URL with additional scopes
*/
export function buildGitHubReAuthUrl(
returnTo: string,
additionalScopes: Array<string>,
): string {
const clientId = env.GITHUB_OAUTH_CLIENT_ID
if (!clientId) {
throw new Error('GITHUB_OAUTH_CLIENT_ID is not configured')
}
const origin = env.SITE_URL
if (!origin) {
throw new Error('SITE_URL is not configured')
}
const redirectUri = `${origin}/api/auth/callback/github`
const state = generateOAuthState()
// Build auth URL with additional scopes
return buildGitHubAuthUrl(clientId, redirectUri, state, additionalScopes)
}