Skip to content

Getting Started with GrydAuth

This guide will walk you through setting up GrydAuth in your .NET application from scratch.

Prerequisites

  • .NET 10.0 SDK or later
  • PostgreSQL, SQL Server, or any EF Core compatible database
  • Basic understanding of ASP.NET Core authentication

Step 1: Create a New Project

bash
# Create new Web API project
dotnet new webapi -n MyApp.API -o src/MyApp.API

# Create class libraries for Clean Architecture
dotnet new classlib -n MyApp.Domain -o src/MyApp.Domain
dotnet new classlib -n MyApp.Application -o src/MyApp.Application
dotnet new classlib -n MyApp.Infrastructure -o src/MyApp.Infrastructure

# Add to solution
dotnet new sln -n MyApp
dotnet sln add src/MyApp.API
dotnet sln add src/MyApp.Domain
dotnet sln add src/MyApp.Application
dotnet sln add src/MyApp.Infrastructure

Step 2: Install GrydAuth Packages

bash
# Core packages
cd src/MyApp.Domain
dotnet add package GrydAuth.Domain

cd ../MyApp.Application
dotnet add package GrydAuth.Application

cd ../MyApp.Infrastructure
dotnet add package GrydAuth.Infrastructure

cd ../MyApp.API
dotnet add package GrydAuth.API
dotnet add package GrydAuth.Infrastructure

Step 3: Configure Database Context

Create your DbContext that extends GrydAuthDbContext:

csharp
// src/MyApp.Infrastructure/Data/AppDbContext.cs
using GrydAuth.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;

namespace MyApp.Infrastructure.Data;

public class AppDbContext : GrydAuthDbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    {
    }

    // Add your custom DbSets here
    // public DbSet<Product> Products => Set<Product>();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        
        // Your custom entity configurations
        // modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
    }
}

Step 4: Configure Program.cs

csharp
// src/MyApp.API/Program.cs
using GrydAuth.API.Controllers;
using GrydAuth.Infrastructure;
using Gryd.API.Extensions;
using MyApp.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// 1. Add GrydAuth services (Application + Infrastructure + JWT + Authorization)
// All configuration is read from appsettings.json sections
builder.Services.AddGrydAuth(builder.Configuration);

// 2. Add Controllers (includes GrydAuth API controllers)
builder.Services.AddControllers()
    .AddApplicationPart(typeof(AuthController).Assembly);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// 3. Configure Database
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));

// 4. Add Exception Handlers (IExceptionHandler pipeline - .NET 8+)
// IMPORTANT: Register in order of specificity (most specific first)
builder.Services.AddGrydAuthExceptionHandler();  // GrydAuth-specific (Redis, DB, etc.)
builder.Services.AddCoreExceptionHandler();       // Core fallback (validation, security, etc.)

var app = builder.Build();

// Configure pipeline
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

// CRITICAL: Exception handling MUST be first to catch all exceptions
app.UseExceptionHandler();

app.UseHttpsRedirection();
app.UseRouting();

// Authentication (standard ASP.NET Core)
app.UseAuthentication();

// GrydAuth middleware (token blacklist, SmartFederation, AppId validation)
// CRITICAL: Must be AFTER Authentication but BEFORE Authorization
app.UseGrydAuth();

// Authorization (standard ASP.NET Core)
app.UseAuthorization();

app.MapControllers();

app.Run();

Step 5: Configure appsettings.json

All GrydAuth configuration is consolidated under the GrydAuth section for better organization:

