keycloak-connect
Version:
Keycloak Connect Middleware
97 lines (88 loc) • 2.96 kB
JavaScript
/*!
* Copyright 2014 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const URL = require('url')
const http = require('http')
const https = require('https')
const jwkToPem = require('jwk-to-pem')
/**
* Construct a Rotation instance
*
* @param {Config} config Config object.
*
* @constructor
*/
function Rotation (config) {
this.realmUrl = config.realmUrl
this.minTimeBetweenJwksRequests = config.minTimeBetweenJwksRequests
this.jwks = []
this.lastTimeRequesTime = 0
}
Rotation.prototype.retrieveJWKs = function retrieveJWKs (callback) {
const url = this.realmUrl + '/protocol/openid-connect/certs'
const options = URL.parse(url); // eslint-disable-line
options.method = 'GET'
const promise = new Promise((resolve, reject) => {
const req = getProtocol(options).request(options, (response) => {
if (response.statusCode < 200 || response.statusCode >= 300) {
return reject(new Error('Error fetching JWK Keys'))
}
let json = ''
response.on('data', (d) => (json += d.toString()))
response.on('end', () => {
const data = JSON.parse(json)
if (data.error) reject(data)
else resolve(data)
})
})
req.on('error', reject)
req.end()
})
return nodeify(promise, callback)
}
Rotation.prototype.getJWK = function getJWK (kid) {
const key = this.jwks.find((key) => { return key.kid === kid })
if (key) {
return new Promise((resolve, reject) => {
resolve(jwkToPem(key))
})
}
const self = this
// check if we are allowed to send request
const currentTime = new Date().getTime() / 1000
if (currentTime > this.lastTimeRequesTime + this.minTimeBetweenJwksRequests) {
return this.retrieveJWKs()
.then(publicKeys => {
self.lastTimeRequesTime = currentTime
self.jwks = publicKeys.keys
const convertedKey = jwkToPem(self.jwks.find((key) => { return key.kid === kid }))
return convertedKey
})
} else {
console.error('Not enough time elapsed since the last request, blocking the request')
}
}
Rotation.prototype.clearCache = function clearCache () {
this.jwks.length = 0
}
const getProtocol = (opts) => {
return opts.protocol === 'https:' ? https : http
}
const nodeify = (promise, cb) => {
if (typeof cb !== 'function') return promise
return promise.then((res) => cb(null, res)).catch((err) => cb(err))
}
module.exports = Rotation