Angular Authentication Using Route Guards
Last Updated :
19 Apr, 2024
In Angular, implementing authentication is important for securing routes and controlling access to different parts of the application based on user authentication status. Angular provides a powerful feature called Route Guards, and among them, the Auth Guard is used to control navigation based on the user's authentication state. In this article, we'll explore how to implement authentication using Auth Guards in Angular applications.
Prerequisites:
Authentication Guards
Auth Guard is a type of Route Guard in Angular that controls navigation to a route based on whether the user is authenticated or not. It intercepts navigation requests and either allows or denies access to certain routes based on predefined conditions. Auth Guards are commonly used where certain routes should only be accessible to authenticated users.
Authentication guards in Angular can be implemented using the CanActivate Interface.
CanActivate Interface
It helps in determining whether a route can be activated or not. Basically it works as gate keeper for the routes.
- Implement the CanActivate interface in a auth-guard service
- Inside method definition return true or false on the basis of current authentication status
- Use CanActivate property in specific routes in app.routes.ts file
Syntax:
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): boolean {
return this.checkAuthenticationStatus();
}
Features of Auth Guards
- Route Protection: Auth Guard provides a mechanism to protect routes in Angular applications, ensuring that only authenticated users can access certain parts of the application.
- Dynamic Routing: Auth Guards support dynamic routing based on the user's authentication status. Depending on whether the user is authenticated or not, the Auth Guard can redirect the user to different routes.
- Integration with Authentication Services: Auth Guards easily integrate with authentication services in Angular applications. They rely on authentication services to determine the authentication status of the user.
- Flexibility: Auth Guards offer flexibility in defining authentication rules and conditions for route access. You can customize Auth Guards to implement various authentication strategies, such as role-based access control (RBAC), token-based authentication, or custom authentication logic.
- Error Handling and Redirects: Auth Guards handle error scenarios and redirects accordingly. In cases where the user is not authenticated and tries to access a protected route, the Auth Guard can redirect the user to a login page or display a customized error message.
Steps to create the application
Step 1: Create the main folder for the project
mkdir auth-guard-application
Step 2: Initialize the node.js project
npm init -y
Step 3: Install the required dependencies
npm install express mongoose cors body-parser jsonwebtoken bcryptjs nodemon
The updated dependencies in package.json file of backend will look like:
"dependencies": {
"bcryptjs": "^2.4.3",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.19.2",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.3.0",
"nodemon": "^3.1.0"
}
Project Structure (Backend):
PROJECT STRUCTURE FOR BACKENDExample: Create the required files as seen on the folder structure and add the following codes.
Node
// authController.js
const User = require("../model/User");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
exports.register = async (req, res) => {
try {
const { username, email, password } = req.body;
let user = await User.findOne({ email });
if (user) {
return res.status(400).json({
message: "User Already Exist",
success: false,
});
}
user = new User({
username: username,
email: email,
password: password,
});
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
await user.save();
const token = generateJwtToken(user.id);
res.status(201).json({
success: true,
token: token,
message: "User registered successfully",
});
} catch (error) {
res.status(500).json({
message: "Server error! New user registration failed",
success: false,
});
}
};
exports.login = async (req, res) => {
try {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user) {
return res.status(400).json({
message: "Invalid credentials",
success: false,
});
}
const isMatched = await bcrypt.compare(password, user.password);
if (!isMatched) {
return res.status(400).json({
message: "Invalid credentials",
success: false,
});
}
const token = generateJwtToken(user.id);
res.status(200).json({
success: true,
message: "User logged in successfully",
token: token,
});
} catch (error) {
return res.status(500).json({
success: false,
message: "Internal Server Error, Login unsuccessful",
});
}
};
function generateJwtToken(userID) {
const payload = {
user: {
id: userID,
},
};
return jwt.sign(payload, "jwtSecret", { expiresIn: 3600 });
}
exports.getUserDetailsFronUserId = async (req, res) => {
try {
const { id } = req.params;
const user = await User.findById(id);
return res.status(200).json(user);
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
};
Node
// User.js - authModel
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true
},
email: {
type: String,
required: true,
uniques: true
},
password: {
type: String,
required: true
}
});
module.exports = mongoose.model('User', userSchema);
Node
// authRoutes.js
const express = require('express');
const router = express.Router();
const authController = require('../controller/authController');
router.post('/register', authController.register);
router.post('/login', authController.login);
router.get('/:id', authController.getUserDetailsFronUserId);
module.exports = router;
Node
// server.js
const express = require("express");
const cors = require("cors");
const mongoose = require("mongoose");
const authRoutes = require("../backend/route/authRoutes");
const app = express();
app.use(cors());
app.use(express.json());
mongoose
.connect("mongodb://localhost:27017/auth-guard", {
family: 4,
})
.then(() => console.log("Mongo DB connected"))
.catch((err) => console.log(err));
app.use("/api/auth", authRoutes);
const PORT = 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
To start the backend run the following command
nodemon server.js
Steps to Create a Angular App and Installing Module
Step 1: Install the angular CLI
npm install -g @angular/cli
Step 2: Create a new angular project
ng new frontend
Step 3: Create components for different functionalities in angular
Syntax - ng generate component <component-name>
ng generate component admin
ng generate component home
ng generate component sidebar
ng generate component user
Step 4: Create services for communication between frontend and backend
Syntax - ng generate service <service-name>
ng generate service auth-guard
ng generate service shared
ng generate service user
Project Structure (Frontend):
PROJECT STRUCTURE FRONTENDExample: Create the required files as seen on the folder structure and add the following codes.
HTML
<!-- user.component.html -->
<div class="error-message" *ngIf="errorMessage">{{ errorMessage }}</div>
<div class="success-message" *ngIf="successMessage">{{ successMessage }}</div>
<div class="container" *ngIf="loginActive">
<h2>Login</h2>
<form (ngSubmit)="login()">
<div class="form-group">
<label for="email">Email:</label>
<input
type="email"
class="form-control"
id="email"
name="email"
[(ngModel)]="email"
required
/>
</div>
<div class="form-group">
<label for="password">Password:</label>
<input
type="password"
class="form-control"
id="password"
name="password"
[(ngModel)]="password"
required
/>
</div>
<button type="submit" class="btn btn-primary" href="">Login</button>
</form>
</div>
<div class="container" *ngIf="registerActive">
<h2>Register</h2>
<form (submit)="register()">
<div class="form-group">
<label for="username">Username</label>
<input
type="text"
id="username"
class="form-control"
[(ngModel)]="username"
name="username"
required
/>
</div>
<div class="form-group">
<label for="email">Email</label>
<input
type="email"
id="email"
class="form-control"
[(ngModel)]="email"
name="email"
required
/>
</div>
<div class="form-group">
<label for="password">Password</label>
<input
type="password"
id="password"
class="form-control"
[(ngModel)]="password"
name="password"
required
/>
</div>
<button type="submit" class="btn btn-primary">Register</button>
</form>
</div>
HTML
<!-- admin.component.html -->
<h1>Welcome to the Admin Page!</h1>
<p>This is another protected route accessible only to authenticated users.</p>
HTML
<!-- home.component.html -->
<h1>Welcome to the Home Page!</h1>
<p>This is an unprotected route accessible to all the users.</p>
HTML
<!-- sidebar.component.html -->
<ul class="nav">
<li><a (click)="loadContent('home')">Home</a></li>
<li><a (click)="loadContent('admin')">Admin</a></li>
</ul>
HTML
<!-- app.component.html -->
<nav class="navbar">
<div class="navbar-title">{{ title }}</div>
<ul class="navbar-menu">
<li><a href="#" (click)="login()" *ngIf="!isLoggedIn">Login</a></li>
<li><a href="#" (click)="register()" *ngIf="!isLoggedIn">Register</a></li>
<li><a href="#" (click)="logout()" *ngIf="isLoggedIn">Logout</a></li>
</ul>
</nav>
<div class="container">
<app-sidebar (contentLoad)="loadContent($event)"></app-sidebar>
<div class="content">
<router-outlet></router-outlet>
</div>
</div>
CSS
/* user.component.css */
.container {
width: 50%;
margin: 2rem auto;
padding: 1.5vmax;
padding-right: 2.5vmax;
border: 1px solid #ccc;
border-radius: 5px;
}
h2 {
text-align: center;
margin-bottom: 20px;
font-size: 2rem;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
}
input[type="text"],
input[type="email"],
input[type="password"] {
width: 97%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
button[type="submit"] {
width: 20%;
padding: 1.1vmax;
background-color: #0056b3;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
font-weight: bold;
font-size: 1rem;
align-self: center;
margin-top: 1vmax;
}
.container {
width: 50%;
margin: 2rem auto;
padding: 1.5vmax;
padding-right: 3.5vmax;
border: 1px solid #ccc;
border-radius: 5px;
}
h2 {
text-align: center;
margin-bottom: 20px;
font-size: 2rem;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
}
input[type="email"],
input[type="password"] {
width: 99%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
button[type="submit"] {
width: 20%;
padding: 1.1vmax;
background-color: #0056b3;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
font-weight: bold;
font-size: 1rem;
align-self: center;
margin-top: 1vmax;
}
.error-message {
color: #FF0000;
background-color: #FFEFEF;
padding: 10px;
border: 1px solid #FF0000;
border-radius: 5px;
margin-bottom: 10px;
}
.success-message {
color: green;
background-color: rgb(185, 231, 185);
padding: 10px;
border: 1px solid green;
border-radius: 5px;
margin-bottom: 10px;
}
CSS
/* sidebar.component.css */
ul {
border: 1px solid lightgray;
margin-top: 0%;
border-radius: 10px;
}
li {
list-style-type: none;
margin: 3vmax;
cursor: pointer;
box-sizing: border-box;
}
li:hover {
color: lightgray;
}
a {
color: lightgray;
text-decoration: none;
}
a:hover {
color: white;
text-decoration: none;
}
CSS
/* app.component.css */
.navbar {
background-color: #333;
color: #fff;
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5vmax 3vmax;
}
.navbar-title {
font-size: 1.7rem;
}
.navbar-menu {
list-style-type: none;
padding: 0;
margin: 0;
}
.navbar-menu li {
display: inline;
margin-right: 2vmax;
font-size: 1.3rem;
}
.navbar-menu li:last-child {
margin-right: 0;
}
.navbar-menu li a {
color: #fff;
text-decoration: none;
}
.navbar-menu li a:hover {
text-decoration: underline;
}
.container {
display: flex;
height: 100%;
min-height: 87vh;
margin-top: 0.4vmax;
}
.content {
flex: 1;
padding: 20px;
}
app-sidebar {
width: 25%;
background-color: #333;
padding: 20px;
color: white;
font-size: 1.4rem;
}
JavaScript
// user.component.ts
import { Component } from "@angular/core";
import { UserService } from "../user.service";
import { Router } from "@angular/router";
import { SharedService } from "../shared.service";
import { FormsModule } from "@angular/forms";
import { CommonModule } from "@angular/common";
@Component({
selector: "app-user",
standalone: true,
imports: [FormsModule, CommonModule],
templateUrl: "./user.component.html",
styleUrl: "./user.component.css",
})
export class UserComponent {
username!: string;
email!: string;
password!: string;
credentials: any = {};
successMessage: string = "";
errorMessage: string = "";
loginActive: boolean = true;
registerActive: boolean = false;
constructor(
private authService: UserService,
private router: Router,
private sharedService: SharedService
) { }
ngOnInit(): void {
this.sharedService.loginEvent.subscribe(() => {
this.errorMessage = "";
this.successMessage = "";
this.loginActive = true;
this.registerActive = false;
});
this.sharedService.registerEvent.subscribe(() => {
this.errorMessage = "";
this.successMessage = "";
this.registerActive = true;
this.loginActive = false;
});
}
login(): void {
const credentials = {
email: this.email,
password: this.password,
};
this.authService.login(credentials).subscribe(
(response: any) => {
const token = response.token;
localStorage.setItem("token", token);
this.authService.emitLoggedInEvent();
this.loginActive = false;
this.registerActive = false;
this.email = "";
this.password = "";
this.successMessage = response.message;
},
(error: any) => {
console.error("Error logging in:", error);
this.errorMessage =
"Login unsuccessfull ! Please reload or try in incognito tab";
}
);
this.errorMessage = "";
this.successMessage = "";
this.email = "";
this.password = "";
}
register(): void {
const userData = {
username: this.username,
email: this.email,
password: this.password,
};
this.authService.register(userData).subscribe(
(response: any) => {
this.successMessage = response.message;
this.loginActive = true;
this.registerActive = false;
this.username = "";
this.email = "";
this.password = "";
},
(error: any) => {
console.error(error);
this.errorMessage = "User not registered successfully";
}
);
this.username = "";
this.email = "";
this.password = "";
}
}
JavaScript
// admin.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-admin',
standalone: true,
imports: [],
templateUrl: './admin.component.html',
styleUrl: './admin.component.css'
})
export class AdminComponent {
}
JavaScript
// sidebar.component.ts
import { Component, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'app-sidebar',
standalone: true,
imports: [],
templateUrl: './sidebar.component.html',
styleUrl: './sidebar.component.css'
})
export class SidebarComponent {
isLoggedIn: boolean = true;
constructor() { }
ngOnInit(): void {
if (typeof localStorage !== 'undefined') {
const token = localStorage.getItem('token');
if (token) {
this.isLoggedIn = true;
}
}
}
@Output() contentLoad = new EventEmitter<string>();
loadContent(page: any) {
console.log("Event emit");
this.contentLoad.emit(page);
}
}
JavaScript
// home.compoennt.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-home',
standalone: true,
imports: [],
templateUrl: './home.component.html',
styleUrl: './home.component.css'
})
export class HomeComponent {
}
JavaScript
// app.component.ts
import { Component } from "@angular/core";
import { Router, RouterOutlet } from "@angular/router";
import { UserService } from "./component/user.service";
import { SharedService } from "./component/shared.service";
import { FormsModule } from "@angular/forms";
import { CommonModule } from "@angular/common";
import { SidebarComponent } from "./component/sidebar/sidebar.component";
@Component({
selector: "app-root",
standalone: true,
imports: [RouterOutlet, FormsModule, CommonModule, SidebarComponent],
templateUrl: "./app.component.html",
styleUrl: "./app.component.css",
})
export class AppComponent {
title = "AuthGuard in Angular";
isLoggedIn: boolean = false;
constructor(
private router: Router,
private authService: UserService,
private sharedService: SharedService
) { }
ngOnInit(): void {
this.authService.loggedInEvent.subscribe((data: any) => {
this.isLoggedIn = true;
});
if (typeof localStorage !== "undefined" && localStorage.getItem("token")) {
this.isLoggedIn = true;
}
}
login(): void {
this.sharedService.triggerLoginEvent();
this.router.navigate(["/"]);
}
register(): void {
this.sharedService.triggerRegisterEvent();
this.router.navigate(["/"]);
}
logout(): void {
this.isLoggedIn = false;
localStorage.removeItem("token");
this.router.navigate(["/"]);
}
loadContent(page: any) {
if (page === "home") {
this.router.navigate(["/home"]);
} else if (page === "admin") {
this.router.navigate(["/admin"]);
}
}
}
JavaScript
// app.routes.ts
import { Routes } from '@angular/router';
import { UserComponent } from './component/user/user.component';
import { HomeComponent } from './component/home/home.component';
import { AuthGuardService } from './component/auth-guard.service';
import { AdminComponent } from './component/admin/admin.component';
export const routes: Routes = [
{ path: '', component: UserComponent },
{ path: 'home', component: HomeComponent },
{ path: 'admin', component: AdminComponent, canActivate: [AuthGuardService] },
];
JavaScript
// app.module.ts
import { InjectionToken, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { JwtHelperService, JWT_OPTIONS } from '@auth0/angular-jwt';
import { RouterModule } from '@angular/router';
import { routes } from './app.routes';
import { UserComponent } from './component/user/user.component';
import { HomeComponent } from './component/home/home.component';
import { AdminComponent } from './component/admin/admin.component';
@NgModule({
declarations: [
AppComponent,
UserComponent,
HomeComponent,
AdminComponent
],
imports: [
BrowserModule,
FormsModule,
RouterModule.forRoot(routes),
],
exports: [RouterModule],
providers: [{ provide: JWT_OPTIONS, useValue: JWT_OPTIONS }, JwtHelperService],
bootstrap: [AppComponent]
})
export class AppModule { }
JavaScript
// user.service.ts
import { HttpClient } from "@angular/common/http";
import { EventEmitter, Injectable } from "@angular/core";
import { Observable, tap } from "rxjs";
@Injectable({
providedIn: "root",
})
export class UserService {
private baseUrl = "http://localhost:5000/api/auth";
private readonly TOKEN_KEY = "token";
constructor(private httpClient: HttpClient) { }
register(userData: any): Observable<any> {
return this.httpClient.post(`${this.baseUrl}/register`, userData);
}
login(credentials: any): Observable<any> {
return this.httpClient.post(`${this.baseUrl}/login`, credentials).pipe(
tap((response: any) => {
if (response && response.token) {
localStorage.setItem(this.TOKEN_KEY, response.token);
this.emitLoggedInEvent();
}
})
);
}
loggedInEvent: EventEmitter<any> = new EventEmitter();
emitLoggedInEvent() {
this.loggedInEvent.emit();
}
getToken(): string | null {
if (typeof window !== "undefined") {
return localStorage.getItem(this.TOKEN_KEY);
}
return null;
}
isAuthenticatedUser(): boolean {
return !!this.getToken();
}
}
JavaScript
// shared.service.ts
import { EventEmitter, Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class SharedService {
loginEvent: EventEmitter<void> = new EventEmitter<void>();
registerEvent: EventEmitter<void> = new EventEmitter<void>();
constructor() { }
triggerLoginEvent(): void {
this.loginEvent.emit();
}
triggerRegisterEvent(): void {
this.registerEvent.emit();
}
}
JavaScript
// auth-guard.service.ts
import { Injectable } from '@angular/core';
import { UserService } from './user.service';
import { Router } from '@angular/router';
@Injectable({
providedIn: 'root'
})
export class AuthGuardService {
constructor(private authService: UserService, private router: Router) { }
canActivate(): boolean {
return this.checkLoggedIn();
}
private checkLoggedIn(): boolean {
if (this.authService.isAuthenticatedUser()) {
return true;
} else {
this.router.navigate(['/']);
return false;
}
}
}
Output:
OUTPUT GIF
Similar Reads
Get Updated List using Angular Signal
Angular Signal is a new feature introduced in Angular 16 that provides a way to handle state management and reactive data flow within Angular applications. It offers a simpler and more efficient alternative to traditional state management solutions like RxJS Observables and NgRx. PrerequisitesNode
2 min read
How to use AuthGuard For Angular 17 routes?
In Angular applications, it is often necessary to protect certain routes to prevent unauthorized access. The Angular Router provides a feature called Route Guards, which allows you to control access to routes based on specific conditions. One of the commonly used Route Guards is AuthGuard, which che
6 min read
Routing in Angular JS using Angular UI Router
AngularJS is a front-end web application framework based on JavaScript and is maintained by Google. AngularJS interprets the attributes of HTML as directives to bind input/output components to a model represented by standard JavaScript variables. Pre-requisites: HTML CSS JavaScript AngularJS Angular
3 min read
Auth Guards in Angular 9/10/11
AuthGuard is used to protect the routes from unauthorized access in angular. How AuthGuard Works? Auth guard provide lifecycle event called canActivate. The canActivate is like a constructor. It will be called before accessing the routes. The canActivate has to return true to access the page. If it
3 min read
Component Communication in Angular
Angular, as a robust front-end framework, allows you to build complex and interactive web applications by creating reusable components. One of the key aspects of building such applications is effective communication between these components. There are a lot of instances where we need to transfer dat
12 min read
How to Get All Route Params/Data in Angular?
In Angular, managing route parameters and extracting data from routes is important for creating dynamic and responsive web applications. Route parameters allow you to pass information in URLs, and accessing this data enables your application to make decisions and render content dynamically. This art
4 min read
Deployment of Angular Application using Github Pages
There are various methods to deploy Angular Application such as Github Pages, Heroku, Firebase, etc. The Github provides the simplest way of all using the Github Pages. Steps to Create and Deploy Sample Angular Application to Github Pages: Install Node.js: a. Windows b. LinuxInstall Angular - CLICre
2 min read
How to Use Lodash in Angular Applications?
Lodash is an open source library that helps in performing operations on various data structures like arrays, or objects, in a seamless and efficient way. When used with javascript frameworks, like angular, it can ease the life of developers while writing functions to manipulate arrays or objects. In
2 min read
How do you configure routes in Angular?
In Angular, configuring routes is an important part of building single-page applications (SPAs) and managing navigation between different components. By defining routes, we can map URLs to specific components, enabling users to access different views within our application. This enhances user experi
5 min read
A Complete Guide To Angular Routing
Angular Routing is a technology to build single-page applications that provide multi-page services at a single port. It enables seamless navigation and loading of the different pages. When an Angular app is started then the whole component is only loaded at once and dynamically reloads the requested
6 min read