DEV Community

Alessandro Prencipe
Alessandro Prencipe

Posted on

How I implemented the authentication with Amplify in Angular

Authentication with Amplify is very easy even though I struggled a little bit at the beginning.
Here's how I implemented my authentication. Let me know what you think.

  • I created my auth auth.service.ts with all the possible operations:

`
import { Injectable } from '@angular/core';
import { CognitoUser } from 'amazon-cognito-identity-js';
import { Auth } from 'aws-amplify';
import { CognitoHostedUIIdentityProvider } from '@aws-amplify/auth';

@Injectable({
providedIn: 'root',
})
export class AuthService {
user: CognitoUser | undefined;
constructor() {}

async signIn({
email,
psw,
}: {
email: string;
psw: string;
}): Promise {
return Auth.signIn(email, psw);
}

async setNewPasswordFirstLogin({
psw,
}: {
psw: string;
}): Promise {
return Auth.completeNewPassword(this.user, psw);
}

async currentAuthenticatedUser(): Promise {
return Auth.currentAuthenticatedUser();
}

async signOut(): Promise {
return Auth.signOut();
}

async forgotPasswordSendEmail({
email,
}: {
email: string;
}): Promise {
return Auth.forgotPassword(email);
}

async forgotPasswordSetNewPsw({
email,
psw,
code,
}: {
email: string;
psw: string;
code: string;
}): Promise {
return Auth.forgotPasswordSubmit(email, code, psw);
}

setUser(user: CognitoUser) {
this.user = user;
}

async federatedSignIn({ type }: { type: string }) {
let provider: CognitoHostedUIIdentityProvider | undefined = undefined;
if (type === 'Google') provider = CognitoHostedUIIdentityProvider.Google;
if (type === 'Apple') provider = CognitoHostedUIIdentityProvider.Apple;
if (type === 'Facebook')
provider = CognitoHostedUIIdentityProvider.Facebook;
if (provider) {
return Auth.federatedSignIn({
provider: provider,
});
}
return;
}
}
`

  • I created the jwt.interceptor.ts, this was the toughest part that I implemented.
import { Injectable } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor,
} from '@angular/common/http';
import { Observable, from, switchMap, catchError, throwError } from 'rxjs';
import { environment } from 'src/environments/environment';
import { AuthService } from 'src/app/core/services/auth.service';
import { Auth } from 'aws-amplify';
import { CognitoUserSession } from 'amazon-cognito-identity-js';

@Injectable()
export class JwtInterceptor implements HttpInterceptor {
  constructor(private authService: AuthService) {}

  intercept(
    request: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    return from(Auth.currentSession()).pipe(
      switchMap((auth: any) => {
        // switchMap() is used instead of map().
        const jwt: string = auth.idToken.jwtToken;
        const authRequest = request.clone({
          setHeaders: {
            Authorization: `Bearer ${jwt}`,
          },
        });
        return next.handle(authRequest);
      }),
      catchError((err) => {
        if (err.status) {
          return throwError(() => new Error(err));
        }
      })
    );
  }
}

Enter fullscreen mode Exit fullscreen mode
  • I implemented the error.interceptor.ts as well for the auto logout
import { Injectable } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor,
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { Router } from '@angular/router';
import { AuthService } from '../services/auth.service';
import { LoaderService } from 'src/app/shared/services/loader.service';

/**
 * This is used to logout the user, when the server responds with an unathorized status code.
 * Especially when the session token expires.
 * @export
 * @class ErrorInterceptor
 * @implements {HttpInterceptor}
 */
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
  constructor(
    private readonly router: Router,
    private readonly authService: AuthService,
  ) {}

  /**
   * Interceptor intercepts the responses, and then process based on the received status code
   * @param {HttpRequest<any>} request
   * @param {HttpHandler} next
   * @returns {Observable<HttpEvent<any>>}
   * @memberof ErrorInterceptor
   */
  intercept(
    request: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    return next.handle(request).pipe(
      catchError((err) => {
        if (err.status === 401) {
          // auto logout if 401 response returned from api
          this.authService
            .signOut()
            .then(() => {
              this.router.navigate(['/auth']);
            })
            .catch((err) => console.log(err));
        }

        // err.error is not null, if the ResponsenEntity contains an Exception
        // err.error.message will give the custom message send from the server
        const error = err.error.errorMessage || err.statusText;
        return throwError(() => new Error(error));
      })
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

I created a login component but it is a simple component that calls the service.

Let me know what you think about this implementation and if there's a better way to do this particular task.

Top comments (0)