Node.js 如何维护Google的公钥缓存';s OpenID连接发现文档

Node.js 如何维护Google的公钥缓存';s OpenID连接发现文档,node.js,caching,google-openid,Node.js,Caching,Google Openid,我正在进行Node.js服务器端验证,验证从跨源ajax客户端接收的json web令牌。据推测,令牌是由以下状态生成的: 要使用Google的OpenID Connect服务,您应该将其硬编码到应用程序中。应用程序获取文档,然后根据需要从中检索端点URI 您可以通过缓存发现文档中的值来避免HTTP往返。使用标准HTTP缓存头,应遵守这些头 资料来源: 我编写了以下函数,该函数使用request.js获取密钥,并使用moment.js向存储缓存密钥的keyCache字典添加一些时间戳属性。此函数

我正在进行Node.js服务器端验证,验证从跨源ajax客户端接收的json web令牌。据推测,令牌是由以下状态生成的:

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

您可以通过缓存发现文档中的值来避免HTTP往返。使用标准HTTP缓存头,应遵守这些头

资料来源:

我编写了以下函数,该函数使用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');
    });
  });
}
在成功缓存密钥之后,我现在关心的是如何随着时间的推移有效地维护缓存

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

资料来源:

由于谷歌每天都在更换公钥,我对
keyCache
timestamp
timeToLive
属性的想法是做两件事之一:

  • 每12小时设置一次超时以更新缓存
  • 处理谷歌在我的12小时更新周期之间更改其公钥的情况。my end上第一次失败的令牌验证会触发密钥缓存刷新,然后最后一次尝试验证令牌
  • 这似乎是一种可行的工作算法,直到我考虑到无效令牌请求的攻击,导致在试图更新高速缓存时,反复访问熟知的配置和公钥。

    也许有更好的方法可以减少网络开销。上面第一句话中的这一行可能与开发更高效的解决方案有关,但我不确定该怎么办:
    使用了标准HTTP缓存头,应该遵守。

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

    我应该利用谷歌发现文档中的HTTP缓存头来开发更高效的缓存解决方案吗?如何工作?

    具有属性
    jwks_uri
    ,它是另一个带有公钥的文档的网址。这另一份文件是谷歌提到的,他们说

    使用标准HTTP缓存头,应遵守这些头

    对该地址的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生成的响应对象以编程方式访问这些头字段,并从中解析最大年龄值,如下所示:

    var cacheControl = res.headers['cache-control'];      
    var values = cacheControl.split(',');
    var maxAge = parseInt(values[1].split('=')[1]);
    
    最大值以秒为单位测量。然后,我们的想法是基于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);    
            });
        });
    }