@@ -22,13 +22,13 @@ function createAuthError(message: string, statusCode: number): AuthError {
2222export type { AuthError } ;
2323
2424function getValidRole ( permissions ?: string [ ] ) : ValidRole {
25- if ( ! permissions || permissions . length === 0 ) return 'user' ;
26- for ( const permission of permissions ) {
27- if ( VALID_ROLES [ permission ] ) {
28- return VALID_ROLES [ permission ] ;
25+ if ( ! permissions || permissions . length === 0 ) return 'user' ;
26+ for ( const permission of permissions ) {
27+ if ( VALID_ROLES [ permission ] ) {
28+ return VALID_ROLES [ permission ] ;
29+ }
2930 }
30- }
31- return 'user' ;
31+ return 'user' ;
3232}
3333
3434interface User {
@@ -91,43 +91,43 @@ export async function authenticateRequest({ req, payload }: AuthenticateRequestO
9191
9292 if ( ! payload ) throw createAuthError ( "Payload instance is required for Keycloak user normalisation" , 500 ) ;
9393
94- const payloadUser = ( await payload . find ( {
95- collection : 'users' ,
96- depth : 1 ,
97- limit : 1 ,
98- draft : false ,
99- overrideAccess : true ,
100- where : { email : { equals : session . extra . email } }
94+ const payloadUser = ( await payload . find ( {
95+ collection : 'users' ,
96+ depth : 1 ,
97+ limit : 1 ,
98+ draft : false ,
99+ overrideAccess : true ,
100+ where : { email : { equals : session . extra . email } }
101101 } ) ) . docs [ 0 ] ;
102102
103103 const role = getValidRole ( permissions ) ;
104104
105105 if ( ! payloadUser ) {
106- const newUser = await payload . create ( {
107- collection : 'users' ,
108- data : {
109- email : session . extra . email ,
110- name : session . extra . name ,
111- role,
112- enabled : true ,
113- accounts : [ { provider : 'keycloak' , providerAccountId : session . sub , type : 'oidc' } ] ,
114- } ,
115- draft : false ,
116- overrideAccess : true ,
117- } ) ;
118- console . log ( "Created new Payload user for Keycloak user:" , newUser . id , newUser . email ) ;
119- return { method : 'bearer' , ...newUser } ;
106+ const newUser = await payload . create ( {
107+ collection : 'users' ,
108+ data : {
109+ email : session . extra . email ,
110+ name : session . extra . name ,
111+ role,
112+ enabled : true ,
113+ accounts : [ { provider : 'keycloak' , providerAccountId : session . sub , type : 'oidc' } ] ,
114+ } ,
115+ draft : false ,
116+ overrideAccess : true ,
117+ } ) ;
118+ console . log ( "Created new Payload user for Keycloak user:" , newUser . id , newUser . email ) ;
119+ return { method : 'bearer' , ...newUser } ;
120120 }
121121
122122 if ( payloadUser . role !== role ) {
123- await payload . update ( {
124- collection : 'users' ,
125- id : payloadUser . id ,
126- data : { role } ,
127- draft : false ,
128- overrideAccess : true ,
129- } ) ;
130- console . log ( `Updated Payload user role for ${ payloadUser . email } to ${ role } ` ) ;
123+ await payload . update ( {
124+ collection : 'users' ,
125+ id : payloadUser . id ,
126+ data : { role } ,
127+ draft : false ,
128+ overrideAccess : true ,
129+ } ) ;
130+ console . log ( `Updated Payload user role for ${ payloadUser . email } to ${ role } ` ) ;
131131 }
132132
133133 return { method : 'bearer' , ...payloadUser } ;
@@ -138,52 +138,144 @@ export async function authenticateRequestHeaders({ headers, payload }: { headers
138138 if ( ! authHeader ) return null ;
139139
140140 const session = await verifyToken ( authHeader . replace ( 'Bearer ' , '' ) ) ;
141- if ( ! session ?. sub || ! session . extra ) throw createAuthError ( "No valid session found" , 401 ) ;
141+ if ( ! session ?. sub || ! session . extra || ! session . agent_id ) throw createAuthError ( "No valid session found" , 401 ) ;
142142
143143 const OAUTH_CLIENT_ID = process . env . OAUTH_CLIENT_ID ! ;
144144 const permissions = ( ( session . resource_access as Record < string , { roles ?: string [ ] } > ) ?. [ OAUTH_CLIENT_ID ] ?. roles as string [ ] | undefined ) ;
145- if ( ! permissions ) throw createAuthError ( "User does not have permission to access this application." , 403 ) ;
145+ if ( ! permissions ) throw createAuthError ( "User or Agent does not have permission to access this application." , 403 ) ;
146146
147147 if ( ! payload ) throw createAuthError ( "Payload instance is required for Keycloak user normalisation" , 500 ) ;
148148
149- const payloadUser = ( await payload . find ( {
150- collection : 'users' ,
151- depth : 1 ,
152- limit : 1 ,
153- draft : false ,
154- overrideAccess : true ,
155- where : { email : { equals : session . extra . email } }
149+ const payloadUser = ( await payload . find ( {
150+ collection : 'users' ,
151+ depth : 1 ,
152+ limit : 1 ,
153+ draft : false ,
154+ overrideAccess : true ,
155+ where : { email : { equals : session . extra . email } }
156156 } ) ) . docs [ 0 ] ;
157157
158158 const role = getValidRole ( permissions ) ;
159159
160160 if ( ! payloadUser ) {
161- const newUser = await payload . create ( {
162- collection : 'users' ,
163- data : {
164- email : session . extra . email ,
165- name : session . extra . name ,
166- role,
167- enabled : true ,
168- accounts : [ { provider : 'keycloak' , providerAccountId : session . sub , type : 'oidc' } ] ,
169- } ,
170- draft : false ,
171- overrideAccess : true ,
172- } ) ;
173- console . log ( "Created new Payload user for Keycloak user:" , newUser . id , newUser . email ) ;
174- return { user : { ...newUser , collection : 'users' as const } } ;
161+ const newUser = await payload . create ( {
162+ collection : 'users' ,
163+ data : {
164+ email : session . extra . email ,
165+ name : session . extra . name ,
166+ role,
167+ enabled : true ,
168+ accounts : [ { provider : 'keycloak' , providerAccountId : session . sub , type : 'oidc' } ] ,
169+ } ,
170+ draft : false ,
171+ overrideAccess : true ,
172+ } ) ;
173+ console . log ( "Created new Payload user for Keycloak user:" , newUser . id , newUser . email ) ;
174+ return { user : { ...newUser , collection : 'users' as const } } ;
175175 }
176176
177177 if ( payloadUser . role !== role ) {
178- await payload . update ( {
179- collection : 'users' ,
180- id : payloadUser . id ,
181- data : { role } ,
182- draft : false ,
183- overrideAccess : true ,
184- } ) ;
185- console . log ( `Updated Payload user role for ${ payloadUser . email } to ${ role } ` ) ;
178+ await payload . update ( {
179+ collection : 'users' ,
180+ id : payloadUser . id ,
181+ data : { role } ,
182+ draft : false ,
183+ overrideAccess : true ,
184+ } ) ;
185+ console . log ( `Updated Payload user role for ${ payloadUser . email } to ${ role } ` ) ;
186186 }
187187
188188 return { user : { ...payloadUser , collection : 'users' as const } } ;
189- }
189+ }
190+
191+ export async function authenticateIdentity ( {
192+ headers,
193+ payload,
194+ } : {
195+ headers : Headers
196+ payload : Payload
197+ } ) {
198+ const authHeader = headers . get ( 'authorization' )
199+ if ( ! authHeader ) return null
200+
201+ const session = await verifyToken ( authHeader . replace ( 'Bearer ' , '' ) )
202+
203+ if ( ( ! session ?. sub || ! session . extra ) && ! session ?. agent_id )
204+ throw createAuthError ( 'No valid session found' , 401 )
205+
206+ const OAUTH_CLIENT_ID = process . env . OAUTH_CLIENT_ID !
207+ const permissions = ( session . resource_access as Record < string , { roles ?: string [ ] } > ) ?. [
208+ OAUTH_CLIENT_ID
209+ ] ?. roles as string [ ] | undefined
210+ if ( ! permissions )
211+ throw createAuthError ( 'User does not have permission to access this application.' , 403 )
212+
213+ if ( ! payload )
214+ throw createAuthError ( 'Payload instance is required for Keycloak user normalisation' , 500 )
215+
216+ let identity = null
217+ let collection = 'users'
218+ if ( session . agent_id && 'identities' in payload . collections ) { // this case is for agent authentication where we expect a preferred_username in the token in the format of service-account-{agentId} to link to an identity record with a keycloakUserId field matching the sub claim in the token
219+ const identities = await payload . find ( {
220+ collection : 'identities' ,
221+ limit : 1 ,
222+ where : {
223+ keycloakUserId : {
224+ equals : session . sub ,
225+ } ,
226+ } ,
227+ overrideAccess : true ,
228+ } )
229+
230+ identity = identities . docs [ 0 ]
231+ collection = 'identities'
232+ if ( ! identity ) {
233+ throw createAuthError ( 'No identity found for authenticated user' , 403 )
234+ }
235+ } else if ( session ?. extra ?. email ) { // this case is for user authentication where we expect an email in the token to link to a Payload user record
236+ const payloadUser = (
237+ await payload . find ( {
238+ collection : 'users' ,
239+ depth : 1 ,
240+ limit : 1 ,
241+ draft : false ,
242+ overrideAccess : true ,
243+ where : { email : { equals : session . extra . email } } ,
244+ } )
245+ ) . docs [ 0 ]
246+ const role = getValidRole ( permissions ) ;
247+
248+ if ( ! payloadUser ) {
249+ const newUser = await payload . create ( {
250+ collection : 'users' ,
251+ data : {
252+ email : session . extra . email ,
253+ name : session . extra . name ,
254+ role,
255+ enabled : true ,
256+ accounts : [ { provider : 'keycloak' , providerAccountId : session . sub , type : 'oidc' } ] ,
257+ } ,
258+ draft : false ,
259+ overrideAccess : true ,
260+ } ) ;
261+ console . log ( "Created new Payload user for Keycloak user:" , newUser . id , newUser . email ) ;
262+ identity = newUser ;
263+ // return { user: { ...newUser, collection: 'users' as const } };
264+ }
265+
266+ if ( payloadUser && payloadUser . role !== role ) {
267+ await payload . update ( {
268+ collection : 'users' ,
269+ id : payloadUser . id ,
270+ data : { role } ,
271+ draft : false ,
272+ overrideAccess : true ,
273+ } ) ;
274+ console . log ( `Updated Payload user role for ${ payloadUser . email } to ${ role } ` ) ;
275+ identity = { ...payloadUser , role : role as ValidRole }
276+ }
277+ } else {
278+ throw createAuthError ( 'Authenticated session does not contain an agent ID' , 403 )
279+ }
280+ return { user : { ...identity , collection } }
281+ }
0 commit comments