【问题标题】:How to maintain a cache of the public keys from Google's OpenID Connect discovery document如何维护来自 Google 的 OpenID Connect 发现文档的公钥缓存
【发布时间】:2017-01-24 15:28:51
【问题描述】:

我正在对从跨源 ajax 客户端接收的 json Web 令牌进行 Node.js 服务器端验证。据推测,令牌是由Google OpenID Connect 生成的,其中声明如下:

要使用 Google 的 OpenID Connect 服务,您应该将 Discovery-document URI 硬编码到您的应用程序中。您的应用程序获取文档,然后根据需要从中检索端点 URI。

您可以通过缓存 Discovery 文档中的值来避免 HTTP 往返。使用标准 HTTP 缓存标头,应予以尊重。

来源: https://developers.google.com/identity/protocols/OpenIDConnect#discovery

我编写了以下函数,该函数使用 request.js 获取键,并使用 moment.js 将一些时间戳属性添加到我存储缓存键的 keyCache 字典中。该函数在服务器启动时调用。

function cacheWellKnownKeys(uri) {
  var openid = 'https://accounts.google.com/.well-known/openid-configuration';

  // get the well known config from google
  request(openid, function(err, res, body) {
    var config               = JSON.parse(body);
    var jwks_uri             = config.jwks_uri;
    var timestamp            = moment();

    // get the public json web keys
    request(jwks_uri, function(err, res, body) {
      keyCache.keys          = JSON.parse(body).keys;
      keyCache.lastUpdate    = timestamp;
      keyCache.timeToLive    = timestamp.add(12, 'hours');
    });
  });
}

成功缓存了密钥之后,我现在关心的是如何随着时间的推移有效地维护缓存。

由于 Google 很少更改其公钥(大约每天一次),因此您可以缓存它们,并且在绝大多数情况下执行本地验证。

来源:https://developers.google.com/identity/protocols/OpenIDConnect#validatinganidtoken

由于 Google 每天都在更改其公钥,我对 keyCachetimestamptimeToLive 属性的想法是做以下两件事之一:

  1. 每 12 小时设置一次超时以更新缓存
  2. 处理 Google 在我的 12 小时更新周期之间更改其公钥的情况。我端第一次失败的令牌验证会触发密钥缓存的刷新,然后是最后一次尝试验证令牌。

这似乎是一种可行的工作算法,直到我考虑到大量无效令牌请求导致在尝试更新缓存时重复往返于众所周知的配置和公钥。

也许有更好的方法可以减少网络开销。上面第一句话中的这一行可能与开发更有效的解决方案有关,但我不确定该怎么做:Standard HTTP caching headers are used and should be respected.

我想我的问题真的只是这个......

我是否应该利用 Google 发现文档中的 HTTP 缓存标头来开发更高效的缓存解决方案?这将如何运作?

【问题讨论】:

    标签: node.js caching google-openid


    【解决方案1】:

    discovery document 具有属性jwks_uri,这是另一个带有公钥的文档的网址。另一份文件是 Google 在他们说...时所指的文件...

    使用标准 HTTP 缓存标头,应予以尊重。

    对该地址 https://www.googleapis.com/oauth2/v3/certs 的 HTTP HEAD 请求显示以下标头:

    HTTP/1.1 200 OK
    Expires: Wed, 25 Jan 2017 02:39:32 GMT
    Date: Tue, 24 Jan 2017 21:08:42 GMT
    Vary: Origin, X-Origin
    Content-Type: application/json; charset=UTF-8
    X-Content-Type-Options: nosniff
    x-frame-options: SAMEORIGIN
    x-xss-protection: 1; mode=block
    Content-Length: 1472
    Server: GSE
    Cache-Control: public, max-age=19850, must-revalidate, no-transform
    Age: 10770
    Alt-Svc: quic=":443"; ma=2592000; v="35,34"
    X-Firefox-Spdy: h2
    

    以编程方式从 request.js 生成的响应对象中访问这些标头字段并从中解析 max-age 值,如下所示:

    var cacheControl = res.headers['cache-control'];      
    var values = cacheControl.split(',');
    var maxAge = parseInt(values[1].split('=')[1]);
    

    maxAge 值以秒为单位。然后的想法是根据 maxAge(毫秒转换的 1000 倍)设置超时,并在每次超时完成时递归刷新缓存。这解决了在每次无效授权尝试时刷新缓存的问题,并且您可以删除正在使用 moment.js 执行的时间戳内容

    我建议使用以下函数来处理这些众所周知的密钥的缓存。

    var keyCache = {};
    
    /**
     * Caches Google's well known public keys
     */
    function cacheWellKnownKeys() {
        var wellKnown= 'https://accounts.google.com/.well-known/openid-configuration';
    
        // get the well known config from google
        request(wellKnown, function(err, res, body) {
            var config    = JSON.parse(body);
            var address   = config.jwks_uri;
    
            // get the public json web keys
            request(address, function(err, res, body) {
    
                keyCache.keys = JSON.parse(body).keys;
    
                // example cache-control header: 
                // public, max-age=24497, must-revalidate, no-transform
                var cacheControl = res.headers['cache-control'];      
                var values = cacheControl.split(',');
                var maxAge = parseInt(values[1].split('=')[1]);
    
                // update the key cache when the max age expires
                setTimeout(cacheWellKnownKeys, maxAge * 1000);    
            });
        });
    }
    

    【讨论】:

    猜你喜欢
    • 2015-09-26
    • 2018-07-13
    • 2018-06-26
    • 1970-01-01
    • 1970-01-01
    • 2017-11-09
    • 1970-01-01
    • 1970-01-01
    • 2017-05-18
    相关资源
    最近更新 更多