使用 Azure API 管理从身份提供者缓存 JWKS 以验证 JWT

Posted

技术标签:

【中文标题】使用 Azure API 管理从身份提供者缓存 JWKS 以验证 JWT【英文标题】:Caching JWKS from Identity Provider using Azure API management for validating JWT 【发布时间】:2019-02-26 23:36:59 【问题描述】:

以下代码通过在每次验证this 之后的每个请求时下载 JWKS 来验证 JWT 令牌

    <validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Error: expired token or invalid token" require-expiration-time="true" require-scheme="Bearer" require-signed-tokens="true">
        <openid-config url="https://IdentityProvider/oidc/.well-known/openid-configuration" />
        <audiences>
            <audience>aud id</audience>
        </audiences>
    </validate-jwt>

我的问题是如何缓存下面示例链接中的 JWKS 以避免每次都下载它,并且无需对 JWKS 进行硬编码,因为它会定期轮换。

https://demo.identityserver.io/.well-known/openid-configuration/jwks

https://openid-connect-eu.onelogin.com/oidc/certs

任何用于缓存和验证 JWT 的代码示例和链接都将不胜感激。

以下似乎相关但不是完整的示例。

https://docs.microsoft.com/en-us/azure/api-management/api-management-sample-cache-by-key

更新

为了清楚起见,我想缓存上面链接的 JWKS 中的内容以提高性能。

【问题讨论】:

你提到github.com/toddkitta/azure-content/blob/master/articles/…了吗? 谢谢,看看,让你知道 旧 UI 不可用。这是当前的:docs.microsoft.com/en-gb/azure/api-management/… 【参考方案1】:

如果有帮助,请查看以下示例 referred from here!

<policies>
    <inbound>
        <!-- Add your wcf relay address as the base URL below -->
        <set-backend-service base-url="" />
        <!-- verify if there is a relaytoken key stored in cache -->
        <cache-lookup-value key="@("relaytoken")" variable-name="relaytoken" />
        <choose>
            <!-- If there is no key stored in cache -->
            <when condition="@(!context.Variables.ContainsKey("relaytoken"))">
                <set-variable name="resourceUri" value="@(context.Request.Url.ToString())" />
                <!-- Retrieve Shared Access Policy key from  Name Value store -->
                <set-variable name="accessKey" value="accessKey" />
                <!-- Retrieve Shared Access Policy key name from  Name Value store -->
                <set-variable name="keyName" value="accessKeyName" />
                <!-- Generate the relaytoken key -->
                <set-variable name="relaytoken" value="@
                    TimeSpan sinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1);
                    string expiry =  Convert.ToString((int)sinceEpoch.TotalSeconds + 3600);
                    string resourceUri = (string)context.Variables["resourceUri"];
                    string stringToSign = Uri.EscapeDataString (resourceUri) + "\n" + expiry;
                    HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes((string)context.Variables["accessKey"]));
                    string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
                    string sasToken = String.Format("SharedAccessSignature sr=0&sig=1&se=2&skn=3",
                    Uri.EscapeDataString(resourceUri), Uri.EscapeDataString(signature), expiry, context.Variables["keyName"]);
                    return sasToken;
                    " />
                <!-- Store the relaytoken in the cache -->
                <cache-store-value key="relaytoken" value="@((string)context.Variables["relaytoken"])" duration="10" />
            </when>
        </choose>
        <!-- If the operation request uses json format, convert it to XML - Azure Relay expects XML format (based on WCF) -->
        <set-body template="liquid">
            <!-- set your body transformation here -->
        </set-body>
        <!-- Create the ServiceBusAuthorization header using the relaytoken as value -->
        <set-header name="ServiceBusAuthorization" exists-action="override">
            <value>@((string)context.Variables["relaytoken"])</value>
        </set-header>
        <!-- Set the content type to application/xml -->
        <set-header name="Content-Type" exists-action="override">
            <value>application/xml</value>
        </set-header>
        <base />
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
    <!-- If the operation responses uses json format, convert it from XML - Azure Relay will return XML format (based on WCF) -->
        <set-body template="liquid">
            <!-- set your body transformation here -->
        </set-body>
        <!-- Set the content type to application/json -->
        <set-header name="Content-Type" exists-action="override">
            <value>application/json</value>
        </set-header>
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

【讨论】:

缓存查找和存储可能是相关的。我想缓存链接的 JWKS 中的内容以提高性能。但我找不到我的案例的完整示例。【参考方案2】:

Azure API 管理服务内置支持使用资源 URL 作为密钥 (https://github.com/toddkitta/azure-content/blob/master/articles/api-management/api-management-sample-cache-by-key.md) 的 HTTP 响应缓存。您可以做的是将 openid-config url 设置为操作并自行控制缓存。另一种方法是引入自己的缓存服务。

【讨论】:

缓存响应不是我想要的这种情况。我想缓存链接的 JWKS 中的内容以提高性能。但我找不到我的案例的完整示例。【参考方案3】:

APIM 不会为每个请求下载开放 ID 配置。如果我没记错的话,它会每隔一小时定期下载、缓存和自动刷新。

【讨论】:

你的信息有链接吗? 此链接openid-connect-eu.onelogin.com/oidc/certs 没有告诉它何时过期的缓存控制。在这种情况下,当 JWK 发生变化时,如何使缓存失效? 我认为它没有明确记录在任何地方。不尊重缓存控制标头,一旦获取的数据使用一小时,然后刷新。要轮换密钥,您需要进行两步轮换:首先添加新密钥,然后在其签名的所有令牌都过期时删除旧密钥。还要确保您的令牌中没有“kid”声明,因为如果它与“kid”匹配,则只会使用单个密钥

以上是关于使用 Azure API 管理从身份提供者缓存 JWKS 以验证 JWT的主要内容,如果未能解决你的问题,请参考以下文章

为用户创建订阅的 Azure API 管理 REST 调用(缺失)

使用 Azure AD 通过 OAuth2 对 Azure API 管理进行身份验证

如何使用 Azure API 管理设计微服务架构中的身份验证和授权策略?

无法对 Azure 上的 API 管理执行资源所有者密码 OAuth2 身份验证

为啥外部使用者的 Azure API 管理中的客户端身份验证需要 PFX 文件?

如何将包含基本身份验证的 Azure API 应用程序导入 API 管理