Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

15 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

AuthGateway - Identity Provider (IDP) Facade

πŸ“‹ Overview

AuthGateway is an authentication API developed in .NET 9 that acts as a Facade for the Supabase identity provider. The project provides a centralized abstraction layer that simplifies integration with authentication services, enabling authentication operations without directly exposing the underlying IDP's complexity.

Main Functionality

This project implements the Facade pattern to:

  • βœ… Abstract the complexity of Supabase GoTrue
  • βœ… Provide a unified interface for authentication operations
  • βœ… Simplify future identity provider changes
  • βœ… Centralize authentication and validation logic
  • βœ… Securely manage JWT tokens

πŸ—οΈ Architecture

The project follows a layered architecture with clear separation of concerns:

AuthGateway/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ API/                          # Presentation Layer (FastEndpoints)
β”‚   β”‚   β”œβ”€β”€ Features/                 # REST Endpoints
β”‚   β”‚   β”‚   β”œβ”€β”€ Auth/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ SignUp/          # Register new user
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ SignIn/          # Login user
β”‚   β”‚   β”‚   β”‚   └── SignInFromRefreshToken/  # Renew tokens
β”‚   β”‚   β”‚   └── Users/               # User operations
β”‚   β”‚   β”œβ”€β”€ Security/                # JWT validation and security
β”‚   β”‚   β”œβ”€β”€ Program.cs               # Application configuration
β”‚   β”‚   └── appsettings.json         # Configuration files
β”‚   β”‚
β”‚   β”œβ”€β”€ application/                 # Application Layer (Use Cases)
β”‚   β”‚   β”œβ”€β”€ Contracts/               # Interfaces (IAuthService)
β”‚   β”‚   └── Features/                # Business use cases
β”‚   β”‚       β”œβ”€β”€ Auth/                # Authentication use cases
β”‚   β”‚       └── Profiles/            # Profile operations
β”‚   β”‚
β”‚   β”œβ”€β”€ domain/                      # Domain Layer (Entities)
β”‚   β”‚   └── Entities/
β”‚   β”‚       └── Profiles/            # Domain models
β”‚   β”‚
β”‚   └── Infrastructure/              # Infrastructure Layer
β”‚       β”œβ”€β”€ DependencyInjection.cs  # IoC Container
β”‚       β”œβ”€β”€ Configuration/           # Supabase Factory
β”‚       β”œβ”€β”€ Security/                # JWT/JWKS Validation
β”‚       └── Application/
β”‚           └── Contracts/           # Implementations (SupabaseAuthService)

Design Patterns Used

  • Facade Pattern: IAuthService and SupabaseAuthService abstract Supabase
  • Dependency Injection: IoC Container for dependency management
  • Clean Architecture: Clear separation of concerns
  • SOLID Principles: Interfaces and dependency injection

πŸ” How the Facade Works

The Concept

The Facade is a pattern that provides a simplified interface to a complex subsystem. In this project:

// Without Facade (complex):
var client = new Supabase.Client(...)
var response = await client.Auth.SignUp(email, password, options);
// ... validate responses, handle exceptions, manage tokens...

// With Facade (simple):
await authService.SignUpAsync(email, password, metadata);
// Complexity is encapsulated in SupabaseAuthService

Authentication Flow

HTTP Client
    ↓
[API - FastEndpoints]  (Presentation Layer)
    ↓
[SignUpUseCase / SignInUseCase]  (Application Layer)
    ↓
[IAuthService Interface]  (Contract - Abstraction)
    ↓
[SupabaseAuthService]  (Implementation - Facade)
    ↓
[Supabase GoTrue]  (Real Identity Provider)

Facade Benefits

  1. Provider Independence: Switching from Supabase to another IDP requires only implementing IAuthService
  2. Security: Centralized validations and conversions
  3. Reusability: IAuthService can be injected anywhere
  4. Testability: Easy to create mocks for unit testing

πŸš€ Main Features

1. Sign Up (Registration)

POST /Auth/SignUp
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "secure_password",
  "userMetadata": {
    "firstName": "John",
    "lastName": "Silva"
  }
}

Response:

{
  "accessToken": "eyJhbGc...",
  "refreshToken": "refresh_token..."
}

2. Sign In (Login)

POST /Auth/SignIn
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "secure_password"
}

3. Refresh Token (Renew Session)

POST /Auth/SignInFromRefreshToken
Content-Type: application/json

{
  "refreshToken": "refresh_token..."
}

Generates new accessToken and refreshToken without requiring credentials.

4. Get Profile By ID (Get User Profile)

GET /Users/GetProfileById/{id}
Authorization: Bearer {accessToken}

Requires valid JWT authentication.


πŸ”§ Configuration

Required Environment Variables

// appsettings.Development.json
{
  "Supabase": {
    "Url": "https://your-project.supabase.co",
    "AnonKey": "your-anonymous-key",
    "ServiceRoleKey": "your-service-role-key",
    "JwksUrl": "https://your-project.supabase.co/auth/v1/jwks",
    "Issuer": "https://your-project.supabase.co/auth/v1",
    "Audience": "authenticated"
  }
}

Dependency Installation

# Navigate to project directory
cd /home/alberth/RiderProjects/AuthGateway

# Restore NuGet packages
dotnet restore

# Build the solution
dotnet build

# Run the API
dotnet run --project src/API

πŸ“¦ Main Dependencies

