Skip to content

Commit 429ed2c

Browse files
committed
feat(token-revocation): also revoke refresh_token
1 parent 5abcd07 commit 429ed2c

File tree

6 files changed

+65
-20
lines changed

6 files changed

+65
-20
lines changed

README.md

+17-1
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,15 @@ Please note, that this dependency is not needed for the **code flow**, which is
4141
### Breaking change in 9.1.0
4242

4343
The use of `encodeURIComponent` on the argument passed to `initImplicitFlow` and its Code Flow counterparts was mandatory before this version.
44+
4445
Since that was considered a _bug_, the need to do so was removed.
4546
Now the reverse is true **if you're upgrading from before 9.0.0**: you need to remove any call to encode URI components in your own application, as the library will now do it for you.
4647

4748
## Tested Environment
4849

4950
Successfully tested with **Angular 9** and its Router, PathLocationStrategy as well as HashLocationStrategy and CommonJS-Bundling via webpack. At server side we've used IdentityServer (.NET / .NET Core) and Redhat's Keycloak (Java).
5051

51-
**Angular 9**: Use 9.x versions of this library (should also work with older Angular versions!).
52+
**Angular 9**: Use 9.x versions of this library (**should also work with older Angular versions!**).
5253

5354
**Angular 8**: Use 8.x versions of this library.
5455

@@ -90,6 +91,7 @@ Successfully tested with **Angular 9** and its Router, PathLocationStrategy as w
9091
- Hook for further custom validations
9192
- Single-Sign-Out by redirecting to the auth-server's logout-endpoint
9293
- Tested with all modern browsers and IE
94+
- Token Revocation according to [RFC 7009](https://tools.ietf.org/html/rfc7009#section-2.2)
9395

9496
## Sample-Auth-Server
9597

@@ -204,6 +206,20 @@ this.oauthService.configure(authCodeFlowConfig);
204206
this.oauthService.loadDiscoveryDocumentAndTryLogin();
205207
```
206208

209+
### Logging out
210+
211+
The logOut method clears the used token store (by default ``sessionStorage``) and forwards the user to the auth servers logout endpoint if one was configured (manually or via the discovery document).
212+
213+
```typescript
214+
this.oauthService.logOut();
215+
```
216+
217+
If you want to revoke the existing access token and the existing refresh token before logging out, use the following method:
218+
219+
```typescript
220+
this.oauthService.revokeTokenAndLogout();
221+
```
222+
207223
### Skipping the Login Form
208224

209225
If you don't want to display a login form that tells the user that they are redirected to the identity server, you can use the convenience function `this.oauthService.loadDiscoveryDocumentAndLogin();` instead of `this.oauthService.loadDiscoveryDocumentAndTryLogin();` when setting up the library.

projects/lib/src/oauth-service.ts

+33-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Injectable, NgZone, Optional, OnDestroy, Inject } from '@angular/core';
22
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
3-
import { Observable, Subject, Subscription, of, race, from } from 'rxjs';
3+
import { Observable, Subject, Subscription, of, race, from, combineLatest } from 'rxjs';
44
import {
55
filter,
66
delay,
@@ -2557,11 +2557,15 @@ export class OAuthService extends AuthConfig implements OnDestroy {
25572557
* up any security credentials associated with the authorization
25582558
*/
25592559
public revokeTokenAndLogout(): Promise<any> {
2560-
let revoke_endpoint = this.revocationEndpoint;
2561-
let current_access_token = this.getAccessToken();
2562-
let params = new HttpParams()
2563-
.set('token', current_access_token)
2564-
.set('token_type_hint', 'access_token');
2560+
let revokeEndpoint = this.revocationEndpoint;
2561+
let accessToken = this.getAccessToken();
2562+
let refreshToken = this.getRefreshToken();
2563+
2564+
if (!accessToken) {
2565+
return;
2566+
}
2567+
2568+
let params = new HttpParams();
25652569

25662570
let headers = new HttpHeaders().set(
25672571
'Content-Type',
@@ -2588,10 +2592,29 @@ export class OAuthService extends AuthConfig implements OnDestroy {
25882592
}
25892593

25902594
return new Promise((resolve, reject) => {
2591-
if (current_access_token) {
2592-
this.http
2593-
.post<any>(revoke_endpoint, params, { headers })
2594-
.subscribe(
2595+
let revokeAccessToken: Observable<void>;
2596+
let revokeRefreshToken: Observable<void>;
2597+
2598+
if (accessToken) {
2599+
let revokationParams = params
2600+
.set('token', accessToken)
2601+
.set('token_type_hint', 'access_token');
2602+
revokeAccessToken = this.http.post<void>(revokeEndpoint, revokationParams, { headers });
2603+
} else {
2604+
revokeAccessToken = of(null);
2605+
}
2606+
2607+
if (refreshToken) {
2608+
let revokationParams = params
2609+
.set('token', refreshToken)
2610+
.set('token_type_hint', 'refresh_token');
2611+
revokeRefreshToken = this.http.post<void>(revokeEndpoint, revokationParams, { headers });
2612+
} else {
2613+
revokeRefreshToken = of(null);
2614+
}
2615+
2616+
combineLatest([revokeAccessToken, revokeRefreshToken])
2617+
.subscribe(
25952618
res => {
25962619
this.logOut();
25972620
resolve(res);
@@ -2605,9 +2628,6 @@ export class OAuthService extends AuthConfig implements OnDestroy {
26052628
reject(err);
26062629
}
26072630
);
2608-
} else {
2609-
this.logger.warn('User not logged in to revoke token.');
2610-
}
26112631
});
26122632
}
26132633
}

projects/sample/src/app/app.component.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,16 @@ export class AppComponent {
3333

3434
private configureCodeFlow() {
3535
this.oauthService.configure(authCodeFlowConfig);
36-
this.oauthService.loadDiscoveryDocumentAndTryLogin();
36+
this.oauthService.loadDiscoveryDocumentAndTryLogin().then(_ => {
37+
if (useHash) {
38+
this.router.navigate(['/']);
39+
}
40+
});
3741

3842
// Optional
39-
// this.oauthService.setupAutomaticSilentRefresh();
43+
this.oauthService.setupAutomaticSilentRefresh();
44+
45+
4046
}
4147

4248
private configureImplicitFlow() {

projects/sample/src/app/auth-code-flow.config.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ export const authCodeFlowConfig: AuthConfig = {
4646

4747
sessionChecksEnabled: true,
4848

49-
timeoutFactor: 0.01
49+
timeoutFactor: 0.01,
5050
// disablePKCI: true,
51+
52+
clearHashAfterLogin: false,
5153
};

projects/sample/src/app/home/home.component.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ export class HomeComponent implements OnInit {
7979
}
8080

8181
logout() {
82-
this.oauthService.logOut();
82+
// this.oauthService.logOut();
83+
this.oauthService.revokeTokenAndLogout();
8384
}
8485

8586
loadUserProfile(): void {

projects/sample/src/flags.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Use HashLocationStrategy for routing?
2-
export const useHash = false;
2+
export const useHash = true;
33

44
// Set this to true, to use silent refresh; otherwise the example
55
// uses the refresh_token via an AJAX coll to get new tokens.
6-
export const useSilentRefreshForCodeFlow = true;
6+
export const useSilentRefreshForCodeFlow = false;

0 commit comments

Comments
 (0)