1- import { generateRandomString } from "@/lib/utils" ;
2- import { IGithubUser , IOAuthService } from "./oauth-contract" ;
3- import { cookies } from "next/headers" ;
4- import { env } from "@/env" ;
1+ import { env } from "@/env" ;
2+ import { generateRandomString } from "@/lib/utils" ;
3+ import { cookies } from "next/headers" ;
4+ import { ActionException , handleActionException } from "../RepositoryException" ;
5+ import {
6+ GithubUserEmailAPIResponse ,
7+ IGithubUser ,
8+ IOAuthService ,
9+ } from "./oauth-contract" ;
510
611export class GithubOAuthService implements IOAuthService < IGithubUser > {
712 async getAuthorizationUrl ( ) : Promise < string > {
813 const state = generateRandomString ( 50 ) ;
914 const params = new URLSearchParams ( {
1015 client_id : env . GITHUB_CLIENT_ID ,
1116 redirect_uri : env . GITHUB_CALLBACK_URL ,
12- scope : "user:email" ,
17+ scope : "read:user, user:email" ,
1318 state,
1419 } ) ;
1520 const _cookies = await cookies ( ) ;
@@ -23,25 +28,32 @@ export class GithubOAuthService implements IOAuthService<IGithubUser> {
2328 return `https://github.com/login/oauth/authorize?${ params . toString ( ) } ` ;
2429 }
2530
26- async getUserInfo ( code : string , state : string ) : Promise < IGithubUser > {
27- const _cookies = await cookies ( ) ;
28- const storedState = _cookies . get ( "github_oauth_state" ) ?. value ?? null ;
31+ async getUserInfo ( code : string , state : string ) {
32+ try {
33+ const _cookies = await cookies ( ) ;
34+ const storedState = _cookies . get ( "github_oauth_state" ) ?. value ?? null ;
2935
30- if ( code === null || state === null || storedState === null ) {
31- throw new Error ( "Please restart the process." ) ;
32- }
33- if ( state !== storedState ) {
34- throw new Error ( "Please restart the process." ) ;
35- }
36+ if ( code === null || state === null || storedState === null ) {
37+ throw new ActionException ( "Please restart the process." ) ;
38+ }
39+ if ( state !== storedState ) {
40+ throw new ActionException ( "Please restart the process." ) ;
41+ }
3642
37- const githubAccessToken = await validateGitHubCode (
38- code ,
39- env . GITHUB_CLIENT_ID ,
40- env . GITHUB_CLIENT_SECRET ,
41- env . GITHUB_CALLBACK_URL
42- ) ;
43+ const githubAccessToken = await validateGitHubCode (
44+ code ,
45+ env . GITHUB_CLIENT_ID ,
46+ env . GITHUB_CLIENT_SECRET ,
47+ env . GITHUB_CALLBACK_URL
48+ ) ;
4349
44- return await getGithubUser ( githubAccessToken . access_token ) ;
50+ return {
51+ success : true as const ,
52+ data : await getGithubUser ( githubAccessToken . access_token ) ,
53+ } ;
54+ } catch ( error ) {
55+ return handleActionException ( error ) ;
56+ }
4557 }
4658}
4759
@@ -82,14 +94,23 @@ export const validateGitHubCode = async (
8294} ;
8395
8496const getGithubUser = async ( accessToken : string ) : Promise < IGithubUser > => {
85- const githubAPI = await fetch ( "https://api.github.com/user" , {
86- headers : {
87- Authorization : `Bearer ${ accessToken } ` ,
88- } ,
97+ const userInfoAPI = await fetch ( "https://api.github.com/user" , {
98+ headers : { Authorization : `Bearer ${ accessToken } ` } ,
8999 } ) ;
90- if ( ! githubAPI . ok ) {
91- throw new Error ( "Failed to get GitHub user" ) ;
100+
101+ const userEmailAPI = await fetch ( "https://api.github.com/user/emails" , {
102+ headers : { Authorization : `Bearer ${ accessToken } ` } ,
103+ } ) ;
104+
105+ if ( ! userInfoAPI . ok || ! userEmailAPI . ok ) {
106+ throw new ActionException ( "Failed to get GitHub user" ) ;
92107 }
93108
94- return ( await githubAPI . json ( ) ) as IGithubUser ;
109+ const user = ( await userInfoAPI . json ( ) ) as IGithubUser ;
110+ const emails = ( await userEmailAPI . json ( ) ) as GithubUserEmailAPIResponse [ ] ;
111+
112+ const primaryEmail = emails . find ( ( e ) => e . primary ) ;
113+ user . email = primaryEmail ?. email ! ;
114+
115+ return user ;
95116} ;
0 commit comments