Skip to content

Commit 998627a

Browse files
committed
add api key logic
1 parent 7e05442 commit 998627a

File tree

2 files changed

+126
-0
lines changed

2 files changed

+126
-0
lines changed

src/apiKey.js

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
const _ = require('lodash')
2+
const got = require('got')
3+
const pupa = require('pupa')
4+
const { raiseUnauthorized, errorMessages } = require('./utils')
5+
6+
/**
7+
* @function
8+
* @public
9+
*
10+
* Get api key endpoint its url with replaced placeholders.
11+
*
12+
* @param {Object} pluginOptions The plugin related options
13+
* @returns {string|false} The rendered url if available
14+
*/
15+
function parseUrl (pluginOptions) {
16+
const { apiKey, clientId, realmUrl } = pluginOptions
17+
18+
return !!apiKey && pupa(apiKey.url, {
19+
realm: realmUrl.split('/').slice(-1),
20+
clientId
21+
})
22+
}
23+
24+
/**
25+
* @function
26+
* @public
27+
*
28+
* Extract the api key out of the original
29+
* incoming request if possible. Otherwise
30+
* return `false`.
31+
*
32+
* @param {Hapi.request} request The incoming request object
33+
* @param {Object} options The api key related options
34+
* @returns {string|false} The extracted api key if premises matched
35+
*/
36+
function getApiKey (request, options) {
37+
const key = request[options.in][options.name]
38+
const hasApiKey = !!key && key.startsWith(options.prefix)
39+
40+
return hasApiKey && key
41+
}
42+
43+
/**
44+
* @function
45+
* @public
46+
*
47+
* Copy the authorization data of the original incoming request
48+
* to the request of the api key service. Extend headers or query
49+
* related to the settings. If there is no related key set or the
50+
* set key is not prefixed with the defined prefix, get `false`.
51+
* Otherwise the options for the proxied request.
52+
*
53+
* @param {Hapi.request} request The incoming request object
54+
* @param {Object} options The api key related options
55+
* @returns {Object|false} The request options if premises matched
56+
*/
57+
function getRequestOptions (request, options) {
58+
const key = getApiKey(request, options)
59+
const path = `${options.in}.${options.name}`
60+
const requestOptions = Object.assign({ [options.in]: {} }, options.request)
61+
62+
return key && _.set(requestOptions, path, key)
63+
}
64+
65+
/**
66+
* @function
67+
* @public
68+
*
69+
* Extend the hapi request life cycle with an
70+
* additional api key interceptor.
71+
*
72+
* @param {Hapi.server} server The related hapi server object
73+
* @param {Object} options The api key related options
74+
* @param {string} url The url to be requested
75+
*
76+
* @throws {Boom.unauthorized} If requesting the access token failed
77+
*/
78+
function extendLifeCycle (server, options, url) {
79+
server.ext('onRequest', async (request, h) => {
80+
const requestOptions = getRequestOptions(request, options)
81+
82+
if (requestOptions) {
83+
try {
84+
const res = await got(url, requestOptions)
85+
const body = JSON.parse(res.body)
86+
const token = _.get(body, options.tokenPath)
87+
88+
request.headers.authorization = `Bearer ${token}`
89+
} catch (err) {
90+
throw raiseUnauthorized(null, errorMessages.apiKey, err.message)
91+
}
92+
}
93+
94+
return h.continue
95+
})
96+
}
97+
98+
/**
99+
* @function
100+
* @public
101+
*
102+
* Initialize the api key strategy if enabled by
103+
* user: parse the url based on the settings and
104+
* extend request life cycle.
105+
*
106+
* @param {Hapi.server} server The related hapi server object
107+
* @param {Object} pluginOptions The plugin related options
108+
*/
109+
function init (server, pluginOptions) {
110+
const options = pluginOptions.apiKey
111+
const url = parseUrl(pluginOptions)
112+
113+
if (options) {
114+
extendLifeCycle(server, options, url)
115+
}
116+
}
117+
118+
module.exports = {
119+
parseUrl,
120+
getApiKey,
121+
getRequestOptions,
122+
extendLifeCycle,
123+
init
124+
}

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const got = require('got')
22
const { GrantManager } = require('keycloak-auth-utils')
33
const KeycloakToken = require('keycloak-auth-utils/lib/token')
4+
const apiKey = require('./apiKey')
45
const cache = require('./cache')
56
const token = require('./token')
67
const { raiseUnauthorized, errorMessages, fakeToolkit, verify } = require('./utils')
@@ -185,6 +186,7 @@ function register (server, opts) {
185186
manager = new GrantManager(options)
186187
store = cache.create(server, options.cache)
187188

189+
apiKey.init(server, opts)
188190
server.auth.scheme('keycloak-jwt', strategy)
189191
server.decorate('server', 'kjwt', { validate })
190192
}

0 commit comments

Comments
 (0)