json
{
  "ConnectionStrings": {
    "DefaultConnection": "Host=localhost;Database=myapp;Username=postgres;Password=your_password"
  },
  "JwtSettings": {
    "SecretKey": "your-super-secret-key-at-least-256-bits-long-for-security",
    "Issuer": "myapp",
    "Audience": "myapp-users",
    "ExpirationMinutes": 60,
    "RefreshTokenExpirationDays": 7,
    "AppId": "my-app-id"
  },
  "GrydAuth": {
    "SmartFederation": {
      "IsEnabled": true,
      "CacheFirst": true,
      "PerformanceHeaders": true,
      "TokenCacheMinutes": 15,
      "UserClaimsCacheMinutes": 30,
      "DatabaseFallback": true
    },
    "Cache": {
      "IsEnabled": true,
      "DefaultExpirationMinutes": 15,
      "TokenCacheMinutes": 15,
      "UserClaimsCacheMinutes": 30,
      "Redis": {
        "ConnectionString": "localhost:6379",
        "Database": 0,
        "KeyPrefix": "MyApp:",
        "Compression": true
      }
    },
    "Storage": {
      "AwsS3": {
        "BucketName": "",
        "Region": "us-east-1",
        "AccessKeyId": "",
        "SecretAccessKey": "",
        "ProfilePicturesPrefix": "profile-pictures/",
        "MaxFileSizeBytes": 5242880,
        "AllowedContentTypes": ["image/jpeg", "image/png", "image/gif", "image/webp"]
      }
    },
    "PasswordPolicy": {
      "MinLength": 8,
      "RequireUppercase": true,
      "RequireLowercase": true,
      "RequireDigit": true,
      "RequireSpecialChar": true,
      "SpecialCharacters": "!@#$%^&*()_+-=[]{}|;':\",./<>?",
      "MaxLength": 128,
      "EnablePasswordExpiration": true,
      "ExpirationDays": 90,
      "WarnBeforeExpirationDays": 7,
      "EnablePasswordHistory": false,
      "PasswordHistoryCount": 5
    },
    "PasswordReset": {
      "TokenExpirationMinutes": 60,
      "BaseUrl": "https://yourapp.com",
      "ResetPath": "/reset-password",
      "FromEmail": "noreply@yourapp.com",
      "FromName": "Your App"
    },
    "Security": {
      "MaxFailedAccessAttempts": 5,
      "LockoutDurationMinutes": 15,
      "EnableSecurityLogging": true
    },
    "Email": {
      "Provider": "Smtp",
      "SmtpHost": "smtp.yourprovider.com",
      "SmtpPort": 587,
      "SmtpUsername": "",
      "SmtpPassword": "",
      "UseSsl": true
    },
    "CircuitBreaker": {
      "FailureThreshold": 5,
      "SamplingDurationSeconds": 30,
      "MinimumThroughput": 10,
      "DurationOfBreakSeconds": 60
    },
    "GeoIp": {
      "Provider": "MaxMind",
      "MaxMindDatabasePath": "GeoLite2-City.mmdb",
      "MaxMindAccountId": 0,
      "MaxMindLicenseKey": "",
      "EnableAutomaticUpdates": false,
      "UpdateIntervalDays": 7
    },
    "ImpossibleTravel": {
      "MaxVelocityKmh": 900.0,
      "TimeWindowMinutes": 60,
      "MinimumDistanceKm": 10.0,
      "AutoBlockCriticalRisk": false,
      "NotifySecurityTeam": true
    }
  },
  "MultiTenancy": {
    "IsEnabled": true,
    "Strategy": "Header",
    "TenantHeaderName": "X-Tenant-ID",
    "TenantQueryParameter": "tenantId",
    "TenantRouteParameter": "tenantId",
    "DefaultTenantId": "",
    "Tenants": []
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  }
}

Configuration Sections

All GrydAuth configuration is consolidated under the GrydAuth section:

  • GrydAuth:SmartFederation - Cache-first authentication strategy
  • GrydAuth:Cache - Redis cache configuration for token blacklisting
  • GrydAuth:Storage - File storage (AWS S3 for profile pictures)
  • GrydAuth:PasswordPolicy - Password complexity requirements
  • GrydAuth:PasswordReset - Password reset token settings
  • GrydAuth:Security - Account lockout and security settings
  • GrydAuth:Email - Email service configuration
  • GrydAuth:CircuitBreaker - Smart Federation resilience settings
  • GrydAuth:GeoIp - GeoIP/MaxMind configuration
  • GrydAuth:ImpossibleTravel - Travel velocity anomaly detection External configuration:
  • JwtSettings - JWT token configuration (SecretKey, Issuer, Audience, etc.)
  • MultiTenancy - Multi-tenant mode settings (Core feature, not GrydAuth-specific)

Step 6: Run Your Application

In Development environment, GrydAuth migrations are applied automatically when the application starts:

