GSMAuth is the dedicated authentication microservice for the GSM Application platform. It is the only service that knows how to map a company identifier to a database connection string, validate user credentials against the tenant’sDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/ti-infinite/GSMApplication/llms.txt
Use this file to discover all available pages before exploring further.
Users table, and issue signed JWTs carrying the tenant and profile identity claims that every other service relies on. All login traffic enters through the gateway’s anonymous /api/security/** route and is forwarded to GSMAuth without requiring a prior token.
Layer Structure
GSMAuth is organized into seven projects following a strict layered architecture:GSMAuth.Api
HTTP controllers, Swagger, and request/response pipeline. Hosts
AuthController and TenantController.GSMAuth.Business
Login orchestration logic: credential validation, JWT generation, and password hash verification.
GSMAuth.Abstractions
Interfaces (
IAuthService, ITenantConnectionResolver, ITenantConfigurationService) that decouple layers via DIP.GSMAuth.Entities
DTOs (
LoginRequestDto, LoginDto, AuthenticatedUserDto, TenantResolveDto) and shared value objects.GSMAuth.Tenant
Transversal
TenantContext and tenant middleware. Contains no business logic.GSMAuth.Infrastructure
PBKDF2 password hashing, JWT token generation, and
TenantConnectionResolver implementation.GSMAuth.DataAccess
EF Core contexts:
RegistryDbContext (reads TenantRegistryDb.Tenants) and the dynamic per-tenant DbContext.Login Flow
Authentication follows a seven-step process that crosses from the HTTP layer down to the tenant database and back.Client posts credentials
The caller sends a All three fields are required. A missing field returns
POST /api/v1/auth/login request with the following body:400 Bad Request.Lookup tenant in the registry
TenantConnectionResolver queries TenantRegistryDb.Tenants where CompanyId = IDCompany AND IsActive = 1. If no active row is found, the service returns 404 Not Found without touching any tenant database.Build dynamic tenant connection
The
Server, Database, DbUser, and DbPassword columns from the registry row are assembled into a connection string, and a scoped DbContext targeting the tenant database is instantiated for this request.Query the Users table
The tenant-specific
DbContext queries the Users table for a record matching the submitted username.Verify password and check account status
The service checks that
user.IsActive is true — an inactive account returns 401 Unauthorized. It then calls PasswordHasher.Verify(password, user.PasswordHash) using ASP.NET Core Identity’s PasswordHasher<T>. A hash mismatch also returns 401 Unauthorized.Issue JWT
A signed JWT is generated containing three key claims:
The token is signed with the secret configured via
| Claim | Source |
|---|---|
sub | User identifier (GUID) |
companyId | IDCompany from the login request |
idProfile | Integer profile ID from the Users table |
JwtSettings__SecretKey and bounded by JwtSettings__Issuer, JwtSettings__Audience, and JwtSettings__ExpirationMinutes.Endpoints
POST /api/v1/auth/login
Authenticates a user against the tenant database and returns a JWT.
The company identifier matching a
CompanyId value in TenantRegistryDb.Tenants.The username as stored in the tenant’s
Users table.The plaintext password. It is never stored — it is hashed on the fly and compared to
PasswordHash.| Status | Meaning |
|---|---|
200 OK | Credentials valid; body contains LoginDto and gsm_token cookie is set. |
400 Bad Request | One or more required fields are missing or malformed. |
401 Unauthorized | Tenant not found or inactive, user not found, user is inactive, or password does not match. |
500 Internal Server Error | Unexpected error during authentication. |
POST /api/v1/auth/logout
Requires a valid Bearer token. Deletes the gsm_token cookie by setting its expiry in the past:
POST /api/v1/tenant/resolve
Anonymous endpoint used by the frontend to verify that a company identifier exists and to fetch its theming configuration before the user enters credentials.
The company identifier to resolve.
true if an active tenant was found for the given IDCompany.The raw
JsonStyles JSON string stored in Tenants.JsonStyles. Parse this on the client to extract the light, dark, and meta theme properties. See Tenant Theming.JWT Claims Reference
| Claim | Type | Description |
|---|---|---|
sub | string (GUID) | Unique user identifier |
companyId | string | Tenant identifier; injected into X-Company-Id by the gateway |
idProfile | integer | User’s permission profile; injected into X-Profile-Id by the gateway |
Cookie Reference
| Property | Value |
|---|---|
| Name | gsm_token |
| HttpOnly | true — not accessible from JavaScript |
| SameSite | Strict — not sent on cross-site requests |
| Path | / |
| Expires | Matches the JWT ExpiresAtUtc value |
Password Storage
Passwords are never stored in plaintext. TheUsers table (db_ms.Users) stores a single PasswordHash column that holds the output produced by ASP.NET Core Identity’s PasswordHasher<T>. The salt is embedded within the hash string itself — no separate PasswordSalt column is required or used.
Environment Variables
Full connection string to
TenantRegistryDb. This variable is mandatory — the service throws InvalidOperationException at startup if it is absent. There is no appsettings.json fallback; the value must be supplied via the environment.Secret key used to sign and verify JWT tokens. This variable is mandatory — the service throws
InvalidOperationException at startup if it is absent. The value is injected into JwtSettings:SecretKey at runtime and is never read from appsettings.json.JWT issuer claim. Must match the
Issuer configured in the gateway. Defaults to GSMAuth.JWT audience claim. Must match the
Audience configured in the gateway. Defaults to GSMClients.Token lifetime in minutes. Tokens expire absolutely at
now + ExpirationMinutes; there is no refresh mechanism.