@@ -239,6 +239,18 @@ pub struct JwtTokenMap {
239239 /// The algorithm used to sign and verify JWT tokens.
240240 /// Default is HS256 (HMAC with SHA-256).
241241 algorithm : Algorithm ,
242+
243+ /// Additional claims to include in tokens
244+ ///
245+ /// This allows adding extra claims to the JWT tokens being generated.
246+ /// Used for including user-specific information in tokens.
247+ claims : HashMap < String , Value > ,
248+
249+ /// Token hook for extensibility
250+ ///
251+ /// A callback function that allows modifying token responses during issuance.
252+ /// Used for enriching tokens with additional data.
253+ token_hook : Option < Box < dyn FnMut ( & mut oxide_auth:: code_grant:: accesstoken:: TokenResponse ) + Send > > ,
242254}
243255
244256impl JwtTokenMap {
@@ -254,6 +266,8 @@ impl JwtTokenMap {
254266 issuer : "rust-photoacoustic" . to_string ( ) ,
255267 usage_counter : 0 ,
256268 algorithm : Algorithm :: HS256 , // Default to HMAC-SHA256
269+ claims : HashMap :: new ( ) ,
270+ token_hook : None ,
257271 }
258272 }
259273
@@ -273,6 +287,8 @@ impl JwtTokenMap {
273287 issuer : "rust-photoacoustic" . to_string ( ) ,
274288 usage_counter : 0 ,
275289 algorithm,
290+ claims : HashMap :: new ( ) ,
291+ token_hook : None ,
276292 }
277293 }
278294
@@ -303,6 +319,8 @@ impl JwtTokenMap {
303319 issuer : "rust-photoacoustic" . to_string ( ) ,
304320 usage_counter : 0 ,
305321 algorithm : Algorithm :: RS256 ,
322+ claims : HashMap :: new ( ) ,
323+ token_hook : None ,
306324 } )
307325 }
308326
@@ -338,6 +356,15 @@ impl JwtTokenMap {
338356 }
339357 }
340358
359+ // Add any additional claims
360+ for ( key, value) in & self . claims {
361+ if let Some ( val) = value {
362+ metadata. insert ( key. to_string ( ) , val. to_string ( ) ) ;
363+ } else {
364+ metadata. insert ( key. to_string ( ) , "true" . to_string ( ) ) ;
365+ }
366+ }
367+
341368 // Store the redirect URI in the metadata
342369 metadata. insert ( "redirect_uri" . to_string ( ) , grant. redirect_uri . to_string ( ) ) ;
343370
@@ -360,6 +387,27 @@ impl JwtTokenMap {
360387 } ,
361388 }
362389 }
390+
391+ /// Add user information to token claims
392+ pub fn add_user_claims ( & mut self , username : & str , permissions : & [ String ] ) -> & mut Self {
393+ // Add user information to claims
394+ self . claims . insert ( "sub" . to_string ( ) , Value :: public ( Some ( username. to_string ( ) ) ) ) ;
395+
396+ // Add permissions as a space-separated string
397+ let perms_str = permissions. join ( " " ) ;
398+ self . claims . insert ( "permissions" . to_string ( ) , Value :: public ( Some ( perms_str) ) ) ;
399+
400+ // Common identity claims
401+ self . claims . insert ( "preferred_username" . to_string ( ) , Value :: public ( Some ( username. to_string ( ) ) ) ) ;
402+ self . claims . insert ( "name" . to_string ( ) , Value :: public ( Some ( username. to_string ( ) ) ) ) ;
403+
404+ self
405+ }
406+
407+ /// Set a token hook function
408+ pub fn set_token_hook ( & mut self , hook : Box < dyn FnMut ( & mut oxide_auth:: code_grant:: accesstoken:: TokenResponse ) + Send > ) {
409+ self . token_hook = Some ( hook) ;
410+ }
363411}
364412
365413impl Issuer for JwtTokenMap {
@@ -397,13 +445,27 @@ impl Issuer for JwtTokenMap {
397445 . insert ( refresh. clone ( ) , Arc :: clone ( & token_entry) ) ;
398446 }
399447
400- // Return the issued token
401- Ok ( IssuedToken {
448+ // Create the token response
449+ let mut token = IssuedToken {
402450 token : access_token,
403451 refresh : refresh_token,
404452 until : grant. until ,
405453 token_type : TokenType :: Bearer ,
406- } )
454+ } ;
455+
456+ // Apply token hook if present
457+ if let Some ( hook) = & mut self . token_hook {
458+ // Convert IssuedToken to TokenResponse for the hook
459+ let mut token_response = oxide_auth:: code_grant:: accesstoken:: TokenResponse :: from ( token. clone ( ) ) ;
460+
461+ // Call the hook with our token response
462+ hook ( & mut token_response) ;
463+
464+ // No need to convert back as we already have our IssuedToken
465+ }
466+
467+ // Return the issued token
468+ Ok ( token)
407469 }
408470
409471 fn refresh ( & mut self , refresh : & str , mut grant : Grant ) -> Result < RefreshedToken , ( ) > {
@@ -464,13 +526,27 @@ impl Issuer for JwtTokenMap {
464526 . insert ( refresh. clone ( ) , Arc :: clone ( & new_token_entry) ) ;
465527 }
466528
467- // Return the refreshed token
468- Ok ( RefreshedToken {
529+ // Create the refreshed token
530+ let mut token = RefreshedToken {
469531 token : new_access_token,
470532 refresh : new_refresh_token,
471533 until : grant. until ,
472534 token_type : TokenType :: Bearer ,
473- } )
535+ } ;
536+
537+ // Apply token hook if present
538+ if let Some ( hook) = & mut self . token_hook {
539+ // Convert RefreshedToken to TokenResponse for the hook
540+ let mut token_response = oxide_auth:: code_grant:: accesstoken:: TokenResponse :: from ( token. clone ( ) ) ;
541+
542+ // Call the hook with our token response
543+ hook ( & mut token_response) ;
544+
545+ // No need to convert back as we already have our RefreshedToken
546+ }
547+
548+ // Return the refreshed token
549+ Ok ( token)
474550 }
475551
476552 fn recover_token < ' a > ( & ' a self , token : & ' a str ) -> Result < Option < Grant > , ( ) > {
@@ -627,6 +703,21 @@ impl JwtIssuer {
627703 self
628704 }
629705
706+ /// Add user information to token claims
707+ pub fn add_user_claims ( & mut self , username : & str , permissions : & [ String ] ) -> & mut Self {
708+ {
709+ let mut map = self . 0 . lock ( ) . unwrap ( ) ;
710+ map. add_user_claims ( username, permissions) ;
711+ }
712+ self
713+ }
714+
715+ /// Set a token hook function
716+ pub fn set_token_hook ( & mut self , hook : Box < dyn FnMut ( & mut oxide_auth:: code_grant:: accesstoken:: TokenResponse ) + Send > ) {
717+ let mut map = self . 0 . lock ( ) . unwrap ( ) ;
718+ map. set_token_hook ( hook) ;
719+ }
720+
630721 /// Print the decoded contents of a JWT token for debugging purposes
631722 /// Returns Ok if the token could be decoded, Err otherwise
632723 pub fn debug_token ( & self , token : & str ) -> Result < JwtClaims , String > {
0 commit comments