Package Version Purpose
Supabase 1.1.1 Official Supabase client
FastEndpoints 5.35.0 REST framework alternative to ASP.NET MVC
FastEndpoints.Swagger 5.35.0 OpenAPI/Swagger documentation
Microsoft.AspNetCore.Authentication.JwtBearer 9.0.11 JWT validation
.NET 9.0 Base framework

πŸ”’ Security

JWT Validation

The API automatically validates JWT tokens using:

  1. JWKS (JSON Web Key Set): Obtains public keys from Supabase
  2. IssuerSigningKeyResolver: Resolves keys by kid (Key ID)
  3. Validation Parameters:
    • Valid issuer
    • Correct audience
    • Expiration time
    • Verified signature

Configuration Example (Program.cs)

builder.Services
    .AddOptions<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme)
    .Configure<SupabaseJwtSigningKeyResolver, IConfiguration>(
        (opt, resolver, configuration) =>
        {
            opt.TokenValidationParameters = new()
            {
                ValidateIssuerSigningKey = true,
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidIssuer = configuration["Supabase:Issuer"],
                ValidAudience = configuration["Supabase:Audience"],
                IssuerSigningKeyResolver = (_, __, kid, ___) => resolver.ResolveByKid(kid),
                RequireExpirationTime = true,
                ClockSkew = TimeSpan.Zero,
            };
        }
    );

πŸ§ͺ Testability

The Facade allows easy mock creation for testing:

// Mock IAuthService for testing
public class MockAuthService : IAuthService
{
    public async Task<(string AccessToken, string RefreshToken)> SignUpAsync(
        string email,
        string password,
        Dictionary<string, object>? userMetadata,
        CancellationToken cancellationToken = default)
    {
        return ("mock-access-token", "mock-refresh-token");
    }
    
    // ... implement other methods ...
}

// Use in test
var mockService = new MockAuthService();
var result = await mockService.SignUpAsync("test@example.com", "password123");
Assert.Equal("mock-access-token", result.AccessToken);

πŸ”„ Dependency Injection

The infrastructure layer centralizes all dependencies:

// DependencyInjection.cs
services.AddScoped<IAuthService, SupabaseAuthService>(opt =>
{
    var (auth, options) = SupabaseClientFactory.CreateStatelessClient(configuration);
    return new SupabaseAuthService(auth, options);
});

services.AddScoped<SignUpUseCase>();
services.AddScoped<SignInUseCase>();
// ... other dependencies ...

This allows:

  • Easy implementation swapping
  • Testing without real dependencies
  • Clean and decoupled code

πŸš€ Next Steps

Suggested Improvements

  1. Add global error handlers

    app.UseExceptionHandler("/error");
  2. Implement Rate Limiting

    app.UseRateLimiting();
  3. Add structured logging

    builder.Services.AddLogging();
  4. Implement multiple IDP providers

    IAuthService auth = idpType switch
    {
        IDPType.Supabase => new SupabaseAuthService(...),
        IDPType.Auth0 => new Auth0AuthService(...),
        IDPType.Okta => new OktaAuthService(...),
        _ => throw new NotSupportedException()
    };
  5. Add refresh token rotation

    • Invalidate old refresh tokens after renewal
  6. Implement 2FA (Two-Factor Authentication)

    • TOTP/SMS for enhanced security

πŸ“š Use Case Structure

Example: SignUpUseCase

public class SignUpUseCase
{
    private readonly IAuthService _authService;
    
    public SignUpUseCase(IAuthService authService)
    {
        _authService = authService;  // Facade injection
    }
    
    public async Task<SignUpResponse> ExecuteAsync(
        string email, 
        string password, 
        CancellationToken cancellationToken)
    {
        // Business validations
        if (!email.Contains("@"))
            throw new InvalidEmailException();
            
        // Call the Facade
        var (accessToken, refreshToken) = await _authService.SignUpAsync(
            email, 
            password, 
            null, 
            cancellationToken
        );
        
        return new SignUpResponse(accessToken, refreshToken);
    }
}

πŸ”— Available Endpoints

Method Endpoint Authentication Description
POST /Auth/SignUp ❌ No Register new user
POST /Auth/SignIn ❌ No User login
POST /Auth/SignInFromRefreshToken ❌ No Renew tokens
GET /Users/GetProfileById/{id} βœ… Yes Get user profile

πŸ“ API Documentation

The Swagger documentation is available at:

https://localhost:7xxx/swagger

(Port varies according to launchSettings.json)


βš™οΈ Technology Stack

  • Language: C# 13
  • Framework: .NET 9
  • Web: FastEndpoints 5.35.0
  • Authentication: Supabase GoTrue + JWT Bearer
  • IDP: Supabase
  • Patterns: Clean Architecture, Facade Pattern, SOLID

🀝 Contributions

This is an educational example of how to implement a Facade for an IDP. Feel free to adapt it to your needs.


❓ FAQ

Q: Why use Facade instead of using Supabase directly?

A: The Facade provides abstraction, making it easy to switch IDPs in the future without breaking application code.

Q: How do I integrate with my frontend?

A: Use the POST endpoints for authentication and store tokens (localStorage/sessionStorage). Include the accessToken in the Authorization: Bearer {token} header.

Q: What if the refresh token expires?

A: The user needs to log in again with their credentials.

Q: Can I add support for OAuth (Google, GitHub)?

A: Yes! Add methods to IAuthService like SignInWithProviderAsync() and implement in SupabaseAuthService.


About

Facade for the Supabase identity provider. The project provides a centralized abstraction layer that simplifies integration with authentication services, enabling authentication operations without directly exposing the underlying IDP's complexity.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages