@@ -420,4 +420,141 @@ func TestAuthService_IntrospectToken(t *testing.T) {
420420 assert .Equal (t , "server_error" , response ["error" ])
421421 assert .Equal (t , "Failed to introspect the token. Please try again later." , response ["error_description" ])
422422 })
423+
424+ t .Run ("successful token introspection with impersonation (act field)" , func (t * testing.T ) {
425+ authService , storage , entClient := setupTestAuthServiceWithDatabase (t )
426+ ctx := context .Background ()
427+
428+ // Create a group for the user
429+ unverifiedGroup , err := entClient .Group .Query ().Where (group .NameEQ ("unverified" )).Only (ctx )
430+ require .NoError (t , err )
431+
432+ // Create a test user (the impersonated user)
433+ user , err := entClient .User .Create ().
434+ SetName ("Impersonated User" ).
435+ 436+ SetGroup (unverifiedGroup ).
437+ Save (ctx )
438+ require .NoError (t , err )
439+
440+ // Create an impersonator user
441+ impersonator , err := entClient .User .Create ().
442+ SetName ("Admin User" ).
443+ 444+ SetGroup (unverifiedGroup ).
445+ Save (ctx )
446+ require .NoError (t , err )
447+
448+ // Create a test token with impersonation metadata
449+ tokenInfo := auth.TokenInfo {
450+ UserID : user .ID ,
451+ 452+ Machine : "test-machine" ,
453+ Scopes : []string {"read" , "write" },
454+ Meta : map [string ]string {
455+ "impersonation" : strconv .Itoa (impersonator .ID ),
456+ },
457+ }
458+ token , err := storage .Create (ctx , tokenInfo )
459+ require .NoError (t , err )
460+ require .NotEmpty (t , token )
461+
462+ // Setup router
463+ router := gin .New ()
464+ router .POST ("/auth/v2/introspect" , authService .IntrospectToken )
465+
466+ // Create introspect request
467+ form := url.Values {}
468+ form .Add ("token" , token )
469+ form .Add ("token_type_hint" , "access_token" )
470+
471+ req := httptest .NewRequest ("POST" , "/auth/v2/introspect" , strings .NewReader (form .Encode ()))
472+ req .Header .Set ("Content-Type" , "application/x-www-form-urlencoded" )
473+ rr := httptest .NewRecorder ()
474+
475+ // Execute request
476+ router .ServeHTTP (rr , req )
477+
478+ // Verify response
479+ assert .Equal (t , http .StatusOK , rr .Code )
480+
481+ var response IntrospectionResponse
482+ err = json .NewDecoder (rr .Body ).Decode (& response )
483+ require .NoError (t , err )
484+
485+ // Verify basic token information
486+ assert .True (t , response .Active )
487+ assert .
Equal (
t ,
"[email protected] " ,
response .
Username )
488+ assert .Equal (t , "read write" , response .Scope )
489+ assert .Equal (t , strconv .Itoa (user .ID ), response .Sub )
490+ assert .Equal (t , "test-machine" , response .Azp )
491+ assert .Greater (t , response .Exp , int64 (0 ))
492+ assert .Greater (t , response .Iat , int64 (0 ))
493+
494+ // Verify the act field is populated with impersonator information
495+ require .NotNil (t , response .Act , "Act field should be populated when impersonation is present" )
496+ assert .Equal (t , strconv .Itoa (impersonator .ID ), response .Act .Sub , "Act.Sub should contain the impersonator's user ID" )
497+ })
498+
499+ t .Run ("successful token introspection without impersonation (no act field)" , func (t * testing.T ) {
500+ authService , storage , entClient := setupTestAuthServiceWithDatabase (t )
501+ ctx := context .Background ()
502+
503+ // Create a group for the user
504+ unverifiedGroup , err := entClient .Group .Query ().Where (group .NameEQ ("unverified" )).Only (ctx )
505+ require .NoError (t , err )
506+
507+ // Create a test user
508+ user , err := entClient .User .Create ().
509+ SetName ("Regular User" ).
510+ 511+ SetGroup (unverifiedGroup ).
512+ Save (ctx )
513+ require .NoError (t , err )
514+
515+ // Create a test token without impersonation metadata
516+ tokenInfo := auth.TokenInfo {
517+ UserID : user .ID ,
518+ 519+ Machine : "test-machine" ,
520+ Scopes : []string {"read" , "write" },
521+ Meta : map [string ]string {}, // No impersonation metadata
522+ }
523+ token , err := storage .Create (ctx , tokenInfo )
524+ require .NoError (t , err )
525+ require .NotEmpty (t , token )
526+
527+ // Setup router
528+ router := gin .New ()
529+ router .POST ("/auth/v2/introspect" , authService .IntrospectToken )
530+
531+ // Create introspect request
532+ form := url.Values {}
533+ form .Add ("token" , token )
534+ form .Add ("token_type_hint" , "access_token" )
535+
536+ req := httptest .NewRequest ("POST" , "/auth/v2/introspect" , strings .NewReader (form .Encode ()))
537+ req .Header .Set ("Content-Type" , "application/x-www-form-urlencoded" )
538+ rr := httptest .NewRecorder ()
539+
540+ // Execute request
541+ router .ServeHTTP (rr , req )
542+
543+ // Verify response
544+ assert .Equal (t , http .StatusOK , rr .Code )
545+
546+ var response IntrospectionResponse
547+ err = json .NewDecoder (rr .Body ).Decode (& response )
548+ require .NoError (t , err )
549+
550+ // Verify basic token information
551+ assert .True (t , response .Active )
552+ assert .
Equal (
t ,
"[email protected] " ,
response .
Username )
553+ assert .Equal (t , "read write" , response .Scope )
554+ assert .Equal (t , strconv .Itoa (user .ID ), response .Sub )
555+ assert .Equal (t , "test-machine" , response .Azp )
556+
557+ // Verify the act field is NOT populated when there's no impersonation
558+ assert .Nil (t , response .Act , "Act field should be nil when no impersonation is present" )
559+ })
423560}
0 commit comments