Skip to content

Subscription Key Authentication

Configure APIM subscription key authentication for Experience APIs that serve legacy server-to-server clients.

Property Value
Goal Allow legacy applications to call your API using an APIM subscription key
Prerequisites Experience API deployed (is_experience_api: true), variable group with keys
Time Estimate 15 minutes
Difficulty Beginner

Overview

Some legacy applications cannot obtain OAuth tokens and need a simpler authentication mechanism. The subscription_key policy type configures your Experience API to accept APIM subscription keys instead of JWT tokens.

The pipeline handles the full lifecycle:

  1. Terraform creates the APIM subscription using your keys from the variable group
  2. Crypto registration (opt-in) stores the keys in the Crypto API and records cipher IDs in the Environment Service so legacy .NET apps can retrieve them via _environmentClient.GetSecureSetting()

When to Use

Scenario Recommended Policy Type
Standard user-delegated or service-to-service standard
Filevine webhook integration filevine
Legacy app that cannot use OAuth subscription_key

Use Only When Necessary

Subscription key auth provides weaker security guarantees than OAuth. It does not carry user identity or granular scopes. Only use this when the calling application cannot support OAuth flows.

How It Works

sequenceDiagram
    participant Legacy App
    participant Front Door
    participant APIM
    participant Backend API

    Legacy App->>Front Door: GET /api/exp/your-app/<br/>Ocp-Apim-Subscription-Key: {key}
    Front Door->>APIM: Forward request + X-Azure-FDID header
    APIM->>APIM: Validate subscription key
    APIM->>APIM: Validate Front Door ID
    APIM->>APIM: Set x-forward-tenant-origin: corp
    APIM->>Backend API: Forward with managed identity token
    Backend API-->>APIM: Response
    APIM-->>Front Door: Response
    Front Door-->>Legacy App: Response

Key differences from standard policy:

  • No JWT validation — APIM validates the subscription key natively
  • No scopes or roles — all requests are treated as corporate internal
  • Managed identity — APIM still authenticates to the backend using managed identity

Step 1: Configure vars.yml

In your Experience API's vars.yml, set api_policy_type to subscription_key:

# infra/api/vars.yml

application_name: your-app-name
application_type: Api
api_type: Experience
owner: YourTeam
project_id: your-project-id
api_policy_type: subscription_key

Experience APIs Only

The subscription_key policy type requires api_type: Experience (which sets is_experience_api: true). Terraform validation will fail if you use it with other API types.


Step 2: Add Subscription Keys to Variable Group

Store your subscription keys in an Azure DevOps variable group so the pipeline can pass them to Terraform and (optionally) register them in the Crypto API.

Generate Keys

Use a password generator (e.g., Keeper) to create a unique key for each environment. Requirements:

  • Letters, numbers, hyphens (-), and underscores (_) only — avoid all other symbols (the pipeline is sensitive to special characters)
  • Minimum 32 characters, maximum 256
  • Different key per environment — do not reuse the same key across TEST, QA, UAT, and PROD

Add Keys to Variable Group

  1. Navigate to Azure DevOps → Pipelines → Library
  2. Open your application's variable group (the name must match secretsVariableGroupName in your pipeline — typically your project ID, e.g., it-api-exp-yourapp)
  3. Add environment-prefixed entries and mark each as Secret:
Variable Name Type Description
[TEST]primary_key Secret Primary key for TEST
[QA]primary_key Secret Primary key for QA
[UAT]primary_key Secret Primary key for UAT
[PROD]primary_key Secret Primary key for PROD

Optionally add secondary_key entries for key rotation support:

Variable Name Type Description
[TEST]secondary_key Secret Secondary key for TEST
[PROD]secondary_key Secret Secondary key for PROD

Critical

If you rename or use a different variable group, its name must match the secretsVariableGroupName value in your pipeline YAML file.

Treat Subscription Keys as Secrets

Subscription keys grant full access to your API. Mark them as Secret in the variable group and never commit them to source control.


Step 3: Configure Pipeline

Add secretsVariableGroupName to both your main pipeline and PR pipeline:

extends:
  template: azure-dotnet-api-v3.yml@templates
  parameters:
    applicationName: ${{ variables.ApplicationName }}
    projectId: ${{ variables.ProjectId }}
    dotNetVersion: '10.x'
    secretsVariableGroupName: 'your-project-id'  # Variable group from Step 2

Enable Crypto Registration

If legacy .NET callers need to retrieve the subscription key at runtime via _environmentClient.GetSecureSetting(), also add the cryptoRegistration parameter. This stores the keys in the Crypto API and records the cipher IDs in the Environment Service after each deployment.

extends:
  template: azure-dotnet-api-v3.yml@templates
  parameters:
    applicationName: ${{ variables.ApplicationName }}
    projectId: ${{ variables.ProjectId }}
    dotNetVersion: '10.x'
    secretsVariableGroupName: 'your-project-id'
    cryptoRegistration:
      enabled: true
      systemName: 'your-project-id'   # Name of the Crypto system
      systemType: 'webapp'             # console, windowsservice, webapp, guidewire, desktopapp, script, other
Parameter Required Default Description
enabled Yes false Enable crypto registration
systemName Yes '' Name of the system in Crypto API
systemType No 'webapp' System type (console, windowsservice, webapp, guidewire, desktopapp, script, other)

See Settings and Secrets for full details on the secretsVariableGroupName parameter and the [ENV] naming convention.


Step 4: Deploy

Commit and push your changes. The pipeline:

  1. Loads keys from the variable group (filtered by environment)
  2. Passes them to Terraform via application_secrets
  3. Creates the APIM subscription with your keys
  4. Sets subscription_required = true on the APIM API
  5. Applies the subscription key policy (no JWT validation)

If crypto registration is enabled, the pipeline additionally:

  1. Creates or updates a Crypto system for your application
  2. Registers each key as an encrypted cipher in the Crypto API
  3. Stores the cipher IDs as secure settings in the Environment Service

Settings are stored under:

  • Track: mapped from the pipeline environment (platformdev/test → enttest1, qa → np13, uat → np16, prod → prod)
  • Setting group: your projectId
  • Keys: primary_subscription_key, secondary_subscription_key

You can verify the registered settings in EnvironmentBoss.


Step 5: Call the API

Legacy callers must send the subscription key in the Ocp-Apim-Subscription-Key header through Front Door:

GET https://app-int-{environment}.saif.com/api/exp/{app-name}/{route}
Ocp-Apim-Subscription-Key: {your-subscription-key}

Retrieving Keys via Environment Service

If crypto registration is enabled, legacy .NET callers can retrieve the decrypted subscription key using the Environment Client and System Hash Service.

Required NuGet package (from the SAIF internal feed):

<PackageReference Include="SAIF.NetCore.ServiceContracts" />

This package provides IEnvironmentClient, ISystemHashService, and SystemType. Register both services in your DI container and inject them into the class that needs to retrieve keys:

public class SubscriptionKeyProvider
{
    private readonly IEnvironmentClient _environmentClient;
    private readonly ISystemHashService _systemHashService;

    public SubscriptionKeyProvider(
        IEnvironmentClient environmentClient,
        ISystemHashService systemHashService)
    {
        _environmentClient = environmentClient;
        _systemHashService = systemHashService;
    }

    public string GetPrimaryKey()
    {
        // Compute time-sensitive system hash
        var hash = _systemHashService.ComputeHash(
            SystemType.WebApp, _environmentClient.ApplicationName);

        // Retrieve decrypted key — returns the original subscription key value
        return _environmentClient.GetSecureSetting(
            "primary_subscription_key",
            "your-project-id",  // setting group = projectId
            hash);
    }
}

System Hash Timeout

The system hash is time-sensitive (~90 second window). Compute the hash immediately before each GetSecureSetting call.


Key Rotation

To rotate subscription keys without downtime:

  1. Generate a new key value
  2. Update the secondary_key entry in your variable group with the new value
  3. Deploy — APIM and Crypto/Environment Service are updated with the new secondary key
  4. Distribute the secondary key to all callers
  5. Once callers have switched, swap: move the new value to primary_key, generate another for secondary_key
  6. Deploy again to finalize

Troubleshooting

401 SubscriptionKeyInvalid

Cause Solution
Wrong key value Verify the key matches what's in your variable group for the target environment
Missing header Ensure Ocp-Apim-Subscription-Key header is present and not empty
Key not deployed Confirm the pipeline ran successfully after adding keys to the variable group

403 Header X-Azure-FDID Not Found

Cause Solution
Calling APIM directly Requests must go through Front Door, not directly to APIM

500 BackendConnectionFailure

Cause Solution
Backend app not deployed Deploy your application before testing
Mock backend not configured Send x-mocking: false header to route to the real backend

Crypto Registration Failures

Cause Solution
primary_key not found in application_secrets Add [ENV]primary_key entries to your variable group
Unknown environment Verify DeploymentEnvironmentShortName maps to a known track
Crypto API timeout The Crypto API may need a warm-up — the pipeline retries automatically