@@ -3992,3 +3992,93 @@ test('database.changePassword request carries the Auth0-Client telemetry header'
39923992 await c . database . changePassword ( { email : 'a@b.com' , connection : 'db' } ) ;
39933993 expect ( headers ?. get ( 'Auth0-Client' ) ) . toBeTruthy ( ) ;
39943994} ) ;
3995+
3996+ describe ( 'revokeToken' , ( ) => {
3997+ const revocationEndpoint = `https://${ domain } /oauth/revoke` ;
3998+
3999+ const setupRevocationHandlers = ( handler : Parameters < typeof http . post > [ 1 ] ) => {
4000+ server . use (
4001+ http . get ( `https://${ domain } /.well-known/openid-configuration` , ( ) =>
4002+ HttpResponse . json ( { ...buildOpenIdConfiguration ( domain ) , revocation_endpoint : revocationEndpoint } )
4003+ ) ,
4004+ http . post ( revocationEndpoint , handler )
4005+ ) ;
4006+ } ;
4007+
4008+ const makeClient = ( ) =>
4009+ new AuthClient ( {
4010+ domain,
4011+ clientId : '<client_id>' ,
4012+ clientSecret : '<client_secret>' ,
4013+ discoveryCache : { ttl : 0 } ,
4014+ } ) ;
4015+
4016+ test ( 'should successfully revoke a token' , async ( ) => {
4017+ let capturedToken : string | null = null ;
4018+ let capturedHint : string | null = null ;
4019+ setupRevocationHandlers ( async ( { request } ) => {
4020+ const body = await request . formData ( ) ;
4021+ capturedToken = body . get ( 'token' ) as string ;
4022+ capturedHint = body . get ( 'token_type_hint' ) as string ;
4023+ return new HttpResponse ( null , { status : 200 } ) ;
4024+ } ) ;
4025+
4026+ await expect (
4027+ makeClient ( ) . revokeToken ( { token : '<refresh_token>' , tokenTypeHint : 'refresh_token' } )
4028+ ) . resolves . toBeUndefined ( ) ;
4029+ expect ( capturedToken ) . toBe ( '<refresh_token>' ) ;
4030+ expect ( capturedHint ) . toBe ( 'refresh_token' ) ;
4031+ } ) ;
4032+
4033+ test ( 'should revoke a token without tokenTypeHint' , async ( ) => {
4034+ let capturedHint : FormDataEntryValue | null = null ;
4035+ setupRevocationHandlers ( async ( { request } ) => {
4036+ const body = await request . formData ( ) ;
4037+ capturedHint = body . get ( 'token_type_hint' ) ;
4038+ return new HttpResponse ( null , { status : 200 } ) ;
4039+ } ) ;
4040+
4041+ await expect (
4042+ makeClient ( ) . revokeToken ( { token : '<refresh_token>' } )
4043+ ) . resolves . toBeUndefined ( ) ;
4044+ expect ( capturedHint ) . toBeNull ( ) ;
4045+ } ) ;
4046+
4047+ test ( 'should throw TokenRevocationError when revocation fails' , async ( ) => {
4048+ setupRevocationHandlers ( ( ) =>
4049+ HttpResponse . json (
4050+ { error : '<error_code>' , error_description : '<error_description>' } ,
4051+ { status : 400 }
4052+ )
4053+ ) ;
4054+
4055+ await expect (
4056+ makeClient ( ) . revokeToken ( { token : '<invalid_token>' } )
4057+ ) . rejects . toThrowError (
4058+ expect . objectContaining ( {
4059+ code : 'token_revocation_error' ,
4060+ message : 'An error occurred while trying to revoke the token.' ,
4061+ cause : expect . objectContaining ( {
4062+ error : '<error_code>' ,
4063+ error_description : '<error_description>' ,
4064+ } ) ,
4065+ } )
4066+ ) ;
4067+ } ) ;
4068+
4069+ test ( 'should send client credentials on the revocation request' , async ( ) => {
4070+ let capturedClientId : string | null = null ;
4071+ let capturedClientSecret : string | null = null ;
4072+ setupRevocationHandlers ( async ( { request } ) => {
4073+ const body = await request . formData ( ) ;
4074+ capturedClientId = body . get ( 'client_id' ) as string ;
4075+ capturedClientSecret = body . get ( 'client_secret' ) as string ;
4076+ return new HttpResponse ( null , { status : 200 } ) ;
4077+ } ) ;
4078+
4079+ await makeClient ( ) . revokeToken ( { token : '<refresh_token>' , tokenTypeHint : 'refresh_token' } ) ;
4080+
4081+ expect ( capturedClientId ) . toBe ( '<client_id>' ) ;
4082+ expect ( capturedClientSecret ) . toBe ( '<client_secret>' ) ;
4083+ } ) ;
4084+ } ) ;
0 commit comments