Hey Angular developers! 🅰️ It’s about that time again for yet another article. In this guide, we’re going to dive into how to integrate Supabase, with Angular to build a secure authentication application. We’ll set up signup, and login authentication with Supabase in Angular and protect routes of some pages. Let’s get started
Prerequisites
Before we dive in, make sure you’ve got:
- An Angular project
- Node.js v16+ installed
- A Supabase account at Supabase Website
Step 1: Set Up a Supabase Project
Create a New Project:
- Log in to Supabase and click New Project.
- Enter a project name, and database password, and choose a region.
- Wait a few minutes for the project to provision.
Retrieve API Credentials:
On the project overview section, Copy the Project URL and anon public (API Key).
Step 2: Create an Angular Project
- Generate a New Angular App:
ng new supabase-auth-app
Choose your preferred styling when prompted.
- Install Dependencies: Install the Supabase JavaScript client:
npm install @supabase/supabase-js
Step 3: Configure Supabase in Angular
- Set Up Environment Variables: Store the Supabase credentials securely in Angular’s environment files.
Open src/environments/environment.ts and add:
export const environment = {
production: false,
supabaseUrl: 'SUPABASE_URL',
supabaseKey: 'SUPABASE_ANON_KEY'
};
Replace SUPABASE_URL and SUPABASE_ANON_KEY with values from the Supabase dashboard.
- Create Type Definitions: In src/app/types/user.type.ts, define types for user data and payloads:
export type User = {
id: string;
email?: string;
phone?: string;
user_metadata: {
displayName?: string;
};
};
export type SignupPayload = {
name: string;
email: string;
password: string;
};
export type LoginPayload = {
email: string;
password: string;
};
- Create a Supabase Service: Generate a service to manage Supabase interactions:
ng generate service services/supabase
Update src/app/services/supabase.service.ts to initialize the Supabase client and handle authentication:
import { Injectable } from '@angular/core';
import { createClient, SupabaseClient } from '@supabase/supabase-js';
import { environment } from '../../environments/environment';
import { LoginPayload, SignupPayload } from '../types/user.type';
@Injectable({
providedIn: 'root',
})
export class SupabaseService {
private readonly supabase: SupabaseClient;
constructor() {
this.supabase = createClient(
environment.supabaseUrl,
environment.supabaseKey
);
}
async signInWithEmail(payload: LoginPayload) {
return await this.supabase.auth.signInWithPassword({
email: payload.email,
password: payload.password,
});
}
async signUpWithEmail(payload: SignupPayload) {
return await this.supabase.auth.signUp({
email: payload.email,
password: payload.password,
options: {
data: {
displayName: payload.name,
},
},
});
}
async getUser() {
return await this.supabase.auth.getUser();
}
async signOut() {
return await this.supabase.auth.signOut();
}
}
The SupabaseService centralizes authentication logic, making it reusable across components. It handles initialization, user state, and auth methods, ensuring clean and maintainable code.
Step 4: Set Up Authentication Components
- We’ll create components for login, signup, and profile pages.
Generate Components:
ng generate component auth/login
ng generate component auth/signup
ng generate component profile
- Login Component: Update src/app/auth/login/login.component.ts to handle email/password login:
import { Component, inject, signal } from '@angular/core';
import { SupabaseService } from '../../services/supabase.service';
import { Router } from '@angular/router';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-login',
imports: [FormsModule],
templateUrl: './login.component.html',
styleUrl: './login.component.css',
})
export class LoginComponent {
email = signal('');
password = signal('');
private readonly supabaseService = inject(SupabaseService);
private readonly router = inject(Router);
async signInWithEmail() {
const { error } = await this.supabaseService.signInWithEmail({
email: this.email(),
password: this.password(),
});
if (error) {
alert('Error signing in: ' + error.message);
} else {
this.router.navigate(['/profile']);
}
}
navigateToSignup() {
this.router.navigate(['/signup']);
}
}
- Update src/app/auth/login/login.component.html:
<div class="container">
<h2>Login</h2>
<form>
<input
type="email"
[(ngModel)]="email"
name="email"
placeholder="Email"
required
/>
<input
type="password"
[(ngModel)]="password"
name="password"
placeholder="Password"
required
/>
<button type="button" (click)="signInWithEmail()">Sign In</button>
</form>
<p>Don't have an account? <a (click)="navigateToSignup()">Sign Up</a></p>
</div>
- Add styling(optional), in src/app/auth/login/login.component.css:
.container {
max-width: 400px;
margin: 50px auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 5px;
}
input, button {
display: block;
width: 100%;
margin: 10px 0;
padding: 10px;
}
button {
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
- Signup Component: Update src/app/auth/signup/signup.component.ts:
import { Component, inject, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { SupabaseService } from '../../services/supabase.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-signup',
imports: [FormsModule],
templateUrl: './signup.component.html',
styleUrl: './signup.component.css',
})
export class SignupComponent {
name = signal('');
email = signal('');
password = signal('');
private readonly supabaseService = inject(SupabaseService);
private readonly router = inject(Router);
async signUp() {
const { error } = await this.supabaseService.signUpWithEmail({
name: this.name(),
email: this.email(),
password: this.password(),
});
if (error) {
alert('Error signing up: ' + error.message);
} else {
alert('Check your email to confirm your account!');
this.router.navigate(['/login']);
}
}
navigateToLogin() {
this.router.navigate(['/login']);
}
}
- Update src/app/auth/signup/signup.component.html:
<div class="container">
<h2>Sign Up</h2>
<form>
<input
type="text"
[(ngModel)]="name"
name="name"
placeholder="Full Name"
required
/>
<input
type="email"
[(ngModel)]="email"
name="email"
placeholder="Email"
required
/>
<input
type="password"
[(ngModel)]="password"
name="password"
placeholder="Password"
required
/>
<button type="button" (click)="signUp()">Sign Up</button>
</form>
<p>Already have an account? <a (click)="navigateToLogin()">Log In</a></p>
</div>
Use the same CSS as the login component (signup.component.css).
- Profile Component: Update src/app/profile/profile.component.ts:
import { Component, inject, OnInit } from '@angular/core';
import { SupabaseService } from '../services/supabase.service';
import { Router } from '@angular/router';
import { User } from '../types/user.type';
@Component({
selector: 'app-profile',
imports: [],
templateUrl: './profile.component.html',
styleUrl: './profile.component.css',
})
export class ProfileComponent implements OnInit {
user: User | null = null;
private readonly supabaseService = inject(SupabaseService);
private readonly router = inject(Router);
ngOnInit() {
this.fetchUser();
}
async fetchUser() {
const { data } = await this.supabaseService.getUser();
this.user = data.user;
if (!this.user) {
this.router.navigate(['/login']);
}
}
async signOut() {
await this.supabaseService.signOut();
this.router.navigate(['/login']);
}
}
- Update
src/app/profile/profile.component.html
:
<div class="container">
<h2>User Profile</h2>
<p>
Congratulations! Welcome to secure auth with Angular + Supabase is running.
🎉
</p>
@if (user) {
<p>User ID: {{ user.id }}</p>
<p>Email: {{ user.email }}</p>
<p>Display Name: {{ user.user_metadata.displayName }}</p>
}
<button (click)="signOut()">Sign Out</button>
</div>
Step 5: Configure Routing and Route Protection
- Create an Auth Guard: Generate a guard:
ng generate guard guards/auth
- Update src/app/guards/auth.guard.ts:
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { SupabaseService } from '../services/supabase.service';
export const authGuard: CanActivateFn = async (route, state) => {
const supabaseService = inject(SupabaseService);
const router = inject(Router);
const user = await supabaseService.getUser();
if (!user) {
router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
return false;
}
return true;
};
- Set Up Routes: Update src/app/app.routes.ts to use lazy loading and protect the profile route:
import { Routes } from '@angular/router';
import { authGuard } from './guards/auth.guard';
export const routes: Routes = [
{ path: '', redirectTo: '/login', pathMatch: 'full' },
{
path: 'login',
loadComponent: () =>
import('./auth/login/login.component').then((c) => c.LoginComponent),
},
{
path: 'signup',
loadComponent: () =>
import('./auth/signup/signup.component').then((c) => c.SignupComponent),
},
{
path: 'profile',
loadComponent: () =>
import('./profile/profile.component').then((c) => c.ProfileComponent),
canActivate: [authGuard],
},
];
Run Application
Using the command:
ng serve -o
This will run and open the application in the default browser.
After successful sign-in🎉🎉🎉
Conclusion
We’ve just built a secure authentication system in Angular using Supabase, complete with email/password signup, login, and route protection.
This setup is a solid foundation for more advanced features like role-based access, password resets, or social logins, all supported by Supabase.
Access the full source code on GitHub.
For more customization and ideas, explore the Supabase Docs.
Happy coding! 🅰️⚡