Blog

Zero-downtime migration of API authentication systems

·4 min read

The authentication migration problem

Migrating an API’s authentication system is one of the highest-risk changes a platform team can make. Every caller needs to update their integration. The old auth mechanism needs to stay live during a transition period. If the cutover is abrupt, some callers break. If it’s too gradual, you’re maintaining two auth systems indefinitely.

The specific scenario comes up more often than you’d expect: switching from a shared API key to per-client keys, moving from OAuth2 to JWT verification, migrating between identity providers (Auth0 to Okta, or vice versa), upgrading from basic auth to bearer tokens. Each of these is a credential migration that affects every consumer.

The gateway layer is where you manage this transition — without touching your backend API and without requiring a coordinated flag-day cutover from your callers.

The abstraction principle

The key insight is that a gateway proxy decouples what callers present (proxy credentials) from what the upstream receives (target credentials). By controlling both sides independently, you can migrate one without affecting the other.

You can also run multiple proxies pointing at the same upstream target — one using the old auth mechanism, one using the new — allowing callers to migrate at their own pace while you retire the old proxy on a schedule.

Scenario 1: Migrating callers from a shared key to per-client keys

Before: every service uses the same shared API key to call your gateway. After: every service has its own credential, with scoped access rules.

Step 1: Create per-service credentials

POST /clients/{clientId}/credentials
{
  "credentialType": "proxy",
  "credentialAuthType": "key",
  "credentialName": "payments-service",
  "credentialRegion": "us-east-1",
  "credentialSecret": {
    "key": "rr_live_payments_xxxxxxxxxxxxxx"
  }
}
POST /clients/{clientId}/credentials
{
  "credentialType": "proxy",
  "credentialAuthType": "key",
  "credentialName": "reporting-service",
  "credentialRegion": "us-east-1",
  "credentialSecret": {
    "key": "rr_live_reporting_xxxxxxxxxxxxxx"
  }
}

Step 2: Create per-service proxies with the old shared key also valid

During transition, create proxies that accept both the old shared credential and the new per-service credential. Use a separate proxy per service with the new credential, but keep the old proxy live:

POST /clients/{clientId}/proxies
{
  "proxyName": "payments-service-proxy",
  "proxyRegion": "us-east-1",
  "proxyProxyCredentialId": "<payments-service-credential-id>",
  "proxyTargetId": "<upstream-target-id>",
  "proxyTargetCredentialId": "<upstream-key-id>",
  "proxyDefaultRuleEffect": "deny",
  "proxyAlias": "payments"
}

Step 3: Migrate services one at a time, then retire the shared proxy

Each service is updated to use its new credential and proxy alias. Once all services are migrated and confirmed in the request log, the old shared-key proxy is deleted — callers using the old key immediately get a 401.

Scenario 2: Migrating from API key to JWT verification

Step 1: Create the new jwtVerify proxy credential

POST /clients/{clientId}/credentials
{
  "credentialType": "proxy",
  "credentialAuthType": "jwtVerify",
  "credentialName": "new-idp-jwt-verify",
  "credentialRegion": "us-east-1",
  "credentialSecret": {
    "jwksUri": "https://new-idp.example.com/.well-known/jwks.json",
    "audience": "https://api.myapp.com",
    "issuer": "https://new-idp.example.com/"
  }
}

Step 2: Create a new proxy using the JWT credential

Keep the old API-key proxy running. Create a new proxy pointing at the same target, using the JWT credential:

POST /clients/{clientId}/proxies
{
  "proxyName": "api-jwt-auth",
  "proxyRegion": "us-east-1",
  "proxyProxyCredentialId": "<new-idp-jwt-verify-id>",
  "proxyTargetId": "<same-upstream-target-id>",
  "proxyTargetCredentialId": "<upstream-credential-id>",
  "proxyDefaultRuleEffect": "deny",
  "proxyAlias": "api-v2"
}

Step 3: Migrate callers to the new proxy alias

Point new callers at api-v2. Migrate existing callers on their own schedule. Monitor traffic via telemetry:

GET /clients/{clientId}/proxies/{oldProxyId}/telemetry?interval=day&limit=30
GET /clients/{clientId}/proxies/{newProxyId}/telemetry?interval=day&limit=30

When the old proxy’s daily request count drops to zero for a sustained period, it’s safe to decommission.

Scenario 3: Rotating the upstream target credential

Target credential rotation (e.g. rotating a vendor API key) is the simplest case and can be done with zero downtime:

Step 1: Create the new target credential

POST /clients/{clientId}/credentials
{
  "credentialType": "target",
  "credentialAuthType": "bearer",
  "credentialName": "hubspot-key-v2",
  "credentialRegion": "us-east-1",
  "credentialSecret": {
    "token": "pat-na1-xxxxxxxxxxxxxxxxxxxxxxxx"
  }
}

Step 2: Update the proxy’s target credential ID

PUT /clients/{clientId}/proxies/{proxyId}
{
  "proxyName": "hubspot-crm",
  "proxyRegion": "us-east-1",
  "proxyProxyCredentialId": "<existing-proxy-credential-id>",
  "proxyTargetId": "<hubspot-target-id>",
  "proxyTargetCredentialId": "<hubspot-key-v2-id>"
}

The next request through the proxy uses the new credential. No restart, no deployment, no change on the caller side. Verify traffic in the request log, then delete the old credential.

Using the proxy alias to abstract the endpoint

The proxyAlias field gives callers a stable URL that doesn’t change even when the underlying proxy configuration changes. If you later decide to point the alias at a completely different target (e.g. migrating from vendor A to vendor B), callers continue using the same alias — the gateway handles the remapping:

PUT /clients/{clientId}/proxies/{proxyId}
{
  "proxyAlias": "crm-api",
  "proxyTargetId": "<new-vendor-target-id>",
  "proxyTargetCredentialId": "<new-vendor-credential-id>"
}

The caller’s integration doesn’t change. The vendor does.

Monitoring migration progress

During any migration, telemetry is your migration scorecard. Declining traffic on the old proxy, growing traffic on the new one, and no increase in error rates confirms the migration is proceeding correctly:

GET /clients/{clientId}/proxies/{legacyProxyId}/telemetry?interval=day&limit=14
GET /clients/{clientId}/proxies/{newProxyId}/telemetry?interval=day&limit=14

When legacy proxy traffic reaches zero and holds for a week, the migration is complete.

Next steps

The combination of proxy aliases, multiple proxies per target, and independent credential rotation makes authentication migrations significantly lower risk. Read the RequestRocket documentation or start for free.

Enhance ISO 27001
Enhance SOC 2
Enhance GDPR
Enhance HIPAA

Add outbound API security
without changing code

Start on your own or talk to our team about improving the security of every API call you make.