CodeB-logo
+1 210 864-9027
+91 (810)-812-6332
We are here to help you
CodeB-logo

Building an Ionic JWT Refresh Token Flown

akash.png
Akash MoreSoftware Developerauthor linkedin
Published On
Updated On
Table of Content
up_arrow

Introduction

JSON Web Tokens (JWT) have become a standard method for authentication in modern web and mobile applications. However, managing token lifecycle and security can be challenging. In this blog post, we'll dive deep into implementing a robust JWT refresh token flow in an Ionic application.

Why Use JWT for Authentication?

  • Stateless Authentication: No need for the server to maintain session data.
  • Compact Tokens: JWTs are lightweight and can be sent via HTTP headers.
  • Security: JWTs are signed, ensuring data integrity and protection against tampering.

Understanding JWT and Refresh Tokens A JSON Web Token is a compact, URL-safe means of representing claims to be transferred between two parties. It consists of three parts:

  • Header: Contains the token type and hashing algorithm
  • Payload: Stores claims or user information
  • Signature: Ensures the token's integrity

Authentication Flow Overview:

  • Registration: Users create an account by providing a username and password.
  • Login: Users log in with their credentials to receive an access token and a refresh token.
  • Token Storage: Access and refresh tokens are stored securely on the client-side.
  • Token Refresh: The client uses the refresh token to obtain a new access token when it expires.
  • Logout: Clears tokens and ends the session.
  • Install Required Packages:

    • axios: For making API requests.
    • @ionic/storage-angular: For secure local storage of tokens.
    • jwt-decode: To decode JWTs and check expiration.

    npm install axios @ionic/storage-angular jwt-decode
    

    Backend Setup (JWT Generation and Validation)

    Ensure your backend API is configured to generate and validate JWT tokens. Here’s a basic Node.js example for reference

    Key points:

    1. Password Hashing: Using bcrypt for secure password storage.
    2. JWT Tokens: Short-lived access tokens (15m) and long-lived refresh tokens (7d).

    1. Express js setup:

    We use Express.js to create the server and handle API routes. app.use(cors()) allows cross-origin requests from the Ionic app.

    2. User Registration (/api/register):

    • We hash the password using bcrypt for security before storing it.
    • bcrypt.hash(password, 10) creates a hashed version of the password with 10 salt rounds for added security.

    3.Login (/api/login):

    • Checks if the user exists and verifies the password.
    • Generates two JWTs: Access Token & Refresh Token.

    4.Token Refresh (/api/refresh):

    • Verifies the refresh token and issues a new access token.
    const express = require('express');
    const jwt = require('jsonwebtoken');
    const cors = require('cors');
    const bcrypt = require('bcrypt');
    
    const app = express();
    app.use(express.json());
    app.use(cors());
    
    const SECRET_KEY = 'your_secret_key';
    const users = []; // In-memory user storage (replace with a database in production)
    
    // Register Endpoint
    app.post('/api/register', async (req, res) => {
      const { username, password } = req.body;
      const hashedPassword = await bcrypt.hash(password, 10);
      users.push({ username, password: hashedPassword });
      res.status(201).send('User registered successfully');
    });
    
    // Login Endpoint
    app.post('/api/login', async (req, res) => {
      const { username, password } = req.body;
      const user = users.find((u) => u.username === username);
      if (user && (await bcrypt.compare(password, user.password))) {
        const accessToken = jwt.sign({ username }, SECRET_KEY, { expiresIn: '15m' });
        const refreshToken = jwt.sign({ username }, SECRET_KEY, { expiresIn: '7d' });
        res.json({ accessToken, refreshToken });
      } else {
        res.status(401).send('Invalid credentials');
      }
    });
    
    // Refresh Token Endpoint
    app.post('/api/refresh', (req, res) => {
      const { refreshToken } = req.body;
      if (!refreshToken) return res.sendStatus(403);
      jwt.verify(refreshToken, SECRET_KEY, (err, user) => {
        if (err) return res.sendStatus(403);
        const newAccessToken = jwt.sign({ username: user.username }, SECRET_KEY, { expiresIn: '15m' });
        res.json({ accessToken: newAccessToken });
      });
    });
    
    app.listen(3000, () => console.log('Server running on port 3000'));
    
    1. Express Server Setup:

      • express() initializes the app.
      • app.use(cors()) allows cross-origin requests from the Ionic client.
      • app.use(express.json()) parses incoming JSON payloads.
    2. Registration Endpoint (/api/register):

      • bcrypt hashes passwords before storing them, ensuring sensitive data is secure.
      • bcrypt.hash(password, 10) adds a salt to strengthen password security.
    3. Login Endpoint (/api/login):

      • Users provide a username and password.
      • If credentials are valid, two JWTs are issued:
    4. Refresh Endpoint (/api/refresh):

      • Takes a refresh token and validates it using jwt.verify().
      • Issues a new access token to maintain the session without requiring re-login.

    Integrating AuthService in Ionic App

    Create an AuthService to manage registration, login, token storage, and refreshing tokens using Ionic Storage.

    1. Dependency Injection: We inject Storage to store tokens locally on the device.
    2. register(): Sends the registration data to the backend.
    3. login(): Stores accessToken and refreshToken in Ionic Storage after successful login.
    4. logout(): Clears stored tokens from the device.
    import { Injectable } from '@angular/core';  
    import axios from 'axios';  
    import { Storage } from '@ionic/storage-angular';  
    
    @Injectable({ providedIn: 'root' })  
    export class AuthService {  
      private API_URL = 'http://localhost:3000/api';  
      private storage: Storage | null = null;  
    
      constructor(private storageCtrl: Storage) {  
        this.init();  
      }  
    
      async init() {  
        this.storage = await this.storageCtrl.create();  
      }  
    
      // Register  
      async register(username: string, password: string) {  
        try {  
          await axios.post(`${this.API_URL}/register`, { username, password });  
          console.log('User registered successfully');  
        } catch (error) {  
          console.error('Registration failed:', error.response.data);  
        }  
      }  
    
      // Login  
      async login(username: string, password: string) {  
        try {  
          const response = await axios.post(`${this.API_URL}/login`, { username, password });  
          await this.storage?.set('accessToken', response.data.accessToken);  
          await this.storage?.set('refreshToken', response.data.refreshToken);  
          console.log('Login successful');  
        } catch (error) {  
          console.error('Login failed:', error.response.data);  
        }  
      }  
    
      // Logout  
      async logout() {  
        await this.storage?.remove('accessToken');  
        await this.storage?.remove('refreshToken');  
      }  
    }  
    
    1. Storage Initialization:

      • We initialize Ionic Storage to store tokens securely on the client device.
    2. register() Method:

      • Calls the backend /register endpoint to create a new user.
    3. login() Method:

      • Sends the username and password to the backend /login endpoint.
      • Stores both the access and refresh tokens locally.
    4. refreshAccessToken():

      • Retrieves the refresh token and uses it to request a new access token when the current one expires.
    5. logout() Method:

      • Clears the stored tokens, effectively logging the user out.

    Building the Registration Page in Ionic

    Now create the RegisterPage that will allow users to sign up.

    1. Form Data Binding:

      • [(ngModel)] binds the input fields to username and password properties.
    2. onRegister() Method:

      • Calls AuthService.register() with the provided credentials.

    <ion-header>  
      <ion-toolbar>  
        <ion-title>Register</ion-title>  
      </ion-toolbar>  
    </ion-header>  
    
    <ion-content>  
      <form (submit)="onRegister()">  
        <ion-item>  
          <ion-label position="floating">Username</ion-label>  
          <ion-input type="text" [(ngModel)]="username" name="username"></ion-input>  
        </ion-item>  
        <ion-item>  
          <ion-label position="floating">Password</ion-label>  
          <ion-input type="password" [(ngModel)]="password" name="password"></ion-input>  
        </ion-item>  
        <ion-button expand="full" type="submit">Register</ion-button>  
      </form>  
    </ion-content>  
    
    import { Component } from '@angular/core';  
    import { AuthService } from '../services/auth.service';  
    
    @Component({ selector: 'app-register', templateUrl: './register.page.html' })  
    export class RegisterPage {  
      username: string = '';  
      password: string = '';  
    
      constructor(private authService: AuthService) {}  
    
      async onRegister() {  
        if (this.username && this.password) {  
          await this.authService.register(this.username, this.password);  
          console.log('Registration successful');  
        } else {  
          console.error('All fields are required');  
        }  
      }  
    }  
    

    Conclusion

    By the end of this guide, you've set up a fully functional Ionic authentication system using JWTs and Node.js. You now have a solid foundation for managing user sessions, refreshing tokens, and building secure Ionic applications.

    Schedule a call now
    Start your offshore web & mobile app team with a free consultation from our solutions engineer.

    We respect your privacy, and be assured that your data will not be shared