Appearance
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: 10 minutes, max 15) used for API authentication. Access tokens are snapshots of authorization, so they are deliberately short-lived; use refresh tokens for session continuity.
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InByb2Qta2V5In0...Asymmetric signing (v5)
Tokens are signed with RS256 (or ES256) using a private key held only by GrydAuth. The alg header is RS256 and a kid header selects the signing key. Consumers validate with the public key published at /.well-known/jwks.json. See the v4 → v5 migration guide if you are upgrading from HS256.
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 (10 minutes TTL, max 15): Full access within a tenant context
Authentication Endpoints
Login
Authenticates a user and returns tokens.
http
POST /api/v1/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):
json
{
"isSuccess": true,
"data": {
"token": "eyJhbGciOiJSUzI1NiIs...",
"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"],
"type": "company",
"parentTenantId": "550e8400-e29b-41d4-a716-446655440010",
"parentTenantName": "Acme Holding",
"groupId": "550e8400-e29b-41d4-a716-446655440010",
"groupName": "Acme Holding"
},
"smartAutoSwitched": true,
"groupId": "550e8400-e29b-41d4-a716-446655440010",
"groupName": "Acme Holding",
"tenantType": "company"
}
}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 accesscurrentTenant.type/groupId/groupName- Hierarchy context for grouped UI rendering- Use
POST /api/v1/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.
http
POST /api/v1/auth/register
Content-Type: application/json
{
"email": "newuser@example.com",
"password": "SecurePass123!",
"fullName": "Jane Doe",
"phoneNumber": "+1234567890"
}Success Response (201):
json
{
"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.
http
POST /api/v1/auth/refresh
Content-Type: application/json
{
"tenantId": "550e8400-e29b-41d4-a716-446655440001"
}Success Response (200):
json
{
"isSuccess": true,
"data": {
"token": "eyJhbGciOiJSUzI1NiIs...",
"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.
http
POST /api/v1/auth/logout
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...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/v1/auth/sessions in your application.
Password Management
Change Password
http
POST /api/v1/auth/change-password
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
Content-Type: application/json
{
"currentPassword": "OldPassword123!",
"newPassword": "NewSecurePass456!"
}Forgot Password
Initiates password reset flow.
http
POST /api/v1/auth/request-password-reset
Content-Type: application/json
{
"email": "user@example.com"
}This sends a password reset email with a token.
Reset Password
http
POST /api/v1/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 under the GrydAuth section:
json
{
"GrydAuth": {
"PasswordPolicy": {
"MinLength": 8,
"RequireUppercase": true,
"RequireLowercase": true,
"RequireDigit": true,
"RequireSpecialChar": true,
"SpecialCharacters": "!@#$%^&*()_+-=[]{}|;':\",./<>?",
"MaxLength": 128,
"EnablePasswordExpiration": true,
"ExpirationDays": 90,
"WarnBeforeExpirationDays": 7,
"EnablePasswordHistory": false,
"PasswordHistoryCount": 5
}
}
}Account Lockout
Protect against brute-force attacks via appsettings.json under the GrydAuth section:
json
{
"GrydAuth": {
"Security": {
"MaxFailedAccessAttempts": 5,
"LockoutDurationMinutes": 15,
"EnableSecurityLogging": true
}
}
}Token Versioning
GrydAuth supports token versioning for immediate invalidation:
csharp
// 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
csharp
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:
csharp
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/v1/auth/social-login:
http
POST /api/v1/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:
bash
dotnet add package GrydAuth.Infrastructure.Auth0Configure in appsettings.json:
json
{
"Auth0": {
"Domain": "your-tenant.auth0.com",
"ClientId": "your-client-id",
"ClientSecret": "your-client-secret",
"Audience": "https://your-api.com"
}
}JWKS Endpoint
GrydAuth publishes its public signing keys so that any service can validate tokens without holding a secret:
http
GET /.well-known/jwks.jsonThe response is a standard JWK Set (kty, use, kid, alg, and key material). It exposes only public keys and returns two keys during rotation so tokens signed by the previous key keep validating. See Migrating from v4 to v5 for key generation, configuration and rotation details.
Security Best Practices
- Use HTTPS - Always use HTTPS in production
- Protect the private signing key - Store the JWT private key (
JwtSettings:PrivateKeyPem) in a secure vault (Azure Key Vault, AWS Secrets Manager); never commit it. Consumers use the public JWKS only. - Short Token Lifetime - Keep access tokens short-lived (1–15 minutes; default 10)
- 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