1
votes

We want to protect API operation call with the validate-jwt policy but I have an issue when I use the required-claims to check the scopes. Example : I have a token with a scope including several values like "xxx.READ xxx.WRITE yyy.READ yyy.WRITE ..." For a specific operation I want to use the validate-jwt policy to check if the token contains the scope linked like :

    <required-claims>
            <claim name="scp" match="any">
                <value>xxx.READ</value>
            </claim>
    </required-claims>

But the validation always fails because of the multiple values in the scp... How can I check this claim? Do I have to extract the scp values before and if yes how can I do that?

Thanks in advance

3

3 Answers

4
votes

Multi-value claims should be represented as arrays inside JWT token:

"scp": ["xxx.READ", "xxx.WRITE", "yyy.READ", "yyy.WRITE"]

this scenarios is completely supported with policy configuration that you've posted.

But if you represent them as a single string ("xxx.READ xxx.WRITE yyy.READ yyy.WRITE") then you have to use manual expressions to validate such token, something along these lines:

<choose>
    <when condition="@(context.Request.Headers.ContainsKey("Authorization"))">
        <set-variable name="token" value="@(context.Request.Headers.GetValueOrDefault("Authorization", string.Empty).AsJwt())" />
        <choose>
            <when condition="@{
if (context.Variables["token"] == null) {
    return false;
}

var scp = ((Jwt)context.Variables["token"]).Claims.GetValueOrDefault("scp", (string[])null);
if (scp == null || scp.Length == 0) {
    return false;
}

return scp.Any(c => c.Contains("xxx.READ"));
            }">
                <return-response response-variable-name="existing response variable">
                    <set-status code="401" reason="Unauthorized" />
                </return-response>
            </when>
        </choose>
    </when>
    <otherwise>
        <return-response response-variable-name="existing response variable">
            <set-status code="401" reason="Unauthorized" />
        </return-response>
    </otherwise>
</choose>
1
votes

I'm also setting up API Management with B2C and I have exactly the same problem. The solution that i'm using is easier.

<policies>
    <inbound>
        <base />
        <validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="The Scope claim does not contain the read permission." require-expiration-time="true" require-scheme="Bearer" require-signed-tokens="true" clock-skew="0">
            <openid-config url="{{your-openid-config-url}}" />
            <required-claims>
                <claim name="scp" match="any" separator=" ">
                    <value>read</value>
                </claim>
            </required-claims>
        </validate-jwt>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>
1
votes

To validate that a scope is present within the JWT, use the separator attribute set to "," since this is a multi-value claim like this: "scope": ["api1.write","roles","profile"] .

For example:

<required-claims>
   <claim name="scope" match="all" separator=",">
        <value>api1.write</value>
   </claim>
</required-claims>