bash
cd src/MyApp.API
dotnet run

✅ The ApplyGrydAuthMigrationsAsync() method applies pending migrations automatically.

Update Admin User AppId (Required - First Run)

After the first run, update the admin user's AppId to match your appsettings.Development.json:

sql
-- Find your AppId in JwtSettings.AppId and update:
UPDATE public."UserAppIds"
SET "AppId" = 'MyApp-dev-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
WHERE "UserId" = '11111111-1111-1111-1111-111111111111';

⚠️ Without this step, login will fail with: Invalid application ID

Step 7: Create Your First User

GrydAuth provides built-in endpoints for user management. Once your application is running:

Register a User

bash
POST /api/auth/register
Content-Type: application/json

{
  "email": "admin@example.com",
  "password": "Admin@123!",
  "fullName": "System Administrator"
}

Login

bash
POST /api/auth/login
Content-Type: application/json
X-App-Id: my-app-id

{
  "email": "admin@example.com",
  "password": "Admin@123!",
  "isPasswordEncrypted": false
}

Response:

json
{
  "isSuccess": true,
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refreshToken": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4...",
    "expiresAt": "2026-01-29T13:00:00Z",
    "permissions": ["read:users", "update:users"],
    "isFirstLogin": false,
    "mustChangePassword": false,
    "isGlobal": false,
    "requiresTenantSelection": false,
    "tokenType": "Tenant",
    "currentTenant": {
      "id": "550e8400-e29b-41d4-a716-446655440001",
      "name": "Default Tenant",
      "isDefault": true
    }
  }
}

Multi-Tenant Authentication

When multi-tenancy is enabled and the user has access to multiple tenants:

  1. First login returns a Global Token (2min TTL, isGlobal: true)
  2. Use POST /api/auth/switch-tenant with the target tenant ID
  3. You'll receive a Tenant Token (60min TTL) with full access

Use the Token

bash
GET /api/auth/me
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Response (basic info from JWT):

json
{
  "isSuccess": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "System Administrator",
    "email": "admin@example.com",
    "roles": ["Admin"],
    "permissions": ["read:users", "update:users"]
  }
}

Complete profile (with include=profile):

bash
GET /api/auth/me?include=profile
# or
GET /api/auth/me/complete

Step 8: Protect Your Endpoints

Using Built-in Attributes

csharp
using GrydAuth.API.Attributes;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
[Authorize] // Requires authentication
public class ProductsController : ControllerBase
{
    // Anyone authenticated can read
    [HttpGet]
    [RequirePermission("read:products")]
    public IActionResult GetAll() => Ok(new[] { "Product 1", "Product 2" });
    
    // Only users with create permission
    [HttpPost]
    [RequirePermission("create:products")]
    public IActionResult Create([FromBody] CreateProductRequest request) 
        => Created("", new { Id = Guid.NewGuid() });
    
    // Only admins
    [HttpDelete("{id}")]
    [Authorize(Roles = "Admin")]
    public IActionResult Delete(Guid id) => NoContent();
}

Using Policy-Based Authorization

csharp
// Program.cs
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("CanManageUsers", policy =>
        policy.RequirePermission("create:users", "update:users", "delete:users"));
    
    options.AddPolicy("AdminOnly", policy =>
        policy.RequireRole("Admin", "SuperAdmin"));
});

// Controller
[HttpPost]
[Authorize(Policy = "CanManageUsers")]
public IActionResult CreateUser([FromBody] CreateUserRequest request) { ... }

Next Steps

Now that you have GrydAuth set up, explore these topics:

Common Issues

Token Validation Fails

Ensure your SecretKey is at least 256 bits (32 characters) and consistent across all environments.

CORS Issues

Add CORS configuration before authentication middleware:

csharp
builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(policy =>
    {
        policy.WithOrigins("http://localhost:3000")
              .AllowAnyHeader()
              .AllowAnyMethod()
              .AllowCredentials();
    });
});

// In pipeline (before UseAuthentication)
app.UseCors();

Database Connection

Ensure your connection string is correct and the database exists. PostgreSQL example:

bash
# Create database
psql -U postgres -c "CREATE DATABASE myapp;"

Released under the MIT License.