UNPKG

anvil-connect-nodejs

Version:
363 lines (278 loc) 10.8 kB
# Anvil Connect client for Node.js [![NPM Version](https://img.shields.io/npm/v/anvil-connect-nodejs.svg?style=flat)](https://npm.im/anvil-connect-nodejs) [![Build Status](https://travis-ci.org/anvilresearch/connect-nodejs.svg?branch=master)](https://travis-ci.org/anvilresearch/connect-nodejs) [Anvil Connect][connect] is a modern authorization server built to authenticate your users and protect your APIs. It's based on [OAuth 2.0][oauth2] and [OpenID Connect][oidc]. This library is a low level OpenID Connect and Anvil Connect API client. Previous versions included Express-specific functions and middleware. These higher-level functions are being split out into a [separate library][connect-express]. [oauth2]: http://tools.ietf.org/html/rfc6749 [oidc]: http://openid.net/connect/ [connect]: https://github.com/anvilresearch/connect [connect-nodejs]: https://github.com/anvilresearch/connect-nodejs [connect-express]: https://github.com/anvilresearch/connect-express ### Install ```bash $ npm install anvil-connect-nodejs --save ``` ### Configure Before performing any other operations (such as verifying or refreshing OIDC tokens, or accessing the AnvilConnect-specific API (such as creating users), an OIDC client needs to be configured and registered with the server (OIDC Provider, OP for short). #### new AnvilConnect(config) ```javascript var AnvilConnectClient = require('anvil-connect-nodejs'); // If the client has been pre-registered, pass the credentials to constructor var client = new AnvilConnectClient({ issuer: 'https://connect.example.com', client_id: 'CLIENT_ID', client_secret: 'CLIENT_SECRET', redirect_uri: 'REDIRECT_URI', scope: 'realm' }) client.initProvider() .then(function () { // Ready to verify() tokens, refresh(), etc }) // If the client has not been registered, use OIDC dynamic registration var client = new AnvilConnectClient({ issuer: 'https://connect.example.com' }) client.initProvider() .then(function () { // Provider config loaded (.discover() and .getJWKs() called) // Ready to register() return client.register({ // ... see below for registration options }) }) .then(function () { // Client is now registered. Ready to verify() tokens, refresh(), etc }) .catch(function (err) { // as always, don't forget error handling }) ``` **options** * `issuer` – REQUIRED uri of your OpenID Connect provider * `client_id` – OPTIONAL client identifier issued by OIDC provider during registration * `client_secret` – OPTIONAL confidential value issued by OIDC provider during registration * `redirect_uri` – OPTIONAL uri users will be redirected back to after authenticating with the issuer * `scope` – OPTIONAL array of strings, or space delimited string value containing scopes to be included in authorization requests. Defaults to `openid profile` ### OpenID Connect #### client.discover() Returns a promise providing [OpenID Metadata][oidc-meta] retrieved from the `.well-known/openid-configuration` endpoint for the configured issuer. Sets the response data as `client.configuration`. [oidc-meta]: http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata **example** ```javascript client.discover() .then(function (openidMetadata) { // client.configuration === openidMetadata }) .catch(function (error) { // ... }) ``` #### client.getJWKs() Returns a promise providing the JWK set published by the configured issuer. Depends on a prior call to `client.discover()`. **example** ```javascript client.getJWKs() .then(function (jwks) { // client.jwks === jwks }) .catch(function (error) { // ... }) ``` #### client.register(registration) Dynamically registers a new client with the configured issuer and returns a promise for the new client registration. You can learn more about [dynamic registration for Anvil Connect][dynamic-registration] in the docs. Depends on a prior call to `client.discover()`. [dynamic-registration]: https://github.com/anvilresearch/connect-docs/blob/master/clients.md#dynamic-registration **example** ```javascript var options = { client_name: 'Antisocial Network', client_uri: 'https://app.example.com', logo_uri: 'https://app.example.com/assets/logo.png', response_types: ['code'], grant_types: ['authorization_code', 'refresh_token'], default_max_age: 86400, // one day in seconds redirect_uris: ['https://app.example.com/callback.html', 'https://app.example.com/other.html'], post_logout_redirect_uris: ['https://app.example.com'] } client.register(options) .then(function (data) { // After the register request resolves // client.client_id and .client_secret are initialized // and the rest of the returned data is set to client.registration }) ``` #### client.authorizationUri([endpoint|options]) Accepts a string specifying a non-default endpoint or an options object and returns an authorization URI. Depends on a prior call to `client.discover()` and `client_id` being configured. **options** * All options accepted by `client.authorizationParams()`. * `endpoint` – This value is used for the path in the returned URI. Defaults to `authorize`. **example** ```javascript client.authorizationUri() // 'https://connect.example.com/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=openid%20profile%20more' client.authorizationUri('signin') // 'https://connect.example.com/signin?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=openid%20profile%20more' client.authorizationUri({ endpoint: 'connect/google', response_type: 'code id_token token', redirect_uri: 'OTHER_REDIRECT_URI', scope: 'openid profile extra' }) // 'https://connect.example.com/connect/google?response_type=code%20id_token%20token&client_id=CLIENT_ID&redirect_uri=OTHER_REDIRECT_URI&scope=openid%20profile%20extra' ``` #### client.authorizationParams(options) Accepts an options object and returns an object containing authorization params including default values. Depends on `client_id` being configured. *Note:* This is a low-level function used by `authorizationUri()`, documented to list its parameters (which are passed through to `authorizationParams()`). It's unlikely that your code will be invoking it directly. **options** * `response_type` – defaults to `code` * `redirect_uri` – defaults to the `redirect_uri` configured for this client * `scope` – defaults to the scope configured for this client * `state` * `response_mode` * `nonce` * `display` * `prompt` * `max_age` * `ui_locales` * `id_token_hint` * `login_hint` * `acr_values` * `email` * `password` * `provider` #### client.token(options) Given an authorization code is provided as the `code` option, this method will exchange the auth code for a set of token credentials, then verify the signatures and decode the payloads. Depends on `client_id` and `client_secret` being configured, and prior calls to `client.discover()` and `client.getJWKs()`. **options** * `code` – value obtained from a successful authorization request with `code` in the `response_types` request param **example** ```javascript client.token({ code: 'AUTHORIZATION_CODE' }) ``` #### client.refresh(options) Given an refresh_token is provided as the `refresh_token` option, this method will exchange the refresh_token for a set of token credentials, then verify the signatures. Depends on `client_id` and `client_secret` being configured, and prior calls to `client.discover()` and `client.getJWKs()`. **options** * `refresh_token` – value obtained from a successful authorization request with `token` in the `response_types` request param **example** ```javascript client.refresh({ refresh_token: 'REFRESH_TOKEN' }) ``` #### client.userInfo(options) Get user info from the issuer. **options** * `token` – access token **example** ```javascript client.userInfo({ token: 'ACCESS_TOKEN' }) ``` #### client.verify(token, options) ### Anvil Connect Admin API Anvil Connect provides a backend admin-only API (outside of the OIDC specs), for managing clients, users and so on. Note: All of these operations require an access token passed in `options.token`. You can get this token either via an admin user (with an `authority` role assigned to them) login, OR via a Client Credentials Grant request (see `client.getClientAccessToken()` docs in `../index.js`). Example Usage: ``` client.getClientAccessToken() .then(function (accessToken) { var options = { token: accessToken } // Once you have the access token you can // call client.users.update(), create(), delete(), etc return client.users.create(userData, options) }) ``` #### Clients #### client.clients.list() #### client.clients.get(id) #### client.clients.create(data) #### client.clients.update(id, data) #### client.clients.delete(id) #### Roles #### client.roles.list() #### client.roles.get(id) #### client.roles.create(data) #### client.roles.update(id, data) #### client.roles.delete(id) #### Scopes #### client.scopes.list() #### client.scopes.get(id) #### client.scopes.create(data) #### client.scopes.update(id, data) #### client.scopes.delete(id) #### Users #### client.users.list() #### client.users.get(id) #### client.users.create(data) #### client.users.update(id, data) #### client.users.delete(id) ### Example ```javascript var AnvilConnectClient = require('anvil-connect-nodejs'); // Assumes a preregistered client (if not, call register() after initProvider()) var client = new AnvilConnectClient({ issuer: 'https://connect.example.com', client_id: 'CLIENT_ID', client_secret: 'CLIENT_SECRET', redirect_uri: 'REDIRECT_URI' }) // Initialize the provider config (endpoints and public keys) client.initProvider() .then(function () { // At this point, provider config and public keys are loaded and cached console.log(client.configuration) console.log(jwks) // Now, build an authorization url return client.authorizationUri() }) .then(function (url) { console.log(url) // handle an authorization response // this verifies the signatures on tokens received from the authorization server return client.token({ code: 'AUTHORIZATION_CODE' }) }) .then(function (tokens) { // a successful call to tokens() gives us id_token, access_token, // refresh_token, expiration, and the decoded payloads of the JWTs console.log(tokens) // get userinfo return client.userInfo({ token: tokens.access_token }) }) .then(function (userInfo) { console.log(userInfo) // verify an access token received by an API service return client.verify(JWT, { scope: 'research' }) }) ```