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?
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:
Authentication Flow Overview:
npm install axios @ionic/storage-angular jwt-decode
Ensure your backend API is configured to generate and validate JWT tokens. Here’s a basic Node.js example for reference
Key points:
bcrypt
for secure password storage.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
):
bcrypt.hash(password, 10)
creates a hashed version of the password with 10 salt rounds for added security.3.Login (/api/login
):
4.Token Refresh (/api/refresh
):
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'));
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.Registration Endpoint (/api/register
):
bcrypt.hash(password, 10)
adds a salt to strengthen password security.Login Endpoint (/api/login
):
Refresh Endpoint (/api/refresh
):
jwt.verify()
.Create an AuthService to manage registration, login, token storage, and refreshing tokens using Ionic Storage.
register()
: Sends the registration data to the backend.login()
: Stores accessToken
and refreshToken
in Ionic Storage after successful login.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');
}
}
Storage Initialization:
register()
Method:
/register
endpoint to create a new user.login()
Method:
/login
endpoint.refreshAccessToken()
:
logout()
Method:
Now create the RegisterPage that will allow users to sign up.
Form Data Binding:
[(ngModel)]
binds the input fields to username
and password
properties.onRegister()
Method:
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');
}
}
}
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.