Zum Inhalt springen

Building Secure Authentication in Angular with Supabase

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.

Supabase Create New Project Image

  • Enter a project name, and database password, and choose a region.

Supabase New Project Form Image

  • 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).

Supabase Project Overview Image

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.

Angular+Suapbase+Successful+integration

After successful sign-in🎉🎉🎉

Supabase+Angular+Succesful+Login

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! 🅰️⚡

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert