Login and Signup with API in Angular 17

Modern Angular: Angular Standalone Components

Subscribe to our Angular newsletter and get Modern Angular ebook!

In this tutorial, we'll learn how to build login and signup pages with Angular 17. we'll use HTML and CSS to create the pages then use Angular code to build the login and signup forms and get the login and signup data from the forms using a reactive approach and then submit it to the auth endpoint using HttpClient.

Setting Up the Authentication Backend

We'll use this Node.js app from https://github.com/techiediaries/node-mongoose-jwt which provides an authentication backend so make sure you have MongoDB installed in your system then clone the repo, navigate inside of it and run the following command:

npm install

Next create an .env file and add the following contents:

CONNECTION_STRING= mongodb://127.0.0.1:27017/myapp
JWT_SECRET= secret123456789

Next, run the backend using this command:

npm start

You app JWT authentication backend will be listening at http://localhost:3000.

Setting Up Angular 17 Authentication Frontend

Now let's get started with our Angular 17 frontend. Open a new terminal and create a new project using the ng new command then navigate inside your project and create three components for login, signup and admin:

ng g c pages/login
ng g c pages/signup
ng g c pages/admin

We also need to create an authentication service and guard using these commands:

ng g s auth/auth
ng g guard auth/auth

Choose CanActivate for the authentication guard:

❯◉ CanActivate
 ◯ CanActivateChild
 ◯ CanDeactivate
 ◯ CanMatch

Then press Enter.

Routing Configuration

Next, we need to add the routes to different components and protect the admin component with the authentication guard. Open the src/app/app.routes.ts file and update it as follows:

import { Routes } from '@angular/router';
import { LoginComponent } from './pages/login/login.component';
import { SignupComponent } from './pages/signup/signup.component';
import { AdminComponent } from './pages/admin/admin.component';
import { authGuard } from './auth/auth.guard';

export const routes: Routes = [
    {
        path: '', redirectTo: '/login', pathMatch: 'full'
    },
    {
        path: 'login', component: LoginComponent
    },
    {
        path: 'signup', component: SignupComponent
    },

    {
        path: 'admin', component: AdminComponent, canActivate: [authGuard]
    }
];

As you can see we are using the redirectTo attribute to redirect users to the login page when they visit the home path and the canActivate attribute to protect the admin page from non authenticated users. We'll implement the authentication guard later.

Implementing the Authentication Service

We'll be using the HttpClient service for communicating with authentication backend so we'll need to provide it first. Open the src/app/app.config.ts file and add the following import:

import { provideHttpClient } from '@angular/common/http';

Next, call provideHttpClient() inside the providers array:

export const appConfig: ApplicationConfig = {
  providers: [provideRouter(routes), provideHttpClient()]
};

Next, let's implement the authentication service. Open the src/app/auth/auth.service.ts file and start by adding the following imports:

import { HttpClient } from '@angular/common/http';
import { inject } from '@angular/core';
import { tap } from 'rxjs/operators';

In the authentication service, inject the HttpClient service using the inject method and define a base URL that contains the URL of our auth backend:

httpClient = inject(HttpClient);
baseUrl = 'http://localhost:3000/api';

Next, define the signup() method of the authentication service:

signup(data: any) {
  return this.httpClient.post(`${this.baseUrl}/register`, data);
}

Define the login() method of the authentication service:

  login(data: any) {
    return this.httpClient.post(`${this.baseUrl}/login`, data)
      .pipe(tap((result) => {
        localStorage.setItem('authUser', JSON.stringify(result));
      }));
  }

Next, define the logout() method:

  logout() {
    localStorage.removeItem('authUser');
  }

Finally define the isLoggedIn() method:

  isLoggedIn() {
    return localStorage.getItem('authUser') !== null;
  }

Implementing the Authentication Guard

Next, let's implement the authentication guard. Open the src/app/auth/auth.guard.ts file and add these imports:

import { inject } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from './auth.service';

Next, inside the authGuard function inject the authentication and router services:

const  authService  =  inject(AuthService);
const  router  =  inject(Router);

Next, add the following code:

  if (authService.isLoggedIn()) {
    return true;
  }
  router.navigate(['/login']);
  return false;

This simply tells Angular 17 to navigate the login path if the user is not logged in.

Implementing the Login Component

Next, open the src/app/pages/login/login.component.ts file and add the following imports:

import { inject } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { Router, RouterModule } from '@angular/router';
import { AuthService } from '../../auth/auth.service';

Next, add the reactive forms and router modules inside the imports array of the login component:

@Component({
  selector: 'app-login',
  standalone: true,
  imports: [ ReactiveFormsModule, RouterModule],
  templateUrl: './login.component.html',
  styleUrl: './login.component.css'
})

Next, inject the authentication and router services using the inject method:

  authService = inject(AuthService);
  router = inject(Router);

Adding the login form

Next, add a form group to the login component:

  protected loginForm = new FormGroup({
    email: new FormControl('', [Validators.required, Validators.email]),
    password: new FormControl('', [Validators.required])
  })

The login form contains an email and password controls with validation. Next, add the onSubmit() method to the login component:

  onSubmit(){
    if(this.loginForm.valid){
      console.log(this.loginForm.value);
      this.authService.login(this.loginForm.value)
      .subscribe((data: any) => {
        if(this.authService.isLoggedIn()){
          this.router.navigate(['/admin']);
        }
        console.log(data);
      });
    }
  }

This method checks if the form is valid, then attempts to log in with the AuthService and navigates to an admin page upon successful authentication.

Adding the HTML form for login

Open the src/app/pages/login/login.component.html file and add the following HTML form:

