Search code examples
angularamazon-cognitoangular-http-interceptors

How to Pass Cognito JWT Token in HttpInteceptor in Angular


I currently have an application that uses Cognito for user login and authentication

This is what I'm using for the the user Authentication

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import Amplify, { Auth } from 'aws-amplify';

import { environment } from '../environments/environment';

export interface IUser {
  email: string;
  password: string;
  showPassword: boolean;
  code: string;
  name: string;
}

@Injectable({
  providedIn: 'root',
})
export class CognitoService {

  private authenticationSubject: BehaviorSubject<any>;

  Tokentest: string = '';

  constructor() {
    Amplify.configure({
      Auth: environment.cognito
    });

    this.authenticationSubject = new BehaviorSubject<boolean>(false);
  }

  public signUp(user: IUser): Promise<any> {
    return Auth.signUp({
      username: user.email,
      password: user.password,
    });
  }

  public confirmSignUp(user: IUser): Promise<any> {
    return Auth.confirmSignUp(user.email, user.code);
  }

  public signIn(user: IUser): Promise<any> {

    return Auth.signIn(user.email, user.password)
    .then((user) => {
      this.authenticationSubject.next(true);
      let Token = user.getSignInUserSession().getAccessToken().getJwtToken();
      console.log(Token);
      this.Tokentest = Token;
    });
    
  }

  public signOut(): Promise<any> {
    return Auth.signOut()
    .then(() => {
      this.authenticationSubject.next(false);
    });
  }

  public isAuthenticated(): Promise<boolean> {
    if (this.authenticationSubject.value) {
      return Promise.resolve(true);
    } else {
      return this.getUser()
      .then((user: any) => {
        if (user) {
          return true;
        } else {
          return false;
        }
      }).catch(() => {
        return false;
      });
    }
  }

  public getUser(): Promise<any> {
    return Auth.currentUserInfo();
  }

  public updateUser(user: IUser): Promise<any> {
    return Auth.currentUserPoolUser()
    .then((cognitoUser: any) => {
      return Auth.updateUserAttributes(cognitoUser, user);
    });
  }

}

I then have a token interceptor service that looks at any http request and passes the jwt token in the header

import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import Amplify, { Auth} from 'aws-amplify';
import { IUser, CognitoService } from '../cognito.service';

@Injectable({
  providedIn: 'root'
})

export class TokenInteceptorService implements HttpInterceptor {


  constructor() { }
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    ///
    let token = localStorage.getItem('{the key for the jwt token}')
    let jwttoken = req.clone({
     setHeaders:{
       Authorization: 'Bearer ' + token
     } 
    })
    return next.handle(jwttoken);
  }
}

My issue is that thus far I have getting the key for the token value that is set in localStorage using getItem and passing that key value into the header.

This does not work if I have multiple users as each key will be different.

How would I go about passing the jwt token via the interceptor no matter who is logged in.

Any help would be greatly appreciated


Solution

  • You should inject your CognitoService to your Interceptor as following:

    import {
      HttpEvent,
      HttpHandler,
      HttpInterceptor,
      HttpRequest,
    } from '@angular/common/http';
    import { Injectable } from '@angular/core';
    import { Observable, from, switchMap } from 'rxjs';
    import Amplify, { Auth } from 'aws-amplify';
    import { IUser, CognitoService } from '../cognito.service';
    
    @Injectable({
      providedIn: 'root',
    })
    export class TokenInteceptorService implements HttpInterceptor {
      constructor(private readonly _cognitoService: CognitoService) {}
    
      intercept(
        req: HttpRequest<any>,
        next: HttpHandler
      ): Observable<HttpEvent<any>> {
        //
        return from(this._cognitoService.getSession()).pipe(
          switchMap((session) => {
            let jwttoken = req.clone({
              setHeaders: {
                Authorization: 'Bearer ' + session.accessToken.jwtToken,
              },
            });
            return next.handle(jwttoken);
          })
        );
      }
    }
    

    and then in your CognitoService you have to provide getSession() function:

    import { Injectable } from '@angular/core';
    import { BehaviorSubject } from 'rxjs';
    import Amplify, { Auth } from 'aws-amplify';
    
    import { environment } from '../environments/environment';
    
    export interface IUser {
      email: string;
      password: string;
      showPassword: boolean;
      code: string;
      name: string;
    }
    
    @Injectable({
      providedIn: 'root',
    })
    export class CognitoService {
      private authenticationSubject: BehaviorSubject<any>;
    
      Tokentest: string = '';
    
      constructor() {
        Amplify.configure({
          Auth: environment.cognito,
        });
    
        this.authenticationSubject = new BehaviorSubject<boolean>(false);
      }
    
      public signUp(user: IUser): Promise<any> {
        return Auth.signUp({
          username: user.email,
          password: user.password,
        });
      }
    
      public confirmSignUp(user: IUser): Promise<any> {
        return Auth.confirmSignUp(user.email, user.code);
      }
    
      public signIn(user: IUser): Promise<any> {
        return Auth.signIn(user.email, user.password).then((user) => {
          this.authenticationSubject.next(true);
          let Token = user.getSignInUserSession().getAccessToken().getJwtToken();
          console.log(Token);
          this.Tokentest = Token;
        });
      }
    
      public signOut(): Promise<any> {
        return Auth.signOut().then(() => {
          this.authenticationSubject.next(false);
        });
      }
    
      public isAuthenticated(): Promise<boolean> {
        if (this.authenticationSubject.value) {
          return Promise.resolve(true);
        } else {
          return this.getUser()
            .then((user: any) => {
              if (user) {
                return true;
              } else {
                return false;
              }
            })
            .catch(() => {
              return false;
            });
        }
      }
    
      public getUser(): Promise<any> {
        return Auth.currentUserInfo();
      }
    
      public getSession(): Promise<any> {
        return Auth.currentSession();
      }
    
      public updateUser(user: IUser): Promise<any> {
        return Auth.currentUserPoolUser().then((cognitoUser: any) => {
          return Auth.updateUserAttributes(cognitoUser, user);
        });
      }
    }
    
    

    One hint: You should also review your types. Setting any as a type is usually not a good practice.