hex-auth-api v0.1.6
hex-auth-api
Provides access to common authn implementations between services.
Currently implements enough to do the OpenID "hybrid"/"three-legged" approach, wherein the user gets redirected to log in in exchange for an access token, and the server signs the token in exchange for access to the OpenID profile API, having proved in those steps that it has a user's permission and is the service it claims to be acting on their behalf.
Client Usage
import { getThreeLeggedBearerToken } from 'hex-auth-api/client'
// ... later
this.bearer = await getThreeLeggedBearerToken({
'oidcServer': 'https://someuri/oidc',
'clientId': 'the service ID I have registered with OIDC'
});
// that might have resulted in a redirect to log in. if this is being executed we're good to go:
try {
const result = await this.bearer.call('/my/api/endpoint', { 'method': 'PUT', 'body': { 'some': 'data' } });
// result is a JSON document if your server is behaving
}
catch (ex) {
// panic
}
The interface to that call
function is that same in the native fetch
API, so you can pass anything you would there.
Additionally, it:
- Adds the negotiated token to request in the Authorization header
- For non-GET requests, implements JSON transport unless you specified otherwise in the second parameter options
- Does some special error handling meant to fit well with the
<osp-error-page>
web component
Server Usage
First you need to do key exchange with the OIDC server. That's out of scope here but there is information about it in the hex-auth-oidc
repository.
This is meant to be used with express-hex
, for which it provides middleware you can depend on in middleware.js
:
module.exports = {
'api': {
'description': 'My protected API functions',
'deps': [ 'hex-auth-api.three-legged' ]
}
};
This middleware requires a few parameters in your conf.js
:
module.exports = {
// your service's JSON web key
'keystore': require('./keys/the-jwk-set-generated-by-the-utility-in-hex-auth-oidc.json'),
// URI for the OIDC service you're authenticating with
'api.oidcServer': 'https://someurl/oidc'
};
By depending on the middleware and providing that configuration, it is able to bind endpoints that
- Publish the public portion of your key
- Accept an access token from an end-user authentication and negotiate with OIDC to exchange that for access to the profile API
- Stores a bearer token for the client that connects them with that access to actually make API calls
Then, in the middleware/api.js
file referred to above, the only thing left to do is to use the bearerAuth
middleware to protect your endpoints
const { bearerAuth } = require('hex-auth-api/server');
module.exports = async ({ app, conf, log }) => {
// instantiate a token guard
const auth = await bearerAuth(conf, log);
app.put(
'/my/api/endpoint',
// require a valid token
auth,
(req, res, next) => {
// req.params.access has info included the token you can use to access the profile API
// or, use the included helper to call it:
req.locals.bearerCall('/some/endpoint', { 'method': 'PUT', 'body': { 'some': 'data' } }, res);
}
);
};
The helper method works the same as the client call
method above, except:
- The options are for the
request
library instead offetch
. It's all pretty similar - If you include the
res
result in the call the response will be piped directly to the client. So they get a JSON document describing the result, error or not. - Omit
res
and the function resolves with the resulting parsed document on success or rejects with a description of the error