<div class="login-card">

    <div class="card-title">
        <h1>Login</h1>
        <span>You don't have an account? <a routerLink="/signup">Sign up</a></span>
    </div>
    <form class="form-group" [formGroup]="loginForm" (ngSubmit)="onSubmit()">
        <input name="email" type="email" formControlName="email" placeholder="Email">
        <input type="password" formControlName="password" placeholder="Password">
        <button>Submit</button>
    </form>

</div>

This code is an Angular template for a login form. It uses a combination of HTML, Angular directives, and reactive forms to create a login card with a title, a link to sign up, and a form that submits login information.

Next, open the src/app/pages/login/login.component.css file and style the login form as follows:

* {
    box-sizing: border-box;
}

:host {
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: rgba(253, 101, 133, 1);

}

.login-card {
    display: flex;
    flex-direction: column;
    margin: 10px 10px;
    box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.3);
    background-color: #ffffff;
    padding: 10px;
    border-radius: 10px;
    width: 40%;

}

.login-card input {
    margin: 5px 0;
    width: 100%;
    background-color: #e2e2e2;
    border: none;
    outline: none;
    padding: 12px 20px;
    border-radius: 4px;
}

.login-card button {
    background-color: #4796ff;
    color: #ffffff;
    font-size: 16px;
    outline: none;
    border-radius: 5px;
    border: none;
    padding: 8px 15px;
    width: 100%;
}


.card-title h1 {
    font-size: 26px;
    font-weight: bold;
}

Implementing the Signup Component

Now let's implement the signup page. Open the src/app/pages/signup/signup.component.ts file and add the following imports:

import { inject } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { Router, RouterModule } from '@angular/router';
import { AuthService } from '../../auth/auth.service';

Next, add the reactive forms and router modules to the imports array of the signup component:

@Component({
  selector: 'app-signup',
  standalone: true,
  imports: [ReactiveFormsModule, RouterModule],
  templateUrl: './signup.component.html',
  styleUrl: './signup.component.css'
})

Next, inject the authentication and router services:

authService  =  inject(AuthService);
router  =  inject(Router);

Next, create the reactive form for signing up users:

  public signupForm = new FormGroup({
    name: new FormControl('', [Validators.required]),
    email: new FormControl('', [Validators.required, Validators.email]),
    password: new FormControl('', [Validators.required])
  })

Finally, define the onSubmit() method as follows:

  public onSubmit() {
    if (this.signupForm.valid) {
      console.log(this.signupForm.value);
      this.authService.signup(this.signupForm.value)
        .subscribe({
          next: (data: any) => {
            console.log(data);
            this.router.navigate(['/login']);
          },
          error: (err) => console.log(err)
        });
    }
  }

Now open the src/app/pages/signup/signup.component.html file and the HTML form for signup:

<div class="signup-card">
    <div class="card-title">
        <h1>Create Account</h1>
        <span>Already have an account? <a routerLink="/login">Sign In</a></span>
    </div>
    <form class="form-group" [formGroup]="signupForm" (ngSubmit)="onSubmit()">
        <input type="text" name="name" placeholder="Name" formControlName="name">
        <input type="email" name="email" placeholder="Email" formControlName="email">
        <input type="password" name="password" placeholder="Password" formControlName="password">
        <input type="submit" value="Signup">
    </form>
    <div class="card-terms">
        <input type="checkbox"> <span>I have read and agree to the <a href="">Terms of Service</a></span>
    </div>
</div>

Open the src/app/pages/signup/signup.component.css and style the signup page:

* {
    box-sizing: border-box;
}

:host {
    height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background-color: rgba(253, 101, 133, 1);
}

.signup-card {
    border-radius: 10px;
    box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.3);
    width: 40%;
    height: 450px;
    background-color: #ffffff;
    padding: 10px 30px;
}

.card-title {
    padding: 10px;
}

.card-title h1 {
    font-size: 26px;
    font-weight: bold;
}

.form-group input {
    margin: 5px 0;
    width: 100%;
    background-color: #e2e2e2;
    border: none;
    outline: none;
    padding: 12px 20px;
    border-radius: 4px;
}

.form-group input[type="submit"] {
    background-color: #4796ff;
    color: #ffffff;
    font-size: 16px;
    outline: none;
    border-radius: 5px;
    border: none;
    padding: 8px 15px;
    width: 100%;
}

.card-terms {
    display: flex;
    align-items: center;
    padding: 10px;
}

.card-terms span {
    margin: 5px;
    font-size: 13px;
}

Implementing the Admin Page

Open the src/app/pages/admin/admin.component.ts file and update it as follows:

import { Component, inject } from '@angular/core';
import { AuthService } from '../../auth/auth.service';
import { Router } from '@angular/router';

@Component({
  selector: 'app-admin',
  standalone: true,
  imports: [],
  templateUrl: './admin.component.html',
  styleUrl: './admin.component.css'
})
export class AdminComponent {
  authService = inject(AuthService);
  router = inject(Router);
  public logout(){
    this.authService.logout();
    this.router.navigate(['/login']);
  }
}

Open the src/app/pages/admin/admin.component.html file and update it as follows:

<button (click)="logout()">Logout</button>

Open the src/app/pages/admin/admin.component.css file and update it as follows:

:host {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100vh; 
    background-color: rgba(253, 101, 133, 1);
}

button {
    padding: 20px;
    background-color: rgba(60, 10, 107, 0.562);
    border-radius: 10px;
    border: 0;
    outline: none;
    width: 300px;
}

Finally open the src/app/app.component.html file and clear everything then add the router outlet:

<router-outlet></router-outlet>

Then open the src/styles.css file and the following styles:

@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap");

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: "Poppins", sans-serif;
  }

Serve your app using the ng serve command.