Skip to content

Instantly share code, notes, and snippets.

@alxhub
Last active July 4, 2020 18:29
Show Gist options
  • Select an option

  • Save alxhub/1fa85d0b8ef0286b617566e65a0011b4 to your computer and use it in GitHub Desktop.

Select an option

Save alxhub/1fa85d0b8ef0286b617566e65a0011b4 to your computer and use it in GitHub Desktop.

Revisions

  1. alxhub revised this gist Jul 20, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion auth-interceptor-example.ts
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@ abstract class AuthService {
    private subject = new BehaviorSubject<string|null>(null);

    readonly refreshToken: Observable<any>;
    readonly token: Observable<string|null>;
    readonly token: Observable<string>;

    constructor() {
    // refreshToken, when subscribed, gets the new token from the backend,
  2. alxhub revised this gist Jul 20, 2017. 1 changed file with 3 additions and 2 deletions.
    5 changes: 3 additions & 2 deletions auth-interceptor-example.ts
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,7 @@
    abstract class AuthService {
    // Subject tracks the
    private subject = new BehaviorSubject<string>(null);
    // Subject tracks the current token, or is null if no token is currently
    // available (e.g. refresh pending).
    private subject = new BehaviorSubject<string|null>(null);

    readonly refreshToken: Observable<any>;
    readonly token: Observable<string|null>;
  3. alxhub created this gist Jul 20, 2017.
    78 changes: 78 additions & 0 deletions auth-interceptor-example.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,78 @@
    abstract class AuthService {
    // Subject tracks the
    private subject = new BehaviorSubject<string>(null);

    readonly refreshToken: Observable<any>;
    readonly token: Observable<string|null>;

    constructor() {
    // refreshToken, when subscribed, gets the new token from the backend,
    // and then completes without values.
    this.refreshToken = Observable.defer(() => {
    // Defer allows us to easily execute some action when the Observable
    // is subscribed. Here, we set the current token to `null` until the
    // refresh operation is complete. This ensures no requests will be
    // sent with a known bad token.
    this.subject.next(null);

    return this
    // Next, we refresh the token from the server.
    .doRefreshToken()
    // Set it as the active token.
    .do(token => this.subject.next(token))
    // Drop the value, ensuring this Observable only completes when
    // done and doesn't emit.
    .ignoreElements()
    // Finally, share the Observable so we don't attempt multiple
    // refreshes at once.
    .shareReplay();
    });

    // token, when subscribed, returns the latest token.
    this.token = this
    // Read the subject (stream of tokens).
    .subject
    // Filter out the `null` ones. This part ensure we wait for the next
    // good token.
    .filter(token => token !== null)
    // Take the next good token.
    .take(1);

    // There's no current token to start, so refresh to start with. Optionally,
    // we could set token up to refresh on the first subscription.
    this.refreshToken.subscribe();
    }

    // Actually refresh the token. Left up to the user.
    abstract doRefreshToken(): Observable<string>;
    }


    class AuthInterceptor implements HttpInterceptor {

    constructor(private auth: AuthService) { }

    private addToken(req: HttpRequest<any>): HttpRequest<any> {
    return req.clone({ headers: req.headers.set('Authorization', `Bearer ${this.auth.getToken()}`) });
    }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return this
    .auth
    // Get the latest token from the auth service.
    .token
    // Map the token to a request with the right header set.
    .map(token => req.clone({ headers: req.headers.set('Authorization', `Bearer ${token}`) }))
    // Execute the request on the server.
    .concatMap(authReq => next.handle(authReq))
    // Catch the 401 and handle it by refreshing the token and restarting the chain
    // (where a new subscription to this.auth.token will get the latest token).
    .catch((err, restart) => {
    // If the request is unauthorized, try refreshing the token before restarting.
    if (err instanceof HttpErrorResponse && err.status === 401) {
    return Observable.concat(this.auth.refreshToken, restart);
    }
    throw err;
    });
    }
    }