Authentication
GrydAuth provides a complete JWT-based authentication system with access and refresh tokens, password management, and secure session handling.
Token Types
Access Token
Short-lived token (default: 60 minutes) used for API authentication.
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...Claims included:
sub- User IDemail- User emailname- Full nameroles- Assigned rolespermissions- Granted permissionstenant_id- Tenant ID (if multi-tenant)token_version- Token version for invalidationscope- Token scope (tenant-selector-onlyfor global tokens)jti- JWT ID for token blacklisting
Refresh Token
Long-lived token (default: 7 days) used to obtain new access tokens without re-authentication.
Global vs Tenant Tokens
- Global Token (2 minutes TTL): Restricted scope, used only for tenant selection when user has multiple tenants
- Tenant Token (60 minutes TTL): Full access within a tenant context
Authentication Endpoints
Login
Authenticates a user and returns tokens.
POST /api/auth/login
Content-Type: application/json
X-App-Id: my-app-id
{
"email": "user@example.com",
"password": "Password123!",
"isPasswordEncrypted": false,
"preferredTenantId": "550e8400-e29b-41d4-a716-446655440001"
}Success Response (200):
{
"isSuccess": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "dGhpcyBpcyBhIHJlZnJlc2g...",
"expiresAt": "2026-01-29T13:00:00Z",
"permissions": ["read:products", "update:products"],
"isFirstLogin": false,
"mustChangePassword": false,
"daysUntilPasswordExpiration": null,
"isGlobal": false,
"requiresTenantSelection": false,
"tokenType": "Tenant",
"currentTenant": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"name": "Acme Corp",
"isDefault": true,
"permissions": ["read:products"]
},
"smartAutoSwitched": true
}
}Multi-Tenant Login
When the user has access to multiple tenants and no preferredTenantId is specified:
isGlobal: true- Token is a Global Token (2min TTL)requiresTenantSelection: true- User must select a tenantavailableTenants- List of tenants the user can access- Use
POST /api/auth/switch-tenantto get a full Tenant Token
Error Responses:
| Status | Code | Description |
|---|---|---|
| 401 | INVALID_CREDENTIALS | Email or password is incorrect |
| 401 | ACCOUNT_LOCKED | Account is locked due to failed attempts |
| 401 | EMAIL_NOT_CONFIRMED | Email confirmation required |
| 401 | ACCOUNT_DISABLED | Account has been disabled |
Register
Creates a new user account.
POST /api/auth/register
Content-Type: application/json
{
"email": "newuser@example.com",
"password": "SecurePass123!",
"fullName": "Jane Doe",
"phoneNumber": "+1234567890"
}Success Response (201):
{
"isSuccess": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"email": "newuser@example.com",
"fullName": "Jane Doe",
"createdAt": "2026-01-29T12:00:00Z"
}
}Refresh Token
Exchanges a valid refresh token for new tokens.
POST /api/auth/refresh
Content-Type: application/json
{
"refreshToken": "dGhpcyBpcyBhIHJlZnJlc2g...",
"tenantId": "550e8400-e29b-41d4-a716-446655440001"
}Success Response (200):
{
"isSuccess": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "bmV3IHJlZnJlc2ggdG9rZW4...",
"expiresAt": "2026-01-29T14:00:00Z",
"permissions": ["read:products"],
"isGlobal": false,
"tokenType": "Tenant"
}
}Refresh Token Rotation
GrydAuth uses refresh token rotation by default. Each refresh request issues a new refresh token and invalidates the previous one. This enhances security by limiting the window for token theft.
Logout
Invalidates the current session and all user tokens globally.
POST /api/auth/logout
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...Simplified OAuth 2.0 Logout
GrydAuth follows OAuth 2.0 RFC 7009 best practices:
- No request body required - All info extracted from Authorization header
- Global logout - Invalidates ALL user sessions using timestamp-based blacklist
- More secure - Doesn't expose refresh token in requests
For selective session management, consider implementing GET /api/auth/sessions in your application.
Password Management
Change Password
POST /api/auth/change-password
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json
{
"currentPassword": "OldPassword123!",
"newPassword": "NewSecurePass456!"
}Forgot Password
Initiates password reset flow.
POST /api/auth/request-password-reset
Content-Type: application/json
{
"email": "user@example.com"
}This sends a password reset email with a token.
Reset Password
POST /api/auth/reset-password
Content-Type: application/json
{
"email": "user@example.com",
"token": "reset-token-from-email",
"newPassword": "NewSecurePass789!"
}Password Policy Configuration
Password policy is configured via appsettings.json:
{
"PasswordPolicy": {
"RequireDigit": true,
"RequireLowercase": true,
"RequireUppercase": true,
"RequireNonAlphanumeric": true,
"RequiredLength": 8,
"RequiredUniqueChars": 4
}
}Account Lockout
Protect against brute-force attacks via appsettings.json:
{
"Security": {
"LockoutEnabled": true,
"MaxFailedAccessAttempts": 5,
"LockoutDurationMinutes": 15
}
}Token Versioning
GrydAuth supports token versioning for immediate invalidation:
// Invalidate all tokens for a user
await authService.InvalidateAllTokensAsync(userId);
// This increments the user's token version, making all existing tokens invalidScenarios that trigger token invalidation:
- Password change
- Security settings change
- Role/permission update
- Manual logout from all devices
- Admin-triggered invalidation
Using Authentication in Code
Injecting Current User
using GrydAuth.Application.Common.Interfaces;
public class ProductService
{
private readonly ICurrentUserService _currentUser;
public ProductService(ICurrentUserService currentUser)
{
_currentUser = currentUser;
}
public async Task<Product> CreateAsync(CreateProductRequest request)
{
var product = new Product
{
Name = request.Name,
CreatedBy = _currentUser.UserId, // Get current user ID
TenantId = _currentUser.TenantId // Get current tenant
};
// ...
}
}ICurrentUserService Properties
| Property | Type | Description |
|---|---|---|
UserId | Guid? | Current user's ID |
Email | string? | Current user's email |
FullName | string? | Current user's full name |
TenantId | Guid? | Current tenant ID |
Roles | IEnumerable<string> | User's roles |
Permissions | IEnumerable<string> | User's permissions |
IsAuthenticated | bool | Whether user is authenticated |
Custom Claims
Add custom claims to tokens:
public class CustomClaimsService : ICustomClaimsProvider
{
private readonly IUserService _userService;
public async Task<IEnumerable<Claim>> GetClaimsAsync(User user)
{
var department = await _userService.GetDepartmentAsync(user.Id);
return new[]
{
new Claim("department", department.Name),
new Claim("department_id", department.Id.ToString())
};
}
}
// Register in DI
builder.Services.AddScoped<ICustomClaimsProvider, CustomClaimsService>();Two-Factor Authentication (2FA)
Coming Soon
Two-factor authentication support is planned for GrydAuth v1.1.0
External Authentication
Social Login
GrydAuth supports social login via POST /api/auth/social-login:
POST /api/auth/social-login
Content-Type: application/json
X-App-Id: my-app-id
{
"provider": "google",
"accessToken": "ya29.a0AfH6SM...",
"preferredTenantId": "550e8400-e29b-41d4-a716-446655440001"
}Supported providers depend on your Auth0 or custom OAuth configuration.
Auth0 Integration
Install the Auth0 package:
dotnet add package GrydAuth.Infrastructure.Auth0Configure in appsettings.json:
{
"Auth0": {
"Domain": "your-tenant.auth0.com",
"ClientId": "your-client-id",
"ClientSecret": "your-client-secret",
"Audience": "https://your-api.com"
}
}Security Best Practices
- Use HTTPS - Always use HTTPS in production
- Secure Secret Key - Store JWT secret in secure vault (Azure Key Vault, AWS Secrets Manager)
- Short Token Lifetime - Keep access tokens short-lived (15-60 minutes)
- Refresh Token Rotation - Enable refresh token rotation (default)
- Token Versioning - Enable token versioning for immediate invalidation
- Rate Limiting - Configure rate limiting for auth endpoints
- Audit Logging - Enable audit logging for authentication events