-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgithub.ts
More file actions
160 lines (137 loc) · 5.55 KB
/
Copy pathgithub.ts
File metadata and controls
160 lines (137 loc) · 5.55 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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import {onCall, HttpsError} from "firebase-functions/v2/https";
import * as admin from "firebase-admin";
import axios from "axios";
// GitHub OAuth 인증 및 커스텀 토큰 발급 함수
export const requestGithubTokens = onCall({
cors: true,
maxInstances: 3,
region: "asia-northeast3",
}, async (request) => {
try {
const { code } = request.data;
if (!code) {
throw new HttpsError('invalid-argument', '인증 코드가 필요합니다.');
}
// GitHub Client ID와 Secret 가져오기
const clientId = process.env.GITHUB_CLIENT_ID;
const clientSecret = process.env.GITHUB_CLIENT_SECRET;
if (!clientId || !clientSecret) {
throw new HttpsError('internal', 'GitHub 환경 설정이 누락되었습니다.');
}
// GitHub OAuth 응답 타입 정의
interface GitHubOAuthResponse {
access_token: string;
token_type: string;
scope: string;
error?: string;
}
// 1. GitHub OAuth 토큰 획득
const tokenResponse = await axios.post<GitHubOAuthResponse>
('https://github.com/login/oauth/access_token', {
client_id: clientId,
client_secret: clientSecret,
code: code
}, {
headers: { 'Accept': 'application/json' }
});
const tokenData = tokenResponse.data;
if (tokenData.error) {
throw new HttpsError('invalid-argument', `GitHub OAuth 오류: ${tokenData.error}`);
}
const accessToken = tokenData.access_token;
// GitHub 사용자 정보 응답 타입 정의
interface GitHubUser {
id: number;
login: string;
name?: string;
email?: string;
avatar_url?: string;
}
// 2. GitHub 사용자 정보 가져오기
const userResponse = await axios.get<GitHubUser>('https://api.github.com/user', {
headers: {
'Authorization': `token ${accessToken}`
}
});
const userData = userResponse.data;
if (!userData.id || !userData.email) {
throw new HttpsError('internal', 'GitHub 사용자 데이터를 가져오지 못했습니다.');
}
// 3. Firebase에서 GitHub 제공자로 사용자를 찾거나 생성
let uid;
try {
const userRecord = await admin.auth().getUserByEmail(userData.email);
uid = userRecord.uid; // 기존 UID 사용
console.log(`이메일(${userData.email})로 기존 사용자를 찾았습니다.`);
} catch (error) {
// 사용자가 없으면 Firebase에 새 사용자 생성
const userRecord = await admin.auth().createUser({
displayName: userData.name || userData.login,
email: userData.email,
photoURL: userData.avatar_url,
});
uid = userRecord.uid; // 새로 생성된 UID 사용
console.log(`이메일 있는 새 사용자가 생성됨: ${uid}`);
}
// 4. Firebase Custom Token 생성
const customToken = await admin.auth().createCustomToken(uid);
console.log(`GitHub 사용자(${userData.login})에 대한 커스텀 토큰이 생성되었습니다. UID: ${uid}`);
return {
accessToken,
customToken
};
} catch (error) {
console.error('GitHub 커스텀 토큰 생성 오류:', error);
throw new HttpsError('internal', error instanceof Error ? error.message : '알 수 없는 오류가 발생했습니다.');
}
});
export const revokeGithubAccessToken = onCall({
cors: true,
maxInstances: 3,
region: "asia-northeast3",
}, async (request) => {
try {
const uid = request.auth?.uid;
if (!uid) {
throw new HttpsError("unauthenticated", "인증된 사용자가 아닙니다.");
}
const clientId = process.env.GITHUB_CLIENT_ID;
const clientSecret = process.env.GITHUB_CLIENT_SECRET;
if (!clientId || !clientSecret) {
throw new HttpsError("internal", "GitHub 클라이언트 설정이 누락되었습니다.");
}
// 클라이언트에서 accessToken을 받을 수도 있고, 없으면 Firestore에서 조회
let accessToken = request.data?.accessToken;
if (!accessToken) {
const tokenDoc = await admin.firestore().collection("users").doc(uid).collection("userData").doc("tokens").get();
accessToken = tokenDoc.exists ? tokenDoc.data()?.githubAccessToken : null;
}
if (!accessToken) {
throw new HttpsError("not-found", "GitHub 토큰이 존재하지 않습니다.");
}
const url = `https://api.github.com/applications/${clientId}/token`;
const response = await axios.request({
method: "delete",
url,
auth: {
username: clientId,
password: clientSecret,
},
data: {
access_token: accessToken,
},
headers: {
Accept: "application/vnd.github+json",
},
});
if (response.status === 204) {
return { success: true };
} else {
throw new HttpsError("internal", "토큰 폐기에 실패했습니다.");
}
} catch (error: any) {
console.error("GitHub 토큰 폐기 오류:", error);
throw new HttpsError("internal", error?.message || "알 수 없는 오류가 발생했습니다.");
}
}
);