openid-client
Advanced tools
Comparing version 0.0.1 to 0.1.0
@@ -6,20 +6,23 @@ 'use strict'; | ||
const jose = require('node-jose'); | ||
const uuid = require('node-uuid').v4; | ||
const gotErrorHandler = require('./got_error_handler'); | ||
const base64url = require('base64url'); | ||
const url = require('url'); | ||
const { merge, defaults, pick, forEach, get } = require('lodash'); | ||
const _ = require('lodash'); | ||
const TokenSet = require('./token_set'); | ||
const tokenHash = require('./token_hash'); | ||
const OpenIdConnectError = require('./open_id_connect_error'); | ||
const { | ||
USER_AGENT, | ||
CLIENT_METADATA, | ||
CLIENT_DEFAULTS, | ||
} = require('./consts'); | ||
const CALLBACK_PROPERTIES = require('./consts').CALLBACK_PROPERTIES; | ||
const CLIENT_METADATA = require('./consts').CLIENT_METADATA; | ||
const CLIENT_DEFAULTS = require('./consts').CLIENT_DEFAULTS; | ||
const debug = require('debug')('oidc:client'); | ||
const got = require('got'); | ||
const map = new WeakMap(); | ||
function bearer(token) { | ||
return `Bearer ${token}`; | ||
} | ||
function instance(ctx) { | ||
@@ -31,6 +34,16 @@ if (!map.has(ctx)) map.set(ctx, {}); | ||
class BaseClient { | ||
constructor(metadata) { | ||
forEach(defaults(pick(metadata, CLIENT_METADATA), CLIENT_DEFAULTS), (value, key) => { | ||
constructor(metadata, keystore) { | ||
_.forEach(_.defaults(_.pick(metadata, CLIENT_METADATA), CLIENT_DEFAULTS), (value, key) => { | ||
instance(this)[key] = value; | ||
}); | ||
if (keystore !== undefined) { | ||
assert.ok(jose.JWK.isKeyStore(keystore), 'keystore must be an instance of jose.JWK.KeyStore'); | ||
instance(this).keystore = keystore; | ||
} | ||
if (this.token_endpoint_auth_method.endsWith('_jwt')) { | ||
assert.ok(this.issuer.token_endpoint_auth_signing_alg_values_supported, | ||
'token_endpoint_auth_signing_alg_values_supported must be provided on the issuer'); | ||
} | ||
} | ||
@@ -41,3 +54,3 @@ | ||
const query = defaults(params, { | ||
const query = _.defaults(params, { | ||
client_id: this.client_id, | ||
@@ -52,3 +65,3 @@ scope: 'openid', | ||
return url.format(defaults({ | ||
return url.format(_.defaults({ | ||
search: null, | ||
@@ -59,7 +72,14 @@ query, | ||
authorizationCallback(redirectUri, params) { | ||
authorizationCallback(redirectUri, parameters, checks) { | ||
const params = _.pick(parameters, CALLBACK_PROPERTIES); | ||
const toCheck = checks || {}; | ||
if (params.error) { | ||
return Promise.reject(pick(params, 'error', 'error_description', 'state')); | ||
return Promise.reject(new OpenIdConnectError(params)); | ||
} | ||
if (toCheck.state !== parameters.state) { | ||
return Promise.reject(new Error('state mismatch')); | ||
} | ||
return this.grant({ | ||
@@ -69,6 +89,6 @@ grant_type: 'authorization_code', | ||
redirect_uri: redirectUri, | ||
}).then(tokenset => this.validateIdToken(tokenset)); | ||
}).then(tokenset => this.validateIdToken(tokenset, toCheck.nonce)); | ||
} | ||
validateIdToken(token) { | ||
validateIdToken(token, nonce) { | ||
let idToken = token; | ||
@@ -84,2 +104,4 @@ | ||
idToken = String(idToken); | ||
const now = Math.ceil(Date.now() / 1000); | ||
@@ -111,2 +133,6 @@ const parts = idToken.split('.'); | ||
if (payloadObject.nonce || nonce !== undefined) { | ||
assert.equal(payloadObject.nonce, nonce, 'nonce mismatch'); | ||
} | ||
assert.ok(typeof payloadObject.exp === 'number', 'exp is not a number'); | ||
@@ -133,7 +159,7 @@ assert.ok(now < payloadObject.exp, 'id_token expired'); | ||
if (payloadObject.c_hash && token.code) { | ||
assert.equal(payloadObject.at_hash, tokenHash(token.code, headerObject.alg), | ||
assert.equal(payloadObject.c_hash, tokenHash(token.code, headerObject.alg), | ||
'c_hash mismatch'); | ||
} | ||
return this.issuer.key(headerObject) | ||
return (headerObject.alg.startsWith('HS') ? this.joseSecret() : this.issuer.key(headerObject)) | ||
.then(key => jose.JWS.createVerify(key).verify(idToken)) | ||
@@ -143,8 +169,2 @@ .then(() => token); | ||
// implicitCallback(params, verify) { | ||
// if (params.error) { | ||
// return Promise.reject(pick(params, 'error', 'error_description', 'state')); | ||
// } | ||
// } | ||
refresh(refreshToken) { | ||
@@ -166,4 +186,8 @@ let token = refreshToken; | ||
userinfo(accessToken) { | ||
userinfo(accessToken, options) { | ||
let token = accessToken; | ||
const opts = _.merge({ | ||
verb: 'get', | ||
via: 'header', | ||
}, options); | ||
@@ -177,13 +201,34 @@ if (token instanceof TokenSet) { | ||
return got.get(this.issuer.userinfo_endpoint, { | ||
retries: 0, | ||
followRedirect: false, | ||
headers: { | ||
'User-Agent': USER_AGENT, | ||
Authorization: `Bearer ${token}`, | ||
}, | ||
}).then(response => JSON.parse(response.body), err => { | ||
debug('userinfo request failed (%s > %s)', | ||
err.name, err.message, get(err, 'response.body')); | ||
throw err; | ||
const verb = String(opts.verb).toLowerCase(); | ||
let httpOptions; | ||
switch (opts.via) { | ||
case 'query': | ||
assert.equal(verb, 'get', 'providers should only parse query strings for GET requests'); | ||
httpOptions = { query: { access_token: token } }; | ||
break; | ||
case 'body': | ||
assert.equal(verb, 'post', 'can only send body on POST'); | ||
httpOptions = { body: { access_token: token } }; | ||
break; | ||
default: | ||
httpOptions = { headers: { Authorization: bearer(token) } }; | ||
} | ||
return got[verb](this.issuer.userinfo_endpoint, this.issuer.httpOptions( | ||
httpOptions | ||
)).then(response => JSON.parse(response.body), gotErrorHandler); | ||
} | ||
joseSecret() { | ||
if (instance(this).jose_secret) { | ||
return Promise.resolve(instance(this).jose_secret); | ||
} | ||
return jose.JWK.asKey({ | ||
k: base64url(new Buffer(this.client_secret)), | ||
kty: 'oct', | ||
}).then(key => { | ||
instance(this).jose_secret = key; | ||
return key; | ||
}); | ||
@@ -193,22 +238,68 @@ } | ||
grant(body) { | ||
const auth = this.grantAuth(); | ||
debug('client %s %s grant request started', | ||
this.client_id, body.grant_type); | ||
return this.authenticatedPost(this.issuer.token_endpoint, { body }, | ||
response => new TokenSet(JSON.parse(response.body))); | ||
} | ||
return got.post(this.issuer.token_endpoint, merge({ | ||
body, | ||
retries: 0, | ||
followRedirect: false, | ||
headers: { | ||
'User-Agent': USER_AGENT, | ||
}, | ||
}, auth)).then(response => new TokenSet(JSON.parse(response.body)), err => { | ||
debug('client %s grant request failed (%s > %s)', | ||
this.client_id, err.name, err.message, get(err, 'response.body')); | ||
throw err; | ||
}); | ||
revoke(token) { | ||
assert.ok(this.issuer.revocation_endpoint || this.issuer.token_revocation_endpoint, | ||
'issuer must be configured with revocation endpoint'); | ||
const endpoint = this.issuer.revocation_endpoint || this.issuer.token_revocation_endpoint; | ||
return this.authenticatedPost(endpoint, { body: { token } }, | ||
response => JSON.parse(response.body)); | ||
} | ||
introspect(token) { | ||
assert.ok(this.issuer.introspection_endpoint || this.issuer.token_introspection_endpoint, | ||
'issuer must be configured with introspection endpoint'); | ||
const endpoint = this.issuer.introspection_endpoint || this.issuer.token_introspection_endpoint; | ||
return this.authenticatedPost(endpoint, { body: { token } }, | ||
response => JSON.parse(response.body)); | ||
} | ||
authenticatedPost(endpoint, httpOptions, success) { | ||
return Promise.resolve(this.grantAuth()) | ||
.then(auth => got.post(endpoint, this.issuer.httpOptions(_.merge(httpOptions, auth))) | ||
.then(success, gotErrorHandler)); | ||
} | ||
createSign() { | ||
let alg = this.token_endpoint_auth_signing_alg; | ||
switch (this.token_endpoint_auth_method) { | ||
case 'client_secret_jwt': | ||
return this.joseSecret().then(key => { | ||
if (!alg) { | ||
alg = _.find(this.issuer.token_endpoint_auth_signing_alg_values_supported, | ||
(signAlg) => key.algorithms('sign').indexOf(signAlg) !== -1); | ||
} | ||
return jose.JWS.createSign({ | ||
fields: { alg, typ: 'JWT' }, | ||
format: 'compact', | ||
}, { key, reference: false }); | ||
}); | ||
case 'private_key_jwt': { | ||
if (!alg) { | ||
const algz = _.uniq(_.flatten(_.map(this.keystore.all(), key => key.algorithms('sign')))); | ||
alg = _.find(this.issuer.token_endpoint_auth_signing_alg_values_supported, | ||
(signAlg) => algz.indexOf(signAlg) !== -1); | ||
} | ||
const key = this.keystore.get({ alg }); | ||
assert.ok(key, 'no valid key found'); | ||
return Promise.resolve(jose.JWS.createSign({ | ||
fields: { alg, typ: 'JWT' }, | ||
format: 'compact', | ||
}, { key, reference: true })); | ||
} | ||
/* istanbul ignore next */ | ||
default: | ||
throw new Error('createSign only works for _jwt token auth methods'); | ||
} | ||
} | ||
grantAuth() { | ||
switch (this.token_endpoint_auth_method) { | ||
case 'none' : | ||
throw new Error('client not supposed to use grant authz'); | ||
case 'client_secret_post': | ||
@@ -221,9 +312,22 @@ return { | ||
}; | ||
case 'private_key_jwt' : | ||
case 'client_secret_jwt' : { | ||
const now = Math.floor(Date.now() / 1000); | ||
return this.createSign().then(sign => sign.update(JSON.stringify({ | ||
iat: now, | ||
exp: now + 60, | ||
jti: uuid(), | ||
iss: this.client_id, | ||
sub: this.client_id, | ||
aud: this.issuer.token_endpoint, | ||
})).final().then(client_assertion => { // eslint-disable-line camelcase, arrow-body-style | ||
return { body: { | ||
client_assertion, | ||
client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer', | ||
} }; | ||
})); | ||
} | ||
default: { | ||
const value = new Buffer(`${this.client_id}:${this.client_secret}`).toString('base64'); | ||
return { | ||
headers: { | ||
Authorization: `Basic ${value}`, | ||
}, | ||
}; | ||
return { headers: { Authorization: `Basic ${value}` } }; | ||
} | ||
@@ -237,42 +341,35 @@ } | ||
static register(body) { | ||
debug('attempting client registration'); | ||
static register(body, keystore) { | ||
assert.ok(this.issuer.registration_endpoint, 'issuer does not support dynamic registration'); | ||
return got.post(this.issuer.registration_endpoint, { | ||
if (keystore !== undefined && !(body.jwks || body.jwks_uri)) { | ||
assert.ok(jose.JWK.isKeyStore(keystore), 'keystore must be an instance of jose.JWK.KeyStore'); | ||
assert.ok(keystore.all().every(key => { | ||
if (key.kty === 'RSA' || key.kty === 'EC') { | ||
try { key.toPEM(true); } catch (err) { return false; } | ||
return true; | ||
} | ||
return false; | ||
}), 'keystore must only contain private EC or RSA keys'); | ||
body.jwks = keystore.toJSON(); | ||
} | ||
return got.post(this.issuer.registration_endpoint, this.issuer.httpOptions({ | ||
body: JSON.stringify(body), | ||
retries: 0, | ||
followRedirect: false, | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'User-Agent': USER_AGENT, | ||
}, | ||
}).then(response => new this(JSON.parse(response.body)), err => { | ||
debug('registration failed (%s > %s)', | ||
err.name, err.message, get(err, 'response.body')); | ||
throw err; | ||
}); | ||
headers: { 'Content-Type': 'application/json' }, | ||
})).then(response => new this(JSON.parse(response.body), keystore), gotErrorHandler); | ||
} | ||
get keystore() { | ||
return instance(this).keystore; | ||
} | ||
get metadata() { | ||
return pick(this, CLIENT_METADATA); | ||
return _.omitBy(_.pick(this, CLIENT_METADATA), _.isUndefined); | ||
} | ||
static fromUri(uri, token) { | ||
debug('fetching client from %s', | ||
uri); | ||
return got.get(uri, { | ||
retries: 0, | ||
followRedirect: false, | ||
headers: { | ||
Authorization: `Bearer ${token}`, | ||
'User-Agent': USER_AGENT, | ||
}, | ||
}).then(response => new this(JSON.parse(response.body)), err => { | ||
debug('%s request failed (%s > %s)', | ||
uri, err.name, err.message, get(err, 'response.body')); | ||
throw err; | ||
}); | ||
return got.get(uri, this.issuer.httpOptions({ | ||
headers: { Authorization: bearer(token) }, | ||
})).then(response => new this(JSON.parse(response.body)), gotErrorHandler); | ||
} | ||
@@ -279,0 +376,0 @@ } |
@@ -10,5 +10,9 @@ const pkg = require('../package.json'); | ||
'authorization_endpoint', | ||
'check_session_iframe', | ||
'claims_parameter_supported', | ||
'claims_supported', | ||
'end_session_endpoint', | ||
'grant_types_supported', | ||
'id_token_encryption_alg_values_supported', | ||
'id_token_encryption_enc_values_supported', | ||
'id_token_signing_alg_values_supported', | ||
@@ -18,5 +22,8 @@ 'issuer', | ||
'registration_endpoint', | ||
'request_object_encryption_alg_values_supported', | ||
'request_object_encryption_enc_values_supported', | ||
'request_object_signing_alg_values_supported', | ||
'request_parameter_supported', | ||
'request_uri_parameter_supported', | ||
'require_request_uri_registration', | ||
'response_modes_supported', | ||
@@ -30,13 +37,9 @@ 'response_types_supported', | ||
'token_introspection_endpoint', | ||
'introspection_endpoint', | ||
'token_revocation_endpoint', | ||
'revocation_endpoint', | ||
'userinfo_encryption_alg_values_supported', | ||
'userinfo_encryption_enc_values_supported', | ||
'userinfo_endpoint', | ||
'userinfo_signing_alg_values_supported', | ||
'id_token_encryption_alg_values_supported', | ||
'id_token_encryption_enc_values_supported', | ||
'userinfo_encryption_alg_values_supported', | ||
'userinfo_encryption_enc_values_supported', | ||
'request_object_encryption_alg_values_supported', | ||
'request_object_encryption_enc_values_supported', | ||
'check_session_iframe', | ||
'end_session_endpoint', | ||
]; | ||
@@ -84,24 +87,36 @@ | ||
const ISSUER_DEFAULTS = { | ||
response_modes_supported: ['query', 'fragment'], | ||
claims_parameter_supported: false, | ||
grant_types_supported: ['authorization_code', 'implicit'], | ||
token_endpoint_auth_methods_supported: ['client_secret_basic'], | ||
claims_parameter_supported: false, | ||
request_parameter_supported: false, | ||
request_uri_parameter_supported: true, | ||
require_request_uri_registration: false, | ||
response_modes_supported: ['query', 'fragment'], | ||
token_endpoint_auth_methods_supported: ['client_secret_basic'], | ||
}; | ||
const CLIENT_DEFAULTS = { | ||
response_types: ['code'], | ||
application_type: ['web'], | ||
grant_types: ['authorization_code'], | ||
application_type: ['web'], | ||
id_token_signed_response_alg: 'RS256', | ||
response_types: ['code'], | ||
token_endpoint_auth_method: 'client_secret_basic', | ||
}; | ||
module.exports.WELL_KNOWN = WELL_KNOWN; | ||
const CALLBACK_PROPERTIES = [ | ||
'access_token', | ||
'code', | ||
'error', | ||
'error_description', | ||
'expires_in', | ||
'id_token', | ||
'state', | ||
'token_type', | ||
]; | ||
module.exports.CALLBACK_PROPERTIES = CALLBACK_PROPERTIES; | ||
module.exports.CLIENT_DEFAULTS = CLIENT_DEFAULTS; | ||
module.exports.CLIENT_METADATA = CLIENT_METADATA; | ||
module.exports.ISSUER_DEFAULTS = ISSUER_DEFAULTS; | ||
module.exports.ISSUER_METADATA = ISSUER_METADATA; | ||
module.exports.CLIENT_DEFAULTS = CLIENT_DEFAULTS; | ||
module.exports.CLIENT_METADATA = CLIENT_METADATA; | ||
module.exports.USER_AGENT = USER_AGENT; | ||
module.exports.WELL_KNOWN = WELL_KNOWN; |
@@ -5,4 +5,2 @@ 'use strict'; | ||
module.exports = { | ||
Issuer, | ||
}; | ||
module.exports = { Issuer }; |
@@ -6,18 +6,26 @@ 'use strict'; | ||
const util = require('util'); | ||
const { get, defaults, pick, forEach } = require('lodash'); | ||
const _ = require('lodash'); | ||
const { | ||
USER_AGENT, | ||
WELL_KNOWN, | ||
ISSUER_METADATA, | ||
ISSUER_DEFAULTS, | ||
} = require('./consts'); | ||
const gotErrorHandler = require('./got_error_handler'); | ||
const USER_AGENT = require('./consts').USER_AGENT; | ||
const WELL_KNOWN = require('./consts').WELL_KNOWN; | ||
const ISSUER_METADATA = require('./consts').ISSUER_METADATA; | ||
const ISSUER_DEFAULTS = require('./consts').ISSUER_DEFAULTS; | ||
const BaseClient = require('./base_client'); | ||
const debug = require('debug')('oidc:issuer'); | ||
const got = require('got'); | ||
const map = new WeakMap(); | ||
const DEFAULT_HTTP_OPTIONS = { | ||
followRedirect: false, | ||
headers: { 'User-Agent': USER_AGENT }, | ||
retries: 0, | ||
timeout: 1500, | ||
}; | ||
Object.freeze(DEFAULT_HTTP_OPTIONS); | ||
let defaultHttpOptions = _.clone(DEFAULT_HTTP_OPTIONS); | ||
function instance(ctx) { | ||
@@ -30,3 +38,3 @@ if (!map.has(ctx)) map.set(ctx, {}); | ||
constructor(metadata) { | ||
forEach(defaults(pick(metadata, ISSUER_METADATA), ISSUER_DEFAULTS), (value, key) => { | ||
_.forEach(_.defaults(_.pick(metadata, ISSUER_METADATA), ISSUER_DEFAULTS), (value, key) => { | ||
instance(this)[key] = value; | ||
@@ -54,21 +62,14 @@ }); | ||
keyStore() { | ||
debug('%s request started', | ||
this.jwks_uri); | ||
return got.get(this.jwks_uri) | ||
.then(response => JSON.parse(response.body), err => { | ||
debug('%s request failed (%s > %s)', | ||
this.issuer.jwks_uri, err.name, err.message, get(err, 'response.body')); | ||
throw err; | ||
}) | ||
.then(jwks => jose.JWK.asKeyStore(jwks)); | ||
keystore() { | ||
return got.get(this.jwks_uri, this.httpOptions()) | ||
.then(response => JSON.parse(response.body), gotErrorHandler) | ||
.then(jwks => jose.JWK.asKeyStore(jwks)); | ||
} | ||
key(def) { | ||
return this.keyStore().then(store => store.get(def)); | ||
return this.keystore().then(store => store.get(def)); | ||
} | ||
get metadata() { | ||
return pick(this, ISSUER_METADATA); | ||
return _.omitBy(_.pick(this, ISSUER_METADATA), _.isUndefined); | ||
} | ||
@@ -80,17 +81,22 @@ | ||
debug('discovering configuration from %s', | ||
wellKnownUri); | ||
return got.get(wellKnownUri, this.httpOptions()) | ||
.then(response => new this(JSON.parse(response.body)), gotErrorHandler); | ||
} | ||
return got.get(wellKnownUri, { | ||
retries: 0, | ||
followRedirect: false, | ||
headers: { | ||
'User-Agent': USER_AGENT, | ||
}, | ||
}).then(response => new this(JSON.parse(response.body)), err => { | ||
debug('%s discovery failed (%s > %s)', | ||
wellKnownUri, err.name, err.message, get(err, 'response.body')); | ||
throw err; | ||
}); | ||
httpOptions() { | ||
return this.constructor.httpOptions.apply(this.constructor, arguments); // eslint-disable-line prefer-rest-params, max-len | ||
} | ||
static httpOptions(values) { | ||
return _.merge({}, this.defaultHttpOptions, values); | ||
} | ||
static get defaultHttpOptions() { | ||
return defaultHttpOptions; | ||
} | ||
static set defaultHttpOptions(value) { | ||
defaultHttpOptions = _.merge({}, DEFAULT_HTTP_OPTIONS, value); | ||
} | ||
} | ||
@@ -97,0 +103,0 @@ |
@@ -11,5 +11,7 @@ 'use strict'; | ||
switch (size) { | ||
/* istanbul ignore next */ | ||
case '512': | ||
hashingAlg = 'sha512'; | ||
break; | ||
/* istanbul ignore next */ | ||
case '384': | ||
@@ -16,0 +18,0 @@ hashingAlg = 'sha384'; |
{ | ||
"name": "openid-client", | ||
"version": "0.0.1", | ||
"version": "0.1.0", | ||
"description": "OpenID Connect Relying Party (RP, Client) implementation for Node.js", | ||
@@ -13,3 +13,3 @@ "main": "lib/index.js", | ||
"engines": { | ||
"node": ">=6" | ||
"node": ">=4" | ||
}, | ||
@@ -48,6 +48,5 @@ "homepage": "https://github.com/panva/node-openid-client", | ||
"base64url": "^1.0.6", | ||
"debug": "^2.2.0", | ||
"create-error-class": "^3.0.2", | ||
"got": "^6.3.0", | ||
"lodash": "^4.13.1", | ||
"lru-cache": "^4.0.1", | ||
"node-jose": "^0.8.0", | ||
@@ -54,0 +53,0 @@ "node-uuid": "^1.4.7" |
@@ -17,3 +17,3 @@ # openid-client | ||
### via Discovery | ||
### via Discovery (recommended) | ||
```js | ||
@@ -42,3 +42,3 @@ const Issuer = require('openid-client').Issuer; | ||
### manually | ||
### manually (recommended) | ||
You should provide the following metadata; `client_id, client_secret`. You can also provide | ||
@@ -52,8 +52,8 @@ `id_token_signed_response_alg` (defaults to `RS256`) and `token_endpoint_auth_method` (defaults to | ||
client_secret: 'TQV5U29k1gHibH5bx1layBo0OSAvAbRT3UYW3EWrSYBB5swxjVfWUa1BS8lqzxG/0v9wruMcrGadany3' | ||
}) // => Client | ||
}); // => Client | ||
``` | ||
### via Dynamic Registration | ||
Should your provider support Dynamic Registration and/or provided you with a registration client uri | ||
and registration access token you can also have the Client discovered. | ||
### via registration client uri | ||
Should your oidc provider have provided you with a registration client uri and registration access | ||
token you can also have the Client discovered. | ||
```js | ||
@@ -63,3 +63,3 @@ new googleIssuer.Client.fromUri(registration_client_uri, registration_access_token) // => Promise | ||
console.log('Discovered client %s', client); | ||
}) | ||
}); | ||
``` | ||
@@ -93,2 +93,18 @@ | ||
### Revoke a token | ||
```js | ||
client.revoke(token) // => Promise | ||
.then(function () { | ||
console.log('revoked token %s', token); | ||
}); | ||
``` | ||
### Introspect a token | ||
```js | ||
client.introspect(token) // => Promise | ||
.then(function (details) { | ||
console.log('token details %j', details); | ||
}); | ||
``` | ||
### Fetching userinfo | ||
@@ -102,2 +118,17 @@ ```js | ||
via POST | ||
```js | ||
client.userinfo(accessToken, { verb: 'post' }); // => Promise | ||
``` | ||
auth via query | ||
```js | ||
client.userinfo(accessToken, { via: 'query' }); // => Promise | ||
``` | ||
auth via body | ||
```js | ||
client.userinfo(accessToken, { verb: 'post', via: 'body' }); // => Promise | ||
``` | ||
### Custom token endpoint grants | ||
@@ -118,2 +149,10 @@ Use when the token endpoint also supports client_credentials or password grants; | ||
### Registering new client (via Dynamic Registration) | ||
```js | ||
issuer.Client.register(metadata, [keystore]) // => Promise | ||
.then(function (client) { | ||
console.log('Registered client %s, %j', client, client.metadata); | ||
}); | ||
``` | ||
[travis-image]: https://img.shields.io/travis/panva/node-openid-client/master.svg?style=flat-square&maxAge=7200 | ||
@@ -120,0 +159,0 @@ [travis-url]: https://travis-ci.org/panva/node-openid-client |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
27234
6
13
572
162
+ Addedcreate-error-class@^3.0.2
- Removeddebug@^2.2.0
- Removedlru-cache@^4.0.1
- Removeddebug@2.6.9(transitive)
- Removedlru-cache@4.1.5(transitive)
- Removedms@2.0.0(transitive)
- Removedpseudomap@1.0.2(transitive)
- Removedyallist@2.1.2(transitive)