Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ttpullima/RomsoftBackEnd2021_v2/llms.txt

Use this file to discover all available pages before exploring further.

The Gestión Clínica API uses JSON Web Tokens (JWT) for stateless authentication. Token creation and validation are handled entirely by two classes in the WebApi project: TokenGenerator (issues tokens at login) and TokenValidationHandler (validates tokens on every subsequent request). Both classes read their configuration from exactly four <appSettings> keys in Web.config — no code changes are needed to adjust the signing secret, issuer, audience, or session lifetime.

AppSettings Keys

<appSettings>
  <!-- JWT Authentication -->
  <add key="JWT_SECRET_KEY"      value="4b436579-7850-45bc-be7a-0e8a7c7715c9" />
  <add key="JWT_AUDIENCE_TOKEN"  value="http://localhost:44390" />
  <add key="JWT_ISSUER_TOKEN"    value="http://localhost:44390" />
  <add key="JWT_EXPIRE_MINUTES"  value="28800" />
</appSettings>
The JWT_SECRET_KEY value shown above (4b436579-7850-45bc-be7a-0e8a7c7715c9) is the public development default committed to the repository. Replace it with a strong, randomly generated value before deploying to any shared or production environment. Anyone who knows this key can forge valid tokens for any username. Use a cryptographically secure random UUID or a 256-bit random hex string.

JWT_SECRET_KEY
string
required
The symmetric HMAC-SHA256 signing secret. TokenGenerator uses this key to sign newly issued tokens; TokenValidationHandler uses the same key to verify incoming tokens. Any change to this value immediately invalidates all currently active sessions — all clients will receive 401 Unauthorized until they log in again and obtain a new token.Recommended format: a randomly generated UUID string (e.g., output of Guid.NewGuid().ToString()) or a 32-byte hex string.
JWT_AUDIENCE_TOKEN
string
required
The expected aud (audience) claim in every token. TokenGenerator sets this as the audience when creating a token; TokenValidationHandler rejects any token whose aud claim does not match this value exactly. In production, set this to the base URL of the API (e.g., https://api.romsoft.pe).
JWT_ISSUER_TOKEN
string
required
The expected iss (issuer) claim. Must match the value embedded in the token at creation time. Typically set to the same URL as JWT_AUDIENCE_TOKEN for single-server deployments.
JWT_EXPIRE_MINUTES
integer
required
Token lifetime in minutes. The default value of 28800 equals 20 days (28800 ÷ 60 = 480 hours), which is a very long session window. After expiry, TokenValidationHandler returns 401 Unauthorized and the client must re-authenticate.To shorten sessions to 4 hours, set this to 240. To extend to 12 hours, use 720.

TokenGenerator — Issuing Tokens

TokenGenerator is an internal static class in University.API.Controllers (the original namespace, retained from the template). It exposes a single method called at login time by AccountController:
internal static class TokenGenerator
{
    public static string GenerateTokenJwt(string username)
    {
        // Read all four appSettings keys
        var secretKey     = ConfigurationManager.AppSettings["JWT_SECRET_KEY"];
        var audienceToken = ConfigurationManager.AppSettings["JWT_AUDIENCE_TOKEN"];
        var issuerToken   = ConfigurationManager.AppSettings["JWT_ISSUER_TOKEN"];
        var expireTime    = ConfigurationManager.AppSettings["JWT_EXPIRE_MINUTES"];

        var securityKey        = new SymmetricSecurityKey(
                                     System.Text.Encoding.Default.GetBytes(secretKey));
        var signingCredentials = new SigningCredentials(
                                     securityKey,
                                     SecurityAlgorithms.HmacSha256Signature);

        // Embed the username as the Name claim
        ClaimsIdentity claimsIdentity = new ClaimsIdentity(
            new[] { new Claim(ClaimTypes.Name, username) });

        var tokenHandler      = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler();
        var jwtSecurityToken  = tokenHandler.CreateJwtSecurityToken(
            audience:           audienceToken,
            issuer:             issuerToken,
            subject:            claimsIdentity,
            notBefore:          DateTime.UtcNow,
            expires:            DateTime.UtcNow.AddMinutes(Convert.ToInt32(expireTime)),
            signingCredentials: signingCredentials);

        return tokenHandler.WriteToken(jwtSecurityToken);
    }
}
The resulting JWT is a standard three-part header.payload.signature string. Clients store it (typically in memory or localStorage) and include it in every subsequent request as Authorization: Bearer <token>.

TokenValidationHandler — Validating Tokens

TokenValidationHandler extends DelegatingHandler and is registered globally in WebApiConfig.Register. It runs before any controller action:
internal class TokenValidationHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        string token;
        if (!TryRetrieveToken(request, out token))
        {
            // No Authorization header — pass through (controller may allow anonymous)
            return base.SendAsync(request, cancellationToken);
        }

        try
        {
            var secretKey     = ConfigurationManager.AppSettings["JWT_SECRET_KEY"];
            var audienceToken = ConfigurationManager.AppSettings["JWT_AUDIENCE_TOKEN"];
            var issuerToken   = ConfigurationManager.AppSettings["JWT_ISSUER_TOKEN"];
            var securityKey   = new SymmetricSecurityKey(
                                    System.Text.Encoding.Default.GetBytes(secretKey));

            var tokenHandler         = new JwtSecurityTokenHandler();
            var validationParameters = new TokenValidationParameters
            {
                ValidAudience          = audienceToken,
                ValidIssuer            = issuerToken,
                ValidateLifetime       = true,
                ValidateIssuerSigningKey = true,
                LifetimeValidator      = this.LifetimeValidator,
                IssuerSigningKey       = securityKey
            };

            // Sets Thread.CurrentPrincipal and HttpContext.Current.User
            Thread.CurrentPrincipal = tokenHandler.ValidateToken(
                token, validationParameters, out _);
            HttpContext.Current.User = tokenHandler.ValidateToken(
                token, validationParameters, out _);

            return base.SendAsync(request, cancellationToken);
        }
        catch (SecurityTokenValidationException)
        {
            return Task.FromResult(new HttpResponseMessage(HttpStatusCode.Unauthorized));
        }
        catch (Exception)
        {
            return Task.FromResult(new HttpResponseMessage(HttpStatusCode.InternalServerError));
        }
    }

    public bool LifetimeValidator(
        DateTime? notBefore, DateTime? expires,
        SecurityToken securityToken, TokenValidationParameters validationParameters)
    {
        return expires != null && DateTime.UtcNow < expires;
    }
}

Validation Checks Performed

CheckMechanism
Token presentTryRetrieveToken — looks for Authorization: Bearer <token> header
Audience matchesValidAudience = JWT_AUDIENCE_TOKEN
Issuer matchesValidIssuer = JWT_ISSUER_TOKEN
Signing key validIssuerSigningKey = HMAC-SHA256 key derived from JWT_SECRET_KEY
Not expiredCustom LifetimeValidator: DateTime.UtcNow < expires
A request with a missing Authorization header is passed through to the controller. A request with a present but invalid token (wrong signature, wrong issuer, expired) returns 401 Unauthorized immediately without reaching the controller.

Changing the Token Expiry

To adjust session length, change only the JWT_EXPIRE_MINUTES value in Web.config:
<!-- 4-hour sessions -->
<add key="JWT_EXPIRE_MINUTES" value="240" />

<!-- 30-minute sessions (high-security environments) -->
<add key="JWT_EXPIRE_MINUTES" value="30" />

<!-- 12-hour sessions (overnight clinical staff) -->
<add key="JWT_EXPIRE_MINUTES" value="720" />
No code recompilation is needed. The change takes effect on the next IIS application pool recycle.

Production Configuration Checklist

1

Generate a new JWT_SECRET_KEY

Run the following in a C# script or LINQPad: Console.WriteLine(Guid.NewGuid().ToString()); and replace the value in Web.Release.config.
2

Set JWT_AUDIENCE_TOKEN and JWT_ISSUER_TOKEN to your production URL

Replace http://localhost:44390 with the actual HTTPS URL of the deployed API, e.g., https://api.romsoft.pe.
3

Review JWT_EXPIRE_MINUTES for your clinic's shift schedule

The default 28800 minutes (20 days) is a very long session window. For a standard 8-hour clinical shift set this to 480; for 12-hour shifts use 720. Adjust based on your clinic’s security and compliance policy.
4

Apply values via Web.Release.config transforms

Use xdt:Transform="SetAttributes" in Web.Release.config so secrets never appear in the base Web.config committed to source control.

Build docs developers (and LLMs) love