Skip to content

Swagger / OpenAPI

Swagger UI and OpenAPI spec generation are enabled in development and stage environments only. Production does not expose a Swagger endpoint to reduce the API attack surface.


Availability by Environment

EnvironmentSwagger UIOpenAPI JSONAuth required
devEnabledEnabledNo
stageEnabledEnabledNo (but behind AFD WAF IP allowlist)
preprodDisabledDisabledN/A
uatDisabledDisabledN/A
productionDisabledDisabledN/A

Why Swagger Is Disabled in Production

Swagger exposes your full API surface including parameter names, response schemas, and security model. In production, this information is only shared with authorised integration partners via direct communication. Disabling Swagger reduces the data available to attackers during reconnaissance.


Swagger UI URLs

EnvironmentURL
devhttps://gateway.microtec-test.com/swagger
stagehttps://gateway.microtecstage.com/swagger
Local (accounting)https://localhost:7001/swagger

Configuration

Swagger is conditionally registered based on environment in Program.cs via the shared extension:

csharp
// Microtec.Web.Core / Extensions/SwaggerExtensions.cs
public static IServiceCollection AddMicrotecSwagger(
    this IServiceCollection services,
    IConfiguration configuration,
    string serviceName,
    string serviceTitle)
{
    var env = configuration["ASPNETCORE_ENVIRONMENT"];

    if (env is not ("Development" or "Staging"))
        return services;

    services.AddEndpointsApiExplorer();
    services.AddSwaggerGen(options =>
    {
        options.SwaggerDoc("v1", new OpenApiInfo
        {
            Title = serviceTitle,
            Version = "v1",
            Description = $"Microtec ERP — {serviceTitle} API",
            Contact = new OpenApiContact
            {
                Name = "Microtec DevOps",
                Email = "devops@microtec.com.sa"
            }
        });

        // JWT Bearer auth scheme in Swagger UI
        options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
        {
            Type = SecuritySchemeType.Http,
            Scheme = "bearer",
            BearerFormat = "JWT",
            Description = "Paste a JWT token obtained from Keycloak. " +
                          "Format: Bearer {your-token}"
        });

        options.AddSecurityRequirement(new OpenApiSecurityRequirement
        {
            {
                new OpenApiSecurityScheme
                {
                    Reference = new OpenApiReference
                    {
                        Type = ReferenceType.SecurityScheme,
                        Id = "Bearer"
                    }
                },
                Array.Empty<string>()
            }
        });

        // Include XML comments for controller actions
        var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
        var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
        if (File.Exists(xmlPath))
            options.IncludeXmlComments(xmlPath);

        // Enumerate enums as strings
        options.UseInlineDefinitionsForEnums();
    });

    return services;
}

public static WebApplication UseMicrotecSwagger(this WebApplication app)
{
    var env = app.Configuration["ASPNETCORE_ENVIRONMENT"];

    if (env is not ("Development" or "Staging"))
        return app;

    app.UseSwagger(options =>
    {
        options.RouteTemplate = "swagger/{documentName}/swagger.json";
    });

    app.UseSwaggerUI(options =>
    {
        options.SwaggerEndpoint("/swagger/v1/swagger.json", "Microtec ERP API v1");
        options.RoutePrefix = "swagger";
        options.OAuthClientId("erp-swagger-client");
        options.OAuthUsePkce();
        options.DocExpansion(DocExpansion.None);       // Collapsed by default
        options.DefaultModelsExpandDepth(-1);          // Hide schemas section
        options.DisplayRequestDuration();
    });

    return app;
}

JWT Bearer Auth in Swagger UI

Getting a Token

  1. Open the Swagger UI for the target environment
  2. Click Authorize (lock icon, top right)
  3. Obtain a JWT from Keycloak:
bash
# Get a token using client credentials (for testing)
curl -X POST \
  "https://auth.microtecstage.com/realms/microtec/protocol/openid-connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=password" \
  -d "client_id=erp-home" \
  -d "username=testuser@example.com" \
  -d "password=TestPassword123"
  1. Copy the access_token value
  2. In the Authorize dialog, enter: Bearer <paste-token-here>
  3. Click Authorize — all subsequent requests include this token

Token Expiry

Keycloak access tokens expire after 15 minutes. If Swagger UI requests start returning 401 Unauthorized, your token has expired. Obtain a new one using the curl command above and re-authorize.


XML Doc Comments

Every controller action must have an XML <summary> comment to appear in Swagger:

csharp
/// <summary>
/// Get a paginated list of journal entries.
/// </summary>
/// <param name="query">Filter and pagination parameters.</param>
/// <returns>Paginated list of journal entry summaries.</returns>
/// <response code="200">Returns the list of journal entries.</response>
/// <response code="401">Unauthenticated — missing or invalid JWT.</response>
/// <response code="403">Unauthorised — user lacks Accounting.Read permission.</response>
[HttpGet]
[Authorize(Policy = "CanReadAccounting")]
[ProducesResponseType(typeof(ApiResponse<PagedResult<JournalEntryDto>>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<IActionResult> GetAll([FromQuery] GetAllJournalEntryQuery query) { ... }

The CI pipeline fails if any public controller action is missing [ProducesResponseType] attributes (enforced by a custom Roslyn analyser in Shared.Web).


API Versioning

Multiple API versions are supported by Swashbuckle:

csharp
// When v2 is introduced alongside v1
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Microtec ERP API", Version = "v1" });
options.SwaggerDoc("v2", new OpenApiInfo { Title = "Microtec ERP API", Version = "v2" });

// In UI, show both versions
options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
options.SwaggerEndpoint("/swagger/v2/swagger.json", "v2");

Controllers are tagged with API version attributes:

csharp
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/invoices")]
public class InvoicesV1Controller : ControllerBase { ... }

[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/invoices")]
public class InvoicesV2Controller : ControllerBase { ... }

OpenAPI Spec Export

The OpenAPI JSON spec is available for download and can be used to generate client SDKs:

bash
# Download the spec
curl https://gateway.microtecstage.com/swagger/v1/swagger.json \
  -o accounting-api-v1.json

# Generate a TypeScript client (using openapi-generator)
npx @openapitools/openapi-generator-cli generate \
  -i accounting-api-v1.json \
  -g typescript-axios \
  -o ./generated/accounting-client

# Generate a C# client
dotnet tool run openapi \
  --input accounting-api-v1.json \
  --output AccountingClient.cs \
  --namespace Microtec.PublicApi.Accounting

Generated Clients vs Manual PublicApi Packages

The Microtec.PublicApi.* NuGet packages used for inter-service communication are generated from the OpenAPI spec using NSwag as part of the build pipeline. Do not hand-write inter-service client code — regenerate the packages when the API changes.

Internal Documentation — Microtec Platform Team