keycloak-connect
Version:
Keycloak Connect Middleware
173 lines (149 loc) • 5.06 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 path = require('path')
const fs = require('fs')
/**
* Construct a configuration object.
*
* A configuration object may be constructed with either
* a path to a `keycloak.json` file (which defaults to
* `$PWD/keycloak.json` if not present, or with a configuration
* object akin to what parsing `keycloak.json` provides.
*
* @param {String|Object} config Configuration path or details.
*
* @constructor
*/
function Config (config) {
if (!config) {
config = path.join(process.cwd(), 'keycloak.json')
}
if (typeof config === 'string') {
this.loadConfiguration(config)
} else {
this.configure(config)
}
}
/**
* Load configuration from a path.
*
* @param {String} configPath Path to a `keycloak.json` configuration.
*/
Config.prototype.loadConfiguration = function loadConfiguration (configPath) {
const json = fs.readFileSync(configPath)
const config = JSON.parse(json.toString())
this.configure(config)
}
/**
* Configure this `Config` object.
*
* This will set the internal configuration details. The details
* may come from a `keycloak.json` formatted object (with names such
* as `auth-server-url`) or from an existing `Config` object (using
* names such as `authServerUrl`).
*
* @param {Object} config The configuration to instill.
*/
Config.prototype.configure = function configure (config) {
/**
* Tries to resolve environment variables in the given value in case it is of type "string", else the given value is returned.
* Environment variable references look like: '${env.MY_ENVIRONMENT_VARIABLE}', optionally one can configure a fallback
* if the referenced env variable is not present. E.g. '${env.NOT_SET:http://localhost:8080}' yields 'http://localhost:8080'.
*
* @param value
* @returns {*}
*/
function resolveValue (value) {
if (typeof value !== 'string') {
return value
}
// "${env.MY_ENVIRONMENT_VARIABLE:http://localhost:8080}".replace(/\$\{env\.([^:]*):?(.*)?\}/,"$1--split--$2").split("--split--")
const regex = /\$\{env\.([^:]*):?(.*)?\}/
// is this an environment variable reference with potential fallback?
if (!regex.test(value)) {
return value
}
const tokens = value.replace(regex, '$1--split--$2').split('--split--')
const envVar = tokens[0]
const envVal = process.env[envVar]
const fallbackVal = tokens[1]
return envVal || fallbackVal
}
/**
* Realm ID
* @type {String}
*/
this.realm = resolveValue(config.realm || config.realm)
/**
* Client/Application ID
* @type {String}
*/
this.clientId = resolveValue(config.resource || config['client-id'] || config.clientId)
/**
* Client/Application secret
* @type {String}
*/
this.secret = resolveValue((config.credentials || {}).secret || config.secret)
/**
* If this is a public application or confidential.
* @type {String}
*/
this.public = resolveValue(config['public-client'] || config.public || false)
/**
* Authentication server URL
* @type {String}
*/
this.authServerUrl = (resolveValue(config['auth-server-url'] || config['server-url'] || config.serverUrl || config.authServerUrl) || '').replace(/\/*$/gi, '')
/**
* Root realm URL.
* @type {String}
*/
this.realmUrl = this.authServerUrl + '/realms/' + this.realm
/**
* Root realm admin URL.
* @type {String} */
this.realmAdminUrl = this.authServerUrl + '/admin/realms/' + this.realm
/**
* How many minutes before retrying getting the keys.
* @type {Integer}
*/
this.minTimeBetweenJwksRequests = config['min-time-between-jwks-requests'] || config.minTimeBetweenJwksRequests || 10
/**
* If this is a Bearer Only application.
* @type {Boolean}
*/
this.bearerOnly = resolveValue(config['bearer-only'] || config.bearerOnly || false)
/**
* Formatted public-key.
* @type {String}
*/
const plainKey = resolveValue(config['realm-public-key'] || config.realmPublicKey)
if (plainKey) {
this.publicKey = '-----BEGIN PUBLIC KEY-----\n'
for (let i = 0; i < plainKey.length; i = i + 64) {
this.publicKey += plainKey.substring(i, i + 64)
this.publicKey += '\n'
}
this.publicKey += '-----END PUBLIC KEY-----'
}
/**
* Verify token audience
* @type {Boolean}
*/
this.verifyTokenAudience = resolveValue(config['verify-token-audience'] || config.verifyTokenAudience || false)
}
module.exports = Config