DEV Community

Alessandro Prencipe
Alessandro Prencipe

Posted on

6

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.

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

Rather than just generating snippets, our agents understand your entire project context, can make decisions, use tools, and carry out tasks autonomously.

Read full post →

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

AWS GenAI LIVE!

GenAI LIVE! is a dynamic live-streamed show exploring how AWS and our partners are helping organizations unlock real value with generative AI.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️