@@ -191,7 +191,7 @@ export class Auth {
191191 return ctx . redirect ( "/login" )
192192 }
193193
194- if ( ! user . emailVerified && user . email ) {
194+ if ( env . UNDB_VERIFY_EMAIL && ! user . emailVerified && user . email ) {
195195 return ctx . redirect ( `/verify-email` , 301 )
196196 }
197197
@@ -271,17 +271,19 @@ export class Auth {
271271 await this . spaceMemberService . createMember ( userId , space . id . value , "owner" )
272272 ctx . cookie [ SPACE_ID_COOKIE_NAME ] . set ( { value : space . id . value } )
273273
274- const verificationCode = await this . #generateEmailVerificationCode( userId , email )
275- await this . mailService . send ( {
276- template : "verify-email" ,
277- data : {
278- username : username ! ,
279- code : verificationCode ,
280- action_url : new URL ( `/api/email-verification` , env . UNDB_BASE_URL ) . toString ( ) ,
281- } ,
282- subject : "Verify your email - undb" ,
283- to : email ,
284- } )
274+ if ( env . UNDB_VERIFY_EMAIL ) {
275+ const verificationCode = await this . #generateEmailVerificationCode( userId , email )
276+ await this . mailService . send ( {
277+ template : "verify-email" ,
278+ data : {
279+ username : username ! ,
280+ code : verificationCode ,
281+ action_url : new URL ( `/api/email-verification` , env . UNDB_BASE_URL ) . toString ( ) ,
282+ } ,
283+ subject : "Verify your email - undb" ,
284+ to : email ,
285+ } )
286+ }
285287 } )
286288 }
287289
@@ -423,7 +425,7 @@ export class Auth {
423425 )
424426 . get ( "/login/github" , async ( ctx ) => {
425427 const state = generateState ( )
426- const url = await github . createAuthorizationURL ( state )
428+ const url = await github . createAuthorizationURL ( state , { scopes : [ "user:email" ] } )
427429 return new Response ( null , {
428430 status : 302 ,
429431 headers : {
@@ -483,6 +485,56 @@ export class Auth {
483485 } )
484486 }
485487
488+ const emailsResponse = await fetch ( "https://api.github.com/user/emails" , {
489+ headers : {
490+ Authorization : `Bearer ${ tokens . accessToken } ` ,
491+ } ,
492+ } )
493+ const emails : GithubEmail [ ] = await emailsResponse . json ( )
494+
495+ const primaryEmail = emails . find ( ( email ) => email . primary ) ?? null
496+ if ( ! primaryEmail ) {
497+ return new Response ( "No primary email address" , {
498+ status : 400 ,
499+ } )
500+ }
501+ if ( ! primaryEmail . verified ) {
502+ return new Response ( "Unverified email" , {
503+ status : 400 ,
504+ } )
505+ }
506+
507+ const existingGithubUser = await this . queryBuilder
508+ . selectFrom ( "undb_user" )
509+ . selectAll ( )
510+ . where ( "undb_user.email" , "=" , primaryEmail . email )
511+ . executeTakeFirst ( )
512+
513+ if ( existingGithubUser ) {
514+ const spaceId = ctx . cookie [ SPACE_ID_COOKIE_NAME ] . value
515+ if ( ! spaceId ) {
516+ await this . spaceService . setSpaceContext ( setContextValue , { userId : existingGithubUser . id } )
517+ }
518+
519+ await this . queryBuilder
520+ . insertInto ( "undb_oauth_account" )
521+ . values ( {
522+ provider_id : "github" ,
523+ provider_user_id : githubUserResult . id . toString ( ) ,
524+ user_id : existingGithubUser . id ,
525+ } )
526+ . execute ( )
527+
528+ const session = await lucia . createSession ( existingGithubUser . id , { } )
529+ const sessionCookie = lucia . createSessionCookie ( session . id )
530+ return new Response ( null , {
531+ status : 302 ,
532+ headers : {
533+ Location : "/" ,
534+ "Set-Cookie" : sessionCookie . serialize ( ) ,
535+ } ,
536+ } )
537+ }
486538 const userId = generateIdFromEntropySize ( 10 ) // 16 characters long
487539 await withTransaction ( this . queryBuilder ) ( async ( ) => {
488540 const tx = getCurrentTransaction ( )
@@ -561,3 +613,9 @@ interface GitHubUserResult {
561613 id : number
562614 login : string // username
563615}
616+
617+ interface GithubEmail {
618+ email : string
619+ primary : boolean
620+ verified : boolean
621+ }
0 commit comments