Share via

How to validate JWT with only a public key

Neščivera Ján (ERNI) 45 Reputation points
2026-05-12T09:11:55.81+00:00

Hello,

we have an APIM policy attempting to validate incoming JWT using public key modulus, exponent and id.

This policy however seems to not validate JWT signature for some reason. When I change key modulus, exponent or id, the validation still passes and request is forwarder to backend. If I change audience or issuer in the policy, the validation fails as expected.

What is the proper way to validate incoming JWT when we only have acess to public key of the identity provider?

Azure API Management
Azure API Management

An Azure service that provides a hybrid, multi-cloud management platform for APIs.


1 answer

Sort by: Most helpful
  1. AI answer

    2026-05-12T09:12:17.8233333+00:00

    Use the validate-jwt policy with an asymmetric issuer signing key configured from the public key. The signature is validated against the public key; if the modulus/exponent are wrong, validation must fail.

    To validate with only a public key, configure one of these options in the validate-jwt policy:

    1. Use an OpenID configuration endpoint (recommended)
    <validate-jwt header-name="Authorization" require-scheme="Bearer">
      <openid-config url="https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration" />
      <audiences>
        <audience>api://your-api-app-id</audience>
      </audiences>
      <issuers>
        <issuer>https://login.microsoftonline.com/{tenant}/v2.0</issuer>
      </issuers>
    </validate-jwt>
    

    openid-config causes API Management to pull the JWKS (public keys) from the identity provider and use them to validate the JWT signature. This is the simplest and most robust way when only the public key is available.

    1. Use modulus and exponent directly

    If the OpenID configuration endpoint cannot be used, specify the RSA public key via issuer-signing-keys with n and e:

    <validate-jwt header-name="Authorization" require-scheme="Bearer">
      <issuer-signing-keys>
        <key id="your-kid" n="{base64url-modulus}" e="{base64url-exponent}" />
      </issuer-signing-keys>
      <audiences>
        <audience>api://your-api-app-id</audience>
      </audiences>
      <issuers>
        <issuer>https://your-issuer</issuer>
      </issuers>
    </validate-jwt>
    

    Key points to ensure signature is actually validated:

    1. Do not disable signing

    Make sure require-signed-tokens is not set to false:

    <validate-jwt header-name="Authorization" require-scheme="Bearer" require-signed-tokens="true">
      ...
    </validate-jwt>
    

    If require-signed-tokens is false, the token is accepted without signature validation, which matches the behavior described (changing key values has no effect).

    1. Configure issuer-signing-keys correctly

    Within issuer-signing-keys:

    • Use <key id="..." n="..." e="..." /> for an RSA public key.
    • n and e must be Base64url-encoded values of the modulus and exponent.
    • If id is set, it is matched to the token’s kid claim; if no kid matches, API Management will try all keys.

    If the key element is misconfigured but require-signed-tokens is false, the token still passes. With require-signed-tokens="true", a mismatched n/e causes validation to fail.

    1. Ensure other required elements are present

    For full validation, configure:

    <audiences>
      <audience>...</audience>
    </audiences>
    <issuers>
      <issuer>...</issuer>
    </issuers>
    

    and keep require-expiration-time at its default true unless there is a specific reason to allow tokens without exp.

    1. Alternative: certificate-based public key

    If the public key is available as a certificate uploaded to API Management, use certificate-id instead of n/e:

    <issuer-signing-keys>
      <key certificate-id="your-cert-id" />
    </issuer-signing-keys>
    

    This also validates the signature using the public key from the certificate.

    In summary, to ensure the JWT signature is actually validated when only a public key is available, configure validate-jwt with either openid-config or issuer-signing-keys (n/e or certificate-id) and keep require-signed-tokens="true". If changing modulus/exponent has no effect, the policy is likely not using those keys (for example, because require-signed-tokens is disabled or the token is not signed with an asymmetric algorithm supported by the policy).


    References:

    AI-generated content may be incorrect. Read our transparency notes for more information.

    Was this answer helpful?

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.