@@ -6,7 +6,8 @@ import GitHubProvider from "next-auth/providers/github";
6
6
const configureIdentityProvider = ( ) => {
7
7
const providers : Array < Provider > = [ ] ;
8
8
9
- const adminEmails = process . env . ADMIN_EMAIL_ADDRESS ?. split ( "," ) . map ( email => email . toLowerCase ( ) . trim ( ) ) ;
9
+ const adminEmails = process . env . ADMIN_EMAIL_ADDRESS ?. split ( "," ) . map ( email => email . toLowerCase ( ) . trim ( ) ) . filter ( email => email ) ;
10
+ const azureAdAllowedPrincipals = process . env . AZURE_AD_ALLOWED_PRINCIPALS ?. split ( "," ) . map ( oid => oid . toLowerCase ( ) . trim ( ) ) . filter ( oid => oid ) ;
10
11
11
12
if ( process . env . AUTH_GITHUB_ID && process . env . AUTH_GITHUB_SECRET ) {
12
13
providers . push (
@@ -16,7 +17,8 @@ const configureIdentityProvider = () => {
16
17
async profile ( profile ) {
17
18
const newProfile = {
18
19
...profile ,
19
- isAdmin : adminEmails ?. includes ( profile . email . toLowerCase ( ) )
20
+ isAdmin : adminEmails ?. includes ( profile . email . toLowerCase ( ) ) ,
21
+ isAllowed : true
20
22
}
21
23
return newProfile ;
22
24
}
@@ -34,13 +36,56 @@ const configureIdentityProvider = () => {
34
36
clientId : process . env . AZURE_AD_CLIENT_ID ! ,
35
37
clientSecret : process . env . AZURE_AD_CLIENT_SECRET ! ,
36
38
tenantId : process . env . AZURE_AD_TENANT_ID ! ,
37
- async profile ( profile ) {
38
-
39
+ authorization : {
40
+ params : {
41
+ // Add User.Read to reach the /me endpoint of Microsoft Graph
42
+ scope : 'email openid profile User.Read'
43
+ }
44
+ } ,
45
+ async profile ( profile , tokens ) {
46
+ let isAllowed = true
47
+ if ( Array . isArray ( azureAdAllowedPrincipals ) && azureAdAllowedPrincipals . length > 0 ) {
48
+ try {
49
+ isAllowed = false
50
+ // POST https://graph.microsoft.com/v1.0/me/getMemberObjects
51
+ // It returns all IDs of principal objects which "me" is a member of (transitive)
52
+ // https://learn.microsoft.com/en-us/graph/api/directoryobject-getmemberobjects?view=graph-rest-1.0&tabs=http
53
+ var response = await fetch (
54
+ 'https://graph.microsoft.com/v1.0/me/getMemberObjects' ,
55
+ {
56
+ method : 'POST' ,
57
+ headers : {
58
+ Authorization : `Bearer ${ tokens . access_token } ` ,
59
+ 'Content-Type' : 'application/json'
60
+ } ,
61
+ body : '{"securityEnabledOnly":true}'
62
+ }
63
+ )
64
+ if ( response . ok ) {
65
+ var body = await response . json ( ) as { value ?: string [ ] }
66
+ var oids = body . value ?? [ ]
67
+ if ( profile . oid ) {
68
+ // Append the object ID of user principal "me"
69
+ oids . push ( profile . oid )
70
+ }
71
+ for ( const principal of azureAdAllowedPrincipals ) {
72
+ if ( oids . includes ( principal ) ) {
73
+ isAllowed = true
74
+ break
75
+ }
76
+ }
77
+ }
78
+ }
79
+ catch ( e ) {
80
+ console . log ( e )
81
+ }
82
+ }
39
83
const newProfile = {
40
84
...profile ,
41
85
// throws error without this - unsure of the root cause (https://stackoverflow.com/questions/76244244/profile-id-is-missing-in-google-oauth-profile-response-nextauth)
42
86
id : profile . sub ,
43
- isAdmin : adminEmails ?. includes ( profile . email . toLowerCase ( ) ) || adminEmails ?. includes ( profile . preferred_username . toLowerCase ( ) )
87
+ isAdmin : adminEmails ?. includes ( profile . email . toLowerCase ( ) ) || adminEmails ?. includes ( profile . preferred_username . toLowerCase ( ) ) ,
88
+ isAllowed
44
89
}
45
90
return newProfile ;
46
91
}
@@ -54,15 +99,18 @@ export const options: NextAuthOptions = {
54
99
secret : process . env . NEXTAUTH_SECRET ,
55
100
providers : [ ...configureIdentityProvider ( ) ] ,
56
101
callbacks : {
57
- async jwt ( { token, user, account, profile, isNewUser, session} ) {
102
+ async jwt ( { token, user, account, profile, isNewUser, session } ) {
58
103
if ( user ?. isAdmin ) {
59
- token . isAdmin = user . isAdmin
104
+ token . isAdmin = user . isAdmin
60
105
}
61
106
return token
62
107
} ,
63
- async session ( { session, token, user } ) {
108
+ async session ( { session, token, user } ) {
64
109
session . user . isAdmin = token . isAdmin as string
65
110
return session
111
+ } ,
112
+ async signIn ( { user } ) {
113
+ return user . isAllowed
66
114
}
67
115
} ,
68
116
session : {
0 commit comments