DeMaki
DeMaki

Reputation: 491

APIM Policy validate-jwt cannot validate Azure AD B2C token created via Azure Front Door

In my solution I have Azure AD B2C and recently I added Azure Front Door to allow users to access Azure AD B2C via a custom domain. (see https://learn.microsoft.com/en-us/azure/active-directory-b2c/custom-domain?pivots=b2c-custom-policy)

Users of my application can now obtain a JWT token from Azure AD B2C using either the original B2C domain URL, or the custom domain (via Azure Front Door).

I can successfully create a valid JWT token using both domains, and when I compare them the only difference is the iss value.

When using the original Azure AD B2C domain name (domain and tenant names are redacted):

"iss": "https://myappname.b2clogin.com/myappname.onmicrosoft.com/v2.0/"

When using the custom domain via Azure Front Door:

"iss": "https://login.myappname.nl/00000000-0000-0000-0000-000000000000/v2.0/"

This means I have 2 distinct addresses for my Open ID Connect well known config:

In Azure API Management I have setup a policy to validate the JWT token. With the introduction of the custom domain I now need to support both tokens.

<policies>
    <inbound>
        <set-variable name="iss" value="@(context.Request.Headers.GetValueOrDefault("Authorization", "")?.Split(' ')?.ElementAtOrDefault(1)?.AsJwt()?.Claims["iss"]?.FirstOrDefault() ?? "")" />
        <choose>
            <when condition="@(context.Variables.GetValueOrDefault("iss", "").StartsWith("https://login"))">
                <validate-jwt header-name="Authorization">
                    <openid-config url="https://login.myappname.nl/00000000-0000-0000-0000-000000000000/v2.0/.well-known/openid-configuration?p=B2C_1A_MYAPPVERIFICATIONANDSIGNUP" />
                    <audiences>
                        <audience>[redacted_audience_id]</audience>
                    </audiences>
                </validate-jwt>
            </when>
            <otherwise>
                <validate-jwt header-name="Authorization">
                    <openid-config url="https://myappname.b2clogin.com/myappname.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=B2C_1A_MYAPPVERIFICATIONANDSIGNUP" />
                    <audiences>
                        <audience>[redacted_audience_id]</audience>
                    </audiences>
                </validate-jwt>
            </otherwise>
        </choose>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

I first read the iss value to determine the Open Id config URL to be used in the validate-jwt element. If the iss starts with https://login I validate against the open-id config URL of the custom domain, otherwise I use the URL from the original B2C domain. Note: the audience is the same for both.

A call with a JWT token from the original B2C domain works and successfully passes the policy validation.

A call with a JWT token from the custom domain fails with a 401 error response.

When I look in Application Insights with the following query

// Last 100 failed requests 
ApiManagementGatewayLogs
| where TimeGenerated > ago(1d)
| where IsRequestSuccess == false
| top 100 by TimeGenerated desc| where ResponseCode >= 400

I see my failed request with these error details:

Column Value
LastErrorSource validate-jwt
LastErrorScope product
LastErrorSection inbound
LastErrorReason TokenSignatureKeyNotFound
LastErrorMessage IDX10500: Signature validation failed. No security keys were provided to validate the signature.

I am puzzled why the policy in Azure API Management accept a token from the original B2C domain but not the one from my custom domain.

Upvotes: 0

Views: 480

Answers (1)

DeMaki
DeMaki

Reputation: 491

Many thank to the comment made by @VitaliyKurokhtin. Apparently <validate-jwt> allows for multiple <openid-config> elements, so instead of deciding what to do based on the iss value I could simply add 2 openid-config urls and having just a single audience.

<policies>
<inbound>
    <validate-jwt header-name="Authorization">
        <openid-config url="https://login.myappname.nl/00000000-0000-0000-0000-000000000000/v2.0/.well-known/openid-configuration?p=B2C_1A_MYAPPVERIFICATIONANDSIGNUP" />
        <openid-config url="https://myappname.b2clogin.com/myappname.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=B2C_1A_MYAPPVERIFICATIONANDSIGNUP" />
        <audiences>
            <audience>[redacted_audience_id]</audience>
        </audiences>
    </validate-jwt>
</inbound>
<backend>
    <base />
</backend>
<outbound>
    <base />
</outbound>
<on-error>
    <base />
</on-error>

Upvotes: 1

Related Questions