UNPKG

3.35 kBJavaScriptView Raw
1const _ = require('lodash')
2const got = require('got')
3const pupa = require('pupa')
4const { 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 */
15function 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 */
36function 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 */
57function 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 */
78function 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(errorMessages.apiKey, err.message, options.prefix.trim())
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 */
109function 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
118module.exports = {
119 parseUrl,
120 getApiKey,
121 getRequestOptions,
122 extendLifeCycle,
123 init
124}