Auth: Login & Signup in Angular 18

Auth: Login & Signup in Angular 18

In this step by step tutorial, we will see by example how to build login and signup pages using Angular 18. We'll leverage HTML and CSS to design the pages, and Angular code to implement the functionality. By taking a reactive approach, we'll manage form data and submit it to the authentication endpoint using Angular's HttpClient module.

Setting Up the Authentication Backend

Welcome to our tutorial on building login and signup pages with Angular 18! In this guide, we'll walk you through the process of building authentication functionality within an Angular 18 application.

To kickstart our project, we'll use a robust authentication backend provided by a Node.js application available at https://github.com/techiediaries/node-mongoose-jwt. This backend app is powered by MongoDB and implements JSON Web Tokens (JWT) for secure user authentication. Before proceeding, make sure you have MongoDB installed on your system.

To get started, clone the repository for the authentication backend, navigate into its directory, and follow the instructions below to set it up:

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 18 Authentication Frontend

Are you ready to embark on the journey of building a robust frontend for your Angular 18 application? Let's dive in and harness the power of Angular to create dynamic login, signup, and admin pages.

To begin, open a new terminal window and initiate a new Angular 18 project using the Angular CLI's ng new command. This command sets up a new Angular 18 workspace with all the necessary files and dependencies to kickstart your development process. 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

In our quest to develop a robust authentication system for our Angular 18 application, there's a crucial step awaiting us: the creation of an authentication service and guard. These essential components play a pivotal role in managing user authentication and securing our application's routes. Run 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

As we continue our journey of building a robust authentication system with Angular 18, it's time to configure our application routes. Routing plays a crucial role in guiding users through different pages and ensuring seamless navigation within our application. Additionally, we'll implement route protection to restrict access to certain components, such as the admin panel, to authenticated users only. 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

As we continue to build our Angular 18 application with a focus on authentication, it's essential to establish communication with our backend authentication server seamlessly. To facilitate this interaction, we'll leverage Angular's HttpClient service, a powerful tool for making HTTP requests and handling responses.

Before diving into communication with the authentication backend, we must ensure that our Angular application is equipped with the necessary dependencies and configurations to use the HttpClient service effectively. This involves providing the HttpClient module within our application setup.

To accomplish this, we'll navigate to the src/app/app.config.ts file and configure the required imports and dependencies. By incorporating the HttpClient module into our application configuration, we lay the groundwork for seamless communication with the authentication backend.

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

Continuing our journey towards building a secure authentication system in Angular 18, our next step is to implement the authentication guard. The authentication guard serves as a crucial line of defense, ensuring that only authenticated users can access protected routes within our application. 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 instructs Angular 18 to automatically redirect users to the login page if they attempt to access protected routes without being logged in.

Adding the Login Component

Embarking on the implementation of our Login Component marks a significant stride in crafting a comprehensive authentication system within our Angular 18 application. The Login Component serves as the gateway for users to access secured areas of our application, requiring authentication credentials for entry. 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;
}

Adding the Signup Component

With the groundwork laid for our authentication system with Angular 18, it's time to turn our attention to the implementation of the Signup Component. This pivotal component empowers users to create new accounts within our application, enabling them to access its features and functionalities securely. 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

As our authentication system takes shape, it's time to introduce the Admin Page—a central hub where authorized users can manage privileged functionalities and access exclusive features within our Angular 18 application.

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.

Conclusion

In summary, we've embarked on the development journey of building a comprehensive authentication system within our Angular 18 application. Our focus has shifted through the implementation of key components, including the Login Component, Signup Component, and Admin Page. Each component leverages Angular's capabilities to create intuitive user interfaces, facilitate secure authentication processes, and provide elevated privileges for authorized users.


  • Date: