Add node modules and compiled JavaScript from main (#54)
Co-authored-by: Oliver King <oking3@uncc.edu>
This commit is contained in:
committed by
GitHub
parent
4a983766a0
commit
52d71d28bd
21
node_modules/openid-client/LICENSE.md
generated
vendored
Normal file
21
node_modules/openid-client/LICENSE.md
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Filip Skokan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
317
node_modules/openid-client/README.md
generated
vendored
Normal file
317
node_modules/openid-client/README.md
generated
vendored
Normal file
@ -0,0 +1,317 @@
|
||||
# openid-client
|
||||
|
||||
openid-client is a server side [OpenID][openid-connect] Relying Party (RP, Client) implementation for
|
||||
Node.js runtime, supports [passport][passport-url].
|
||||
|
||||
## Implemented specs & features
|
||||
|
||||
The following client/RP features from OpenID Connect/OAuth2.0 specifications are implemented by
|
||||
openid-client.
|
||||
|
||||
- [OpenID Connect Core 1.0][feature-core]
|
||||
- Authorization Callback
|
||||
- Authorization Code Flow
|
||||
- Implicit Flow
|
||||
- Hybrid Flow
|
||||
- UserInfo Request
|
||||
- Fetching Distributed Claims
|
||||
- Unpacking Aggregated Claims
|
||||
- Offline Access / Refresh Token Grant
|
||||
- Client Credentials Grant
|
||||
- Client Authentication
|
||||
- none
|
||||
- client_secret_basic
|
||||
- client_secret_post
|
||||
- client_secret_jwt
|
||||
- private_key_jwt
|
||||
- Consuming Self-Issued OpenID Provider ID Token response
|
||||
- [RFC8414 - OAuth 2.0 Authorization Server Metadata][feature-oauth-discovery] and [OpenID Connect Discovery 1.0][feature-discovery]
|
||||
- Discovery of OpenID Provider (Issuer) Metadata
|
||||
- Discovery of OpenID Provider (Issuer) Metadata via user provided inputs (via [webfinger][documentation-webfinger])
|
||||
- [OpenID Connect Dynamic Client Registration 1.0][feature-registration]
|
||||
- Dynamic Client Registration request
|
||||
- Client initialization via registration client uri
|
||||
- [RFC7009 - OAuth 2.0 Token revocation][feature-revocation]
|
||||
- Client Authenticated request to token revocation
|
||||
- [RFC7662 - OAuth 2.0 Token introspection][feature-introspection]
|
||||
- Client Authenticated request to token introspection
|
||||
- [RFC8628 - OAuth 2.0 Device Authorization Grant (Device Flow)][feature-device-flow]
|
||||
- [RFC8705 - OAuth 2.0 Mutual TLS Client Authentication and Certificate-Bound Access Tokens][feature-mtls]
|
||||
- Mutual TLS Client Certificate-Bound Access Tokens
|
||||
- Metadata for Mutual TLS Endpoint Aliases
|
||||
- Client Authentication
|
||||
- tls_client_auth
|
||||
- self_signed_tls_client_auth
|
||||
- [RFC9101 - OAuth 2.0 JWT-Secured Authorization Request (JAR)][feature-jar]
|
||||
- [RFC9126 - OAuth 2.0 Pushed Authorization Requests (PAR)][feature-par]
|
||||
- [OpenID Connect Session Management 1.0 - draft 28][feature-rp-logout]
|
||||
- RP-Initiated Logout
|
||||
- [Financial-grade API - Part 2: Read and Write API Security Profile (FAPI) - ID2][feature-fapi]
|
||||
- [JWT Secured Authorization Response Mode for OAuth 2.0 (JARM) - ID1][feature-jarm]
|
||||
- [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer (DPoP) - draft 03][feature-dpop]
|
||||
|
||||
Updates to draft specifications (DPoP, JARM, and FAPI) are released as MINOR library versions,
|
||||
if you utilize these specification implementations consider using the tilde `~` operator in your
|
||||
package.json since breaking changes may be introduced as part of these version updates.
|
||||
|
||||
## Certification
|
||||
[<img width="184" height="96" align="right" src="https://cdn.jsdelivr.net/gh/panva/node-openid-client@38cf016b0837e6d4116de3780b28d222d5780bc9/OpenID_Certified.png" alt="OpenID Certification">][openid-certified-link]
|
||||
Filip Skokan has [certified][openid-certified-link] that [openid-client][npm-url]
|
||||
conforms to the following profiles of the OpenID Connect™ protocol
|
||||
|
||||
- RP Basic, Implicit, Hybrid, Config, Dynamic, and Form Post
|
||||
- RP FAPI R/W MTLS and Private Key
|
||||
|
||||
|
||||
## Sponsor
|
||||
|
||||
[<img width="65" height="65" align="left" src="https://avatars.githubusercontent.com/u/2824157?s=75&v=4" alt="auth0-logo">][sponsor-auth0] If you want to quickly add OpenID Connect authentication to Node.js apps, feel free to check out Auth0's Node.js SDK and free plan at [auth0.com/developers][sponsor-auth0].<br><br>
|
||||
|
||||
## Support
|
||||
|
||||
If you or your business use openid-client, please consider becoming a [sponsor][support-sponsor] so I can continue maintaining it and adding new features carefree.
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
The library exposes what are essentially steps necessary to be done by a relying party consuming
|
||||
OpenID Connect Authorization Server responses or wrappers around requests to its endpoints. Aside
|
||||
from a generic OpenID Connect [passport][passport-url] strategy it does not expose neither express
|
||||
or koa middlewares. Those can however be built using the exposed API.
|
||||
|
||||
- [openid-client API Documentation][documentation]
|
||||
- [Issuer][documentation-issuer]
|
||||
- [Client][documentation-client]
|
||||
- [Customizing][documentation-customizing]
|
||||
- [TokenSet][documentation-tokenset]
|
||||
- [Strategy][documentation-strategy]
|
||||
- [generators][documentation-generators]
|
||||
- [errors][documentation-errors]
|
||||
|
||||
## Install
|
||||
|
||||
Node.js version **>=12.0.0** is recommended, but **^10.19.0** lts/dubnium is also supported.
|
||||
|
||||
```console
|
||||
npm install openid-client
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
Discover an Issuer configuration using its published .well-known endpoints
|
||||
```js
|
||||
const { Issuer } = require('openid-client');
|
||||
Issuer.discover('https://accounts.google.com') // => Promise
|
||||
.then(function (googleIssuer) {
|
||||
console.log('Discovered issuer %s %O', googleIssuer.issuer, googleIssuer.metadata);
|
||||
});
|
||||
```
|
||||
|
||||
### Authorization Code Flow
|
||||
|
||||
Authorization Code flow is for obtaining Access Tokens (and optionally Refresh Tokens) to use with
|
||||
third party APIs securely as well as Refresh Tokens. In this quick start your application also uses
|
||||
PKCE instead of `state` parameter for CSRF protection.
|
||||
|
||||
Create a Client instance for that issuer's authorization server intended for Authorization Code
|
||||
flow.
|
||||
|
||||
**See the [documentation][documentation] for full API details.**
|
||||
|
||||
```js
|
||||
const client = new googleIssuer.Client({
|
||||
client_id: 'zELcpfANLqY7Oqas',
|
||||
client_secret: 'TQV5U29k1gHibH5bx1layBo0OSAvAbRT3UYW3EWrSYBB5swxjVfWUa1BS8lqzxG/0v9wruMcrGadany3',
|
||||
redirect_uris: ['http://localhost:3000/cb'],
|
||||
response_types: ['code'],
|
||||
// id_token_signed_response_alg (default "RS256")
|
||||
// token_endpoint_auth_method (default "client_secret_basic")
|
||||
}); // => Client
|
||||
```
|
||||
|
||||
When you want to have your end-users authorize you need to send them to the issuer's
|
||||
`authorization_endpoint`. Consult the web framework of your choice on how to redirect but here's how
|
||||
to get the authorization endpoint's URL with parameters already encoded in the query to redirect
|
||||
to.
|
||||
|
||||
```js
|
||||
const { generators } = require('openid-client');
|
||||
const code_verifier = generators.codeVerifier();
|
||||
// store the code_verifier in your framework's session mechanism, if it is a cookie based solution
|
||||
// it should be httpOnly (not readable by javascript) and encrypted.
|
||||
|
||||
const code_challenge = generators.codeChallenge(code_verifier);
|
||||
|
||||
client.authorizationUrl({
|
||||
scope: 'openid email profile',
|
||||
resource: 'https://my.api.example.com/resource/32178',
|
||||
code_challenge,
|
||||
code_challenge_method: 'S256',
|
||||
});
|
||||
```
|
||||
|
||||
When end-users are redirected back to your `redirect_uri` your application consumes the callback and
|
||||
passes in the `code_verifier` to include it in the authorization code grant token exchange.
|
||||
```js
|
||||
const params = client.callbackParams(req);
|
||||
client.callback('https://client.example.com/callback', params, { code_verifier }) // => Promise
|
||||
.then(function (tokenSet) {
|
||||
console.log('received and validated tokens %j', tokenSet);
|
||||
console.log('validated ID Token claims %j', tokenSet.claims());
|
||||
});
|
||||
```
|
||||
|
||||
You can then call the `userinfo_endpoint`.
|
||||
```js
|
||||
client.userinfo(access_token) // => Promise
|
||||
.then(function (userinfo) {
|
||||
console.log('userinfo %j', userinfo);
|
||||
});
|
||||
```
|
||||
|
||||
And later refresh the tokenSet if it had a `refresh_token`.
|
||||
```js
|
||||
client.refresh(refresh_token) // => Promise
|
||||
.then(function (tokenSet) {
|
||||
console.log('refreshed and validated tokens %j', tokenSet);
|
||||
console.log('refreshed ID Token claims %j', tokenSet.claims());
|
||||
});
|
||||
```
|
||||
|
||||
### Implicit ID Token Flow
|
||||
|
||||
Implicit `response_type=id_token` flow is perfect for simply authenticating your end-users, assuming
|
||||
the only job you want done is authenticating the user and then relying on your own session mechanism
|
||||
with no need for accessing any third party APIs with an Access Token from the Authorization Server.
|
||||
|
||||
Create a Client instance for that issuer's authorization server intended for ID Token implicit flow.
|
||||
|
||||
**See the [documentation][documentation] for full API details.**
|
||||
```js
|
||||
const client = new googleIssuer.Client({
|
||||
client_id: 'zELcpfANLqY7Oqas',
|
||||
redirect_uris: ['http://localhost:3000/cb'],
|
||||
response_types: ['id_token'],
|
||||
// id_token_signed_response_alg (default "RS256")
|
||||
}); // => Client
|
||||
```
|
||||
|
||||
When you want to have your end-users authorize you need to send them to the issuer's
|
||||
`authorization_endpoint`. Consult the web framework of your choice on how to redirect but here's how
|
||||
to get the authorization endpoint's URL with parameters already encoded in the query to redirect
|
||||
to.
|
||||
|
||||
```js
|
||||
const { generators } = require('openid-client');
|
||||
const nonce = generators.nonce();
|
||||
// store the nonce in your framework's session mechanism, if it is a cookie based solution
|
||||
// it should be httpOnly (not readable by javascript) and encrypted.
|
||||
|
||||
client.authorizationUrl({
|
||||
scope: 'openid email profile',
|
||||
response_mode: 'form_post',
|
||||
nonce,
|
||||
});
|
||||
```
|
||||
|
||||
When end-users hit back your `redirect_uri` with a POST (authorization request included `form_post`
|
||||
response mode) your application consumes the callback and passes the `nonce` in to include it in the
|
||||
ID Token verification steps.
|
||||
```js
|
||||
// assumes req.body is populated from your web framework's body parser
|
||||
const params = client.callbackParams(req);
|
||||
client.callback('https://client.example.com/callback', params, { nonce }) // => Promise
|
||||
.then(function (tokenSet) {
|
||||
console.log('received and validated tokens %j', tokenSet);
|
||||
console.log('validated ID Token claims %j', tokenSet.claims());
|
||||
});
|
||||
```
|
||||
|
||||
### Device Authorization Grant (Device Flow)
|
||||
|
||||
[RFC8628 - OAuth 2.0 Device Authorization Grant (Device Flow)](https://tools.ietf.org/html/rfc8628)
|
||||
is started by starting a Device Authorization Request.
|
||||
|
||||
```js
|
||||
const handle = await client.deviceAuthorization();
|
||||
console.log('User Code: ', handle.user_code);
|
||||
console.log('Verification URI: ', handle.verification_uri);
|
||||
console.log('Verification URI (complete): ', handle.verification_uri_complete);
|
||||
```
|
||||
|
||||
The handle represents a Device Authorization Response with the `verification_uri`, `user_code` and
|
||||
other defined response properties.
|
||||
|
||||
You will display the instructions to the end-user and have him directed at `verification_uri` or
|
||||
`verification_uri_complete`, afterwards you can start polling for the Device Access Token Response.
|
||||
```js
|
||||
const tokenSet = await handle.poll();
|
||||
console.log('received tokens %j', tokenSet);
|
||||
```
|
||||
|
||||
This will poll in the defined interval and only resolve with a TokenSet once one is received. This
|
||||
will handle the defined `authorization_pending` and `slow_down` "soft" errors and continue polling
|
||||
but upon any other error it will reject. With tokenSet received you can throw away the handle.
|
||||
|
||||
## Electron Support
|
||||
|
||||
Electron >=v6.0.0 runtime is supported to the extent of the crypto engine BoringSSL feature parity
|
||||
with standard Node.js OpenSSL.
|
||||
|
||||
## FAQ
|
||||
|
||||
#### Semver?
|
||||
|
||||
**Yes.** Everything that's either exported in the TypeScript definitions file or
|
||||
[documented][documentation] is subject to
|
||||
[Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html). The rest is to be considered
|
||||
private API and is subject to change between any versions.
|
||||
|
||||
#### How do I use it outside of Node.js
|
||||
|
||||
It is **only built for ^10.19.0 || >=12.0.0 Node.js** environment - including openid-client in
|
||||
browser-environment targeted projects is not supported and may result in unexpected results.
|
||||
|
||||
#### How to make the client send client_id and client_secret in the body?
|
||||
|
||||
See [Client Authentication Methods (docs)][documentation-methods].
|
||||
|
||||
#### Can I adjust the HTTP timeout?
|
||||
|
||||
See [Customizing (docs)](https://github.com/panva/node-openid-client/blob/master/docs/README.md#customizing).
|
||||
|
||||
#### How can I debug the requests and responses?
|
||||
|
||||
See [Customizing (docs)](https://github.com/panva/node-openid-client/blob/master/docs/README.md#customizing).
|
||||
|
||||
|
||||
[openid-connect]: https://openid.net/connect/
|
||||
[feature-core]: https://openid.net/specs/openid-connect-core-1_0.html
|
||||
[feature-discovery]: https://openid.net/specs/openid-connect-discovery-1_0.html
|
||||
[feature-oauth-discovery]: https://tools.ietf.org/html/rfc8414
|
||||
[feature-registration]: https://openid.net/specs/openid-connect-registration-1_0.html
|
||||
[feature-revocation]: https://tools.ietf.org/html/rfc7009
|
||||
[feature-introspection]: https://tools.ietf.org/html/rfc7662
|
||||
[feature-mtls]: https://tools.ietf.org/html/rfc8705
|
||||
[feature-device-flow]: https://tools.ietf.org/html/rfc8628
|
||||
[feature-rp-logout]: https://openid.net/specs/openid-connect-session-1_0.html#RPLogout
|
||||
[feature-jarm]: https://openid.net/specs/openid-financial-api-jarm-ID1.html
|
||||
[feature-fapi]: https://openid.net/specs/openid-financial-api-part-2-ID2.html
|
||||
[feature-dpop]: https://tools.ietf.org/html/draft-ietf-oauth-dpop-03
|
||||
[feature-par]: https://www.rfc-editor.org/rfc/rfc9126.html
|
||||
[feature-jar]: https://www.rfc-editor.org/rfc/rfc9101.html
|
||||
[openid-certified-link]: https://openid.net/certification/
|
||||
[passport-url]: http://passportjs.org
|
||||
[npm-url]: https://www.npmjs.com/package/openid-client
|
||||
[sponsor-auth0]: https://auth0.com/developers?utm_source=GHsponsor&utm_medium=GHsponsor&utm_campaign=openid-client&utm_content=auth
|
||||
[support-sponsor]: https://github.com/sponsors/panva
|
||||
[documentation]: https://github.com/panva/node-openid-client/blob/master/docs/README.md
|
||||
[documentation-issuer]: https://github.com/panva/node-openid-client/blob/master/docs/README.md#issuer
|
||||
[documentation-client]: https://github.com/panva/node-openid-client/blob/master/docs/README.md#client
|
||||
[documentation-customizing]: https://github.com/panva/node-openid-client/blob/master/docs/README.md#customizing
|
||||
[documentation-tokenset]: https://github.com/panva/node-openid-client/blob/master/docs/README.md#tokenset
|
||||
[documentation-strategy]: https://github.com/panva/node-openid-client/blob/master/docs/README.md#strategy
|
||||
[documentation-errors]: https://github.com/panva/node-openid-client/blob/master/docs/README.md#errors
|
||||
[documentation-generators]: https://github.com/panva/node-openid-client/blob/master/docs/README.md#generators
|
||||
[documentation-methods]: https://github.com/panva/node-openid-client/blob/master/docs/README.md#client-authentication-methods
|
||||
[documentation-webfinger]: https://github.com/panva/node-openid-client/blob/master/docs/README.md#issuerwebfingerinput
|
1733
node_modules/openid-client/lib/client.js
generated
vendored
Normal file
1733
node_modules/openid-client/lib/client.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
123
node_modules/openid-client/lib/device_flow_handle.js
generated
vendored
Normal file
123
node_modules/openid-client/lib/device_flow_handle.js
generated
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
/* eslint-disable camelcase */
|
||||
const { inspect } = require('util');
|
||||
|
||||
const { RPError, OPError } = require('./errors');
|
||||
const instance = require('./helpers/weak_cache');
|
||||
const now = require('./helpers/unix_timestamp');
|
||||
const { authenticatedPost } = require('./helpers/client');
|
||||
const processResponse = require('./helpers/process_response');
|
||||
const TokenSet = require('./token_set');
|
||||
|
||||
class DeviceFlowHandle {
|
||||
constructor({
|
||||
client, exchangeBody, clientAssertionPayload, response, maxAge, DPoP,
|
||||
}) {
|
||||
['verification_uri', 'user_code', 'device_code'].forEach((prop) => {
|
||||
if (typeof response[prop] !== 'string' || !response[prop]) {
|
||||
throw new RPError(`expected ${prop} string to be returned by Device Authorization Response, got %j`, response[prop]);
|
||||
}
|
||||
});
|
||||
|
||||
if (!Number.isSafeInteger(response.expires_in)) {
|
||||
throw new RPError('expected expires_in number to be returned by Device Authorization Response, got %j', response.expires_in);
|
||||
}
|
||||
|
||||
instance(this).expires_at = now() + response.expires_in;
|
||||
instance(this).client = client;
|
||||
instance(this).DPoP = DPoP;
|
||||
instance(this).maxAge = maxAge;
|
||||
instance(this).exchangeBody = exchangeBody;
|
||||
instance(this).clientAssertionPayload = clientAssertionPayload;
|
||||
instance(this).response = response;
|
||||
instance(this).interval = response.interval * 1000 || 5000;
|
||||
}
|
||||
|
||||
abort() {
|
||||
instance(this).aborted = true;
|
||||
}
|
||||
|
||||
async poll({ signal } = {}) {
|
||||
if ((signal && signal.aborted) || instance(this).aborted) {
|
||||
throw new RPError('polling aborted');
|
||||
}
|
||||
|
||||
if (this.expired()) {
|
||||
throw new RPError('the device code %j has expired and the device authorization session has concluded', this.device_code);
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, instance(this).interval));
|
||||
|
||||
const response = await authenticatedPost.call(
|
||||
instance(this).client,
|
||||
'token',
|
||||
{
|
||||
form: {
|
||||
...instance(this).exchangeBody,
|
||||
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
|
||||
device_code: this.device_code,
|
||||
},
|
||||
responseType: 'json',
|
||||
},
|
||||
{ clientAssertionPayload: instance(this).clientAssertionPayload, DPoP: instance(this).DPoP },
|
||||
);
|
||||
|
||||
let responseBody;
|
||||
try {
|
||||
responseBody = processResponse(response);
|
||||
} catch (err) {
|
||||
switch (err instanceof OPError && err.error) {
|
||||
case 'slow_down':
|
||||
instance(this).interval += 5000;
|
||||
case 'authorization_pending': // eslint-disable-line no-fallthrough
|
||||
return this.poll({ signal });
|
||||
default:
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
const tokenset = new TokenSet(responseBody);
|
||||
|
||||
if ('id_token' in tokenset) {
|
||||
await instance(this).client.decryptIdToken(tokenset);
|
||||
await instance(this).client.validateIdToken(tokenset, undefined, 'token', instance(this).maxAge);
|
||||
}
|
||||
|
||||
return tokenset;
|
||||
}
|
||||
|
||||
get device_code() {
|
||||
return instance(this).response.device_code;
|
||||
}
|
||||
|
||||
get user_code() {
|
||||
return instance(this).response.user_code;
|
||||
}
|
||||
|
||||
get verification_uri() {
|
||||
return instance(this).response.verification_uri;
|
||||
}
|
||||
|
||||
get verification_uri_complete() {
|
||||
return instance(this).response.verification_uri_complete;
|
||||
}
|
||||
|
||||
get expires_in() {
|
||||
return Math.max.apply(null, [instance(this).expires_at - now(), 0]);
|
||||
}
|
||||
|
||||
expired() {
|
||||
return this.expires_in === 0;
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
[inspect.custom]() {
|
||||
return `${this.constructor.name} ${inspect(instance(this).response, {
|
||||
depth: Infinity,
|
||||
colors: process.stdout.isTTY,
|
||||
compact: false,
|
||||
sorted: true,
|
||||
})}`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DeviceFlowHandle;
|
61
node_modules/openid-client/lib/errors.js
generated
vendored
Normal file
61
node_modules/openid-client/lib/errors.js
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
/* eslint-disable camelcase */
|
||||
const { format } = require('util');
|
||||
|
||||
const makeError = require('make-error');
|
||||
|
||||
function OPError({
|
||||
error_description,
|
||||
error,
|
||||
error_uri,
|
||||
session_state,
|
||||
state,
|
||||
scope,
|
||||
}, response) {
|
||||
OPError.super.call(this, !error_description ? error : `${error} (${error_description})`);
|
||||
|
||||
Object.assign(
|
||||
this,
|
||||
{ error },
|
||||
(error_description && { error_description }),
|
||||
(error_uri && { error_uri }),
|
||||
(state && { state }),
|
||||
(scope && { scope }),
|
||||
(session_state && { session_state }),
|
||||
);
|
||||
|
||||
if (response) {
|
||||
Object.defineProperty(this, 'response', {
|
||||
value: response,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
makeError(OPError);
|
||||
|
||||
function RPError(...args) {
|
||||
if (typeof args[0] === 'string') {
|
||||
RPError.super.call(this, format(...args));
|
||||
} else {
|
||||
const {
|
||||
message, printf, response, ...rest
|
||||
} = args[0];
|
||||
if (printf) {
|
||||
RPError.super.call(this, format(...printf));
|
||||
} else {
|
||||
RPError.super.call(this, message);
|
||||
}
|
||||
Object.assign(this, rest);
|
||||
if (response) {
|
||||
Object.defineProperty(this, 'response', {
|
||||
value: response,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
makeError(RPError);
|
||||
|
||||
module.exports = {
|
||||
OPError,
|
||||
RPError,
|
||||
};
|
22
node_modules/openid-client/lib/helpers/assert.js
generated
vendored
Normal file
22
node_modules/openid-client/lib/helpers/assert.js
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
function assertSigningAlgValuesSupport(endpoint, issuer, properties) {
|
||||
if (!issuer[`${endpoint}_endpoint`]) return;
|
||||
|
||||
const eam = `${endpoint}_endpoint_auth_method`;
|
||||
const easa = `${endpoint}_endpoint_auth_signing_alg`;
|
||||
const easavs = `${endpoint}_endpoint_auth_signing_alg_values_supported`;
|
||||
|
||||
if (properties[eam] && properties[eam].endsWith('_jwt') && !properties[easa] && !issuer[easavs]) {
|
||||
throw new TypeError(`${easavs} must be configured on the issuer if ${easa} is not defined on a client`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertIssuerConfiguration(issuer, endpoint) {
|
||||
if (!issuer[endpoint]) {
|
||||
throw new TypeError(`${endpoint} must be configured on the issuer`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
assertSigningAlgValuesSupport,
|
||||
assertIssuerConfiguration,
|
||||
};
|
12
node_modules/openid-client/lib/helpers/base64url.js
generated
vendored
Normal file
12
node_modules/openid-client/lib/helpers/base64url.js
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
let encode;
|
||||
if (Buffer.isEncoding('base64url')) {
|
||||
encode = (input, encoding = 'utf8') => Buffer.from(input, encoding).toString('base64url');
|
||||
} else {
|
||||
const fromBase64 = (base64) => base64.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
|
||||
encode = (input, encoding = 'utf8') => fromBase64(Buffer.from(input, encoding).toString('base64'));
|
||||
}
|
||||
|
||||
const decode = (input) => Buffer.from(input, 'base64');
|
||||
|
||||
module.exports.decode = decode;
|
||||
module.exports.encode = encode;
|
170
node_modules/openid-client/lib/helpers/client.js
generated
vendored
Normal file
170
node_modules/openid-client/lib/helpers/client.js
generated
vendored
Normal file
@ -0,0 +1,170 @@
|
||||
const jose = require('jose');
|
||||
|
||||
const { assertIssuerConfiguration } = require('./assert');
|
||||
const { random } = require('./generators');
|
||||
const now = require('./unix_timestamp');
|
||||
const request = require('./request');
|
||||
const instance = require('./weak_cache');
|
||||
const merge = require('./merge');
|
||||
|
||||
const formUrlEncode = (value) => encodeURIComponent(value).replace(/%20/g, '+');
|
||||
|
||||
async function clientAssertion(endpoint, payload) {
|
||||
let alg = this[`${endpoint}_endpoint_auth_signing_alg`];
|
||||
if (!alg) {
|
||||
assertIssuerConfiguration(this.issuer, `${endpoint}_endpoint_auth_signing_alg_values_supported`);
|
||||
}
|
||||
|
||||
if (this[`${endpoint}_endpoint_auth_method`] === 'client_secret_jwt') {
|
||||
const key = await this.joseSecret();
|
||||
|
||||
if (!alg) {
|
||||
const supported = this.issuer[`${endpoint}_endpoint_auth_signing_alg_values_supported`];
|
||||
alg = Array.isArray(supported) && supported.find((signAlg) => key.algorithms('sign').has(signAlg));
|
||||
}
|
||||
|
||||
return jose.JWS.sign(payload, key, { alg, typ: 'JWT' });
|
||||
}
|
||||
|
||||
const keystore = instance(this).get('keystore');
|
||||
|
||||
if (!keystore) {
|
||||
throw new TypeError('no client jwks provided for signing a client assertion with');
|
||||
}
|
||||
|
||||
if (!alg) {
|
||||
const algs = new Set();
|
||||
|
||||
keystore.all().forEach((key) => {
|
||||
key.algorithms('sign').forEach(Set.prototype.add.bind(algs));
|
||||
});
|
||||
|
||||
const supported = this.issuer[`${endpoint}_endpoint_auth_signing_alg_values_supported`];
|
||||
alg = Array.isArray(supported) && supported.find((signAlg) => algs.has(signAlg));
|
||||
}
|
||||
|
||||
const key = keystore.get({ alg, use: 'sig' });
|
||||
if (!key) {
|
||||
throw new TypeError(`no key found in client jwks to sign a client assertion with using alg ${alg}`);
|
||||
}
|
||||
return jose.JWS.sign(payload, key, { alg, typ: 'JWT', kid: key.kid.startsWith('DONOTUSE.') ? undefined : key.kid });
|
||||
}
|
||||
|
||||
async function authFor(endpoint, { clientAssertionPayload } = {}) {
|
||||
const authMethod = this[`${endpoint}_endpoint_auth_method`];
|
||||
switch (authMethod) {
|
||||
case 'self_signed_tls_client_auth':
|
||||
case 'tls_client_auth':
|
||||
case 'none':
|
||||
return { form: { client_id: this.client_id } };
|
||||
case 'client_secret_post':
|
||||
if (!this.client_secret) {
|
||||
throw new TypeError('client_secret_post client authentication method requires a client_secret');
|
||||
}
|
||||
return { form: { client_id: this.client_id, client_secret: this.client_secret } };
|
||||
case 'private_key_jwt':
|
||||
case 'client_secret_jwt': {
|
||||
const timestamp = now();
|
||||
|
||||
const mTLS = endpoint === 'token' && this.tls_client_certificate_bound_access_tokens;
|
||||
const audience = [...new Set([
|
||||
this.issuer.issuer,
|
||||
this.issuer.token_endpoint,
|
||||
this.issuer[`${endpoint}_endpoint`],
|
||||
mTLS && this.issuer.mtls_endpoint_aliases
|
||||
? this.issuer.mtls_endpoint_aliases.token_endpoint : undefined,
|
||||
].filter(Boolean))];
|
||||
|
||||
const assertion = await clientAssertion.call(this, endpoint, {
|
||||
iat: timestamp,
|
||||
exp: timestamp + 60,
|
||||
jti: random(),
|
||||
iss: this.client_id,
|
||||
sub: this.client_id,
|
||||
aud: audience,
|
||||
...clientAssertionPayload,
|
||||
});
|
||||
|
||||
return {
|
||||
form: {
|
||||
client_id: this.client_id,
|
||||
client_assertion: assertion,
|
||||
client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
|
||||
},
|
||||
};
|
||||
}
|
||||
default: { // client_secret_basic
|
||||
// This is correct behaviour, see https://tools.ietf.org/html/rfc6749#section-2.3.1 and the
|
||||
// related appendix. (also https://github.com/panva/node-openid-client/pull/91)
|
||||
// > The client identifier is encoded using the
|
||||
// > "application/x-www-form-urlencoded" encoding algorithm per
|
||||
// > Appendix B, and the encoded value is used as the username; the client
|
||||
// > password is encoded using the same algorithm and used as the
|
||||
// > password.
|
||||
if (!this.client_secret) {
|
||||
throw new TypeError('client_secret_basic client authentication method requires a client_secret');
|
||||
}
|
||||
const encoded = `${formUrlEncode(this.client_id)}:${formUrlEncode(this.client_secret)}`;
|
||||
const value = Buffer.from(encoded).toString('base64');
|
||||
return { headers: { Authorization: `Basic ${value}` } };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resolveResponseType() {
|
||||
const { length, 0: value } = this.response_types;
|
||||
|
||||
if (length === 1) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function resolveRedirectUri() {
|
||||
const { length, 0: value } = this.redirect_uris || [];
|
||||
|
||||
if (length === 1) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function authenticatedPost(endpoint, opts, {
|
||||
clientAssertionPayload, endpointAuthMethod = endpoint, DPoP,
|
||||
} = {}) {
|
||||
const auth = await authFor.call(this, endpointAuthMethod, { clientAssertionPayload });
|
||||
const requestOpts = merge(opts, auth);
|
||||
|
||||
const mTLS = this[`${endpointAuthMethod}_endpoint_auth_method`].includes('tls_client_auth')
|
||||
|| (endpoint === 'token' && this.tls_client_certificate_bound_access_tokens);
|
||||
|
||||
let targetUrl;
|
||||
if (mTLS && this.issuer.mtls_endpoint_aliases) {
|
||||
targetUrl = this.issuer.mtls_endpoint_aliases[`${endpoint}_endpoint`];
|
||||
}
|
||||
|
||||
targetUrl = targetUrl || this.issuer[`${endpoint}_endpoint`];
|
||||
|
||||
if ('form' in requestOpts) {
|
||||
for (const [key, value] of Object.entries(requestOpts.form)) { // eslint-disable-line no-restricted-syntax, max-len
|
||||
if (typeof value === 'undefined') {
|
||||
delete requestOpts.form[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return request.call(this, {
|
||||
...requestOpts,
|
||||
method: 'POST',
|
||||
url: targetUrl,
|
||||
}, { mTLS, DPoP });
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
resolveResponseType,
|
||||
resolveRedirectUri,
|
||||
authFor,
|
||||
authenticatedPost,
|
||||
};
|
62
node_modules/openid-client/lib/helpers/consts.js
generated
vendored
Normal file
62
node_modules/openid-client/lib/helpers/consts.js
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
const OIDC_DISCOVERY = '/.well-known/openid-configuration';
|
||||
const OAUTH2_DISCOVERY = '/.well-known/oauth-authorization-server';
|
||||
const WEBFINGER = '/.well-known/webfinger';
|
||||
const REL = 'http://openid.net/specs/connect/1.0/issuer';
|
||||
const AAD_MULTITENANT_DISCOVERY = [
|
||||
`https://login.microsoftonline.com/common${OIDC_DISCOVERY}`,
|
||||
`https://login.microsoftonline.com/common/v2.0${OIDC_DISCOVERY}`,
|
||||
`https://login.microsoftonline.com/organizations/v2.0${OIDC_DISCOVERY}`,
|
||||
`https://login.microsoftonline.com/consumers/v2.0${OIDC_DISCOVERY}`,
|
||||
];
|
||||
|
||||
const CLIENT_DEFAULTS = {
|
||||
grant_types: ['authorization_code'],
|
||||
id_token_signed_response_alg: 'RS256',
|
||||
authorization_signed_response_alg: 'RS256',
|
||||
response_types: ['code'],
|
||||
token_endpoint_auth_method: 'client_secret_basic',
|
||||
};
|
||||
|
||||
const ISSUER_DEFAULTS = {
|
||||
claim_types_supported: ['normal'],
|
||||
claims_parameter_supported: false,
|
||||
grant_types_supported: ['authorization_code', 'implicit'],
|
||||
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 CALLBACK_PROPERTIES = [
|
||||
'access_token', // 6749
|
||||
'code', // 6749
|
||||
'error', // 6749
|
||||
'error_description', // 6749
|
||||
'error_uri', // 6749
|
||||
'expires_in', // 6749
|
||||
'id_token', // Core 1.0
|
||||
'state', // 6749
|
||||
'token_type', // 6749
|
||||
'session_state', // Session Management
|
||||
'response', // JARM
|
||||
];
|
||||
|
||||
const JWT_CONTENT = /^application\/jwt/;
|
||||
|
||||
const HTTP_OPTIONS = Symbol('openid-client.custom.http-options');
|
||||
const CLOCK_TOLERANCE = Symbol('openid-client.custom.clock-tolerance');
|
||||
|
||||
module.exports = {
|
||||
AAD_MULTITENANT_DISCOVERY,
|
||||
CALLBACK_PROPERTIES,
|
||||
CLIENT_DEFAULTS,
|
||||
CLOCK_TOLERANCE,
|
||||
HTTP_OPTIONS,
|
||||
ISSUER_DEFAULTS,
|
||||
JWT_CONTENT,
|
||||
OAUTH2_DISCOVERY,
|
||||
OIDC_DISCOVERY,
|
||||
REL,
|
||||
WEBFINGER,
|
||||
};
|
1
node_modules/openid-client/lib/helpers/deep_clone.js
generated
vendored
Normal file
1
node_modules/openid-client/lib/helpers/deep_clone.js
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
module.exports = (obj) => JSON.parse(JSON.stringify(obj));
|
29
node_modules/openid-client/lib/helpers/defaults.js
generated
vendored
Normal file
29
node_modules/openid-client/lib/helpers/defaults.js
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
/* eslint-disable no-restricted-syntax, no-continue */
|
||||
|
||||
const isPlainObject = require('./is_plain_object');
|
||||
|
||||
function defaults(deep, target, ...sources) {
|
||||
for (const source of sources) {
|
||||
if (!isPlainObject(source)) {
|
||||
continue;
|
||||
}
|
||||
for (const [key, value] of Object.entries(source)) {
|
||||
/* istanbul ignore if */
|
||||
if (key === '__proto__' || key === 'constructor') {
|
||||
continue;
|
||||
}
|
||||
if (typeof target[key] === 'undefined' && typeof value !== 'undefined') {
|
||||
target[key] = value;
|
||||
}
|
||||
|
||||
if (deep && isPlainObject(target[key]) && isPlainObject(value)) {
|
||||
defaults(true, target[key], value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
module.exports = defaults.bind(undefined, false);
|
||||
module.exports.deep = defaults.bind(undefined, true);
|
13
node_modules/openid-client/lib/helpers/generators.js
generated
vendored
Normal file
13
node_modules/openid-client/lib/helpers/generators.js
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
const { createHash, randomBytes } = require('crypto');
|
||||
|
||||
const base64url = require('./base64url');
|
||||
|
||||
const random = (bytes = 32) => base64url.encode(randomBytes(bytes));
|
||||
|
||||
module.exports = {
|
||||
random,
|
||||
state: random,
|
||||
nonce: random,
|
||||
codeVerifier: random,
|
||||
codeChallenge: (codeVerifier) => base64url.encode(createHash('sha256').update(codeVerifier).digest()),
|
||||
};
|
12
node_modules/openid-client/lib/helpers/is_absolute_url.js
generated
vendored
Normal file
12
node_modules/openid-client/lib/helpers/is_absolute_url.js
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
const url = require('url');
|
||||
const { strict: assert } = require('assert');
|
||||
|
||||
module.exports = (target) => {
|
||||
try {
|
||||
const { protocol } = new url.URL(target);
|
||||
assert(protocol.match(/^(https?:)$/));
|
||||
return true;
|
||||
} catch (err) {
|
||||
throw new TypeError('only valid absolute URLs can be requested');
|
||||
}
|
||||
};
|
1
node_modules/openid-client/lib/helpers/is_plain_object.js
generated
vendored
Normal file
1
node_modules/openid-client/lib/helpers/is_plain_object.js
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
module.exports = (a) => !!a && a.constructor === Object;
|
26
node_modules/openid-client/lib/helpers/merge.js
generated
vendored
Normal file
26
node_modules/openid-client/lib/helpers/merge.js
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
/* eslint-disable no-restricted-syntax, no-param-reassign, no-continue */
|
||||
|
||||
const isPlainObject = require('./is_plain_object');
|
||||
|
||||
function merge(target, ...sources) {
|
||||
for (const source of sources) {
|
||||
if (!isPlainObject(source)) {
|
||||
continue;
|
||||
}
|
||||
for (const [key, value] of Object.entries(source)) {
|
||||
/* istanbul ignore if */
|
||||
if (key === '__proto__' || key === 'constructor') {
|
||||
continue;
|
||||
}
|
||||
if (isPlainObject(target[key]) && isPlainObject(value)) {
|
||||
target[key] = merge(target[key], value);
|
||||
} else if (typeof value !== 'undefined') {
|
||||
target[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
module.exports = merge;
|
9
node_modules/openid-client/lib/helpers/pick.js
generated
vendored
Normal file
9
node_modules/openid-client/lib/helpers/pick.js
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
module.exports = function pick(object, ...paths) {
|
||||
const obj = {};
|
||||
for (const path of paths) { // eslint-disable-line no-restricted-syntax
|
||||
if (object[path]) {
|
||||
obj[path] = object[path];
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
};
|
62
node_modules/openid-client/lib/helpers/process_response.js
generated
vendored
Normal file
62
node_modules/openid-client/lib/helpers/process_response.js
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
const { STATUS_CODES } = require('http');
|
||||
const { format } = require('util');
|
||||
|
||||
const { OPError } = require('../errors');
|
||||
|
||||
const REGEXP = /(\w+)=("[^"]*")/g;
|
||||
const throwAuthenticateErrors = (response) => {
|
||||
const params = {};
|
||||
try {
|
||||
while ((REGEXP.exec(response.headers['www-authenticate'])) !== null) {
|
||||
if (RegExp.$1 && RegExp.$2) {
|
||||
params[RegExp.$1] = RegExp.$2.slice(1, -1);
|
||||
}
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
if (params.error) {
|
||||
throw new OPError(params, response);
|
||||
}
|
||||
};
|
||||
|
||||
const isStandardBodyError = (response) => {
|
||||
let result = false;
|
||||
try {
|
||||
let jsonbody;
|
||||
if (typeof response.body !== 'object' || Buffer.isBuffer(response.body)) {
|
||||
jsonbody = JSON.parse(response.body);
|
||||
} else {
|
||||
jsonbody = response.body;
|
||||
}
|
||||
result = typeof jsonbody.error === 'string' && jsonbody.error.length;
|
||||
if (result) response.body = jsonbody;
|
||||
} catch (err) {}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
function processResponse(response, { statusCode = 200, body = true, bearer = false } = {}) {
|
||||
if (response.statusCode !== statusCode) {
|
||||
if (bearer) {
|
||||
throwAuthenticateErrors(response);
|
||||
}
|
||||
|
||||
if (isStandardBodyError(response)) {
|
||||
throw new OPError(response.body, response);
|
||||
}
|
||||
|
||||
throw new OPError({
|
||||
error: format('expected %i %s, got: %i %s', statusCode, STATUS_CODES[statusCode], response.statusCode, STATUS_CODES[response.statusCode]),
|
||||
}, response);
|
||||
}
|
||||
|
||||
if (body && !response.body) {
|
||||
throw new OPError({
|
||||
error: format('expected %i %s with body but no body was returned', statusCode, STATUS_CODES[statusCode]),
|
||||
}, response);
|
||||
}
|
||||
|
||||
return response.body;
|
||||
}
|
||||
|
||||
module.exports = processResponse;
|
56
node_modules/openid-client/lib/helpers/request.js
generated
vendored
Normal file
56
node_modules/openid-client/lib/helpers/request.js
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
const Got = require('got');
|
||||
|
||||
const pkg = require('../../package.json');
|
||||
|
||||
const { deep: defaultsDeep } = require('./defaults');
|
||||
const isAbsoluteUrl = require('./is_absolute_url');
|
||||
const { HTTP_OPTIONS } = require('./consts');
|
||||
|
||||
let DEFAULT_HTTP_OPTIONS;
|
||||
let got;
|
||||
|
||||
const setDefaults = (options) => {
|
||||
DEFAULT_HTTP_OPTIONS = defaultsDeep({}, options, DEFAULT_HTTP_OPTIONS);
|
||||
got = Got.extend(DEFAULT_HTTP_OPTIONS);
|
||||
};
|
||||
|
||||
setDefaults({
|
||||
followRedirect: false,
|
||||
headers: { 'User-Agent': `${pkg.name}/${pkg.version} (${pkg.homepage})` },
|
||||
retry: 0,
|
||||
timeout: 3500,
|
||||
throwHttpErrors: false,
|
||||
});
|
||||
|
||||
module.exports = async function request(options, { accessToken, mTLS = false, DPoP } = {}) {
|
||||
const { url } = options;
|
||||
isAbsoluteUrl(url);
|
||||
const optsFn = this[HTTP_OPTIONS];
|
||||
let opts = options;
|
||||
|
||||
if (DPoP && 'dpopProof' in this) {
|
||||
opts.headers = opts.headers || {};
|
||||
opts.headers.DPoP = this.dpopProof({
|
||||
htu: url,
|
||||
htm: options.method,
|
||||
}, DPoP, accessToken);
|
||||
}
|
||||
|
||||
if (optsFn) {
|
||||
opts = optsFn.call(this, defaultsDeep({}, opts, DEFAULT_HTTP_OPTIONS));
|
||||
}
|
||||
|
||||
if (
|
||||
mTLS
|
||||
&& (
|
||||
(!opts.key || !opts.cert)
|
||||
&& (!opts.https || !((opts.https.key && opts.https.certificate) || opts.https.pfx))
|
||||
)
|
||||
) {
|
||||
throw new TypeError('mutual-TLS certificate and key not set');
|
||||
}
|
||||
|
||||
return got(opts);
|
||||
};
|
||||
|
||||
module.exports.setDefaults = setDefaults;
|
1
node_modules/openid-client/lib/helpers/unix_timestamp.js
generated
vendored
Normal file
1
node_modules/openid-client/lib/helpers/unix_timestamp.js
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
module.exports = () => Math.floor(Date.now() / 1000);
|
8
node_modules/openid-client/lib/helpers/weak_cache.js
generated
vendored
Normal file
8
node_modules/openid-client/lib/helpers/weak_cache.js
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
const privateProps = new WeakMap();
|
||||
|
||||
module.exports = (ctx) => {
|
||||
if (!privateProps.has(ctx)) {
|
||||
privateProps.set(ctx, new Map([['metadata', new Map()]]));
|
||||
}
|
||||
return privateProps.get(ctx);
|
||||
};
|
71
node_modules/openid-client/lib/helpers/webfinger_normalize.js
generated
vendored
Normal file
71
node_modules/openid-client/lib/helpers/webfinger_normalize.js
generated
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
// Credit: https://github.com/rohe/pyoidc/blob/master/src/oic/utils/webfinger.py
|
||||
|
||||
// -- Normalization --
|
||||
// A string of any other type is interpreted as a URI either the form of scheme
|
||||
// "://" authority path-abempty [ "?" query ] [ "#" fragment ] or authority
|
||||
// path-abempty [ "?" query ] [ "#" fragment ] per RFC 3986 [RFC3986] and is
|
||||
// normalized according to the following rules:
|
||||
//
|
||||
// If the user input Identifier does not have an RFC 3986 [RFC3986] scheme
|
||||
// portion, the string is interpreted as [userinfo "@"] host [":" port]
|
||||
// path-abempty [ "?" query ] [ "#" fragment ] per RFC 3986 [RFC3986].
|
||||
// If the userinfo component is present and all of the path component, query
|
||||
// component, and port component are empty, the acct scheme is assumed. In this
|
||||
// case, the normalized URI is formed by prefixing acct: to the string as the
|
||||
// scheme. Per the 'acct' URI Scheme [I‑D.ietf‑appsawg‑acct‑uri], if there is an
|
||||
// at-sign character ('@') in the userinfo component, it needs to be
|
||||
// percent-encoded as described in RFC 3986 [RFC3986].
|
||||
// For all other inputs without a scheme portion, the https scheme is assumed,
|
||||
// and the normalized URI is formed by prefixing https:// to the string as the
|
||||
// scheme.
|
||||
// If the resulting URI contains a fragment portion, it MUST be stripped off
|
||||
// together with the fragment delimiter character "#".
|
||||
// The WebFinger [I‑D.ietf‑appsawg‑webfinger] Resource in this case is the
|
||||
// resulting URI, and the WebFinger Host is the authority component.
|
||||
//
|
||||
// Note: Since the definition of authority in RFC 3986 [RFC3986] is
|
||||
// [ userinfo "@" ] host [ ":" port ], it is legal to have a user input
|
||||
// identifier like userinfo@host:port, e.g., alice@example.com:8080.
|
||||
|
||||
const PORT = /^\d+$/;
|
||||
|
||||
function hasScheme(input) {
|
||||
if (input.includes('://')) return true;
|
||||
|
||||
const authority = input.replace(/(\/|\?)/g, '#').split('#')[0];
|
||||
if (authority.includes(':')) {
|
||||
const index = authority.indexOf(':');
|
||||
const hostOrPort = authority.slice(index + 1);
|
||||
if (!PORT.test(hostOrPort)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function acctSchemeAssumed(input) {
|
||||
if (!input.includes('@')) return false;
|
||||
const parts = input.split('@');
|
||||
const host = parts[parts.length - 1];
|
||||
return !(host.includes(':') || host.includes('/') || host.includes('?'));
|
||||
}
|
||||
|
||||
function normalize(input) {
|
||||
if (typeof input !== 'string') {
|
||||
throw new TypeError('input must be a string');
|
||||
}
|
||||
|
||||
let output;
|
||||
if (hasScheme(input)) {
|
||||
output = input;
|
||||
} else if (acctSchemeAssumed(input)) {
|
||||
output = `acct:${input}`;
|
||||
} else {
|
||||
output = `https://${input}`;
|
||||
}
|
||||
|
||||
return output.split('#')[0];
|
||||
}
|
||||
|
||||
module.exports = normalize;
|
25
node_modules/openid-client/lib/index.js
generated
vendored
Normal file
25
node_modules/openid-client/lib/index.js
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
const Issuer = require('./issuer');
|
||||
const { OPError, RPError } = require('./errors');
|
||||
const Registry = require('./issuer_registry');
|
||||
const Strategy = require('./passport_strategy');
|
||||
const TokenSet = require('./token_set');
|
||||
const { CLOCK_TOLERANCE, HTTP_OPTIONS } = require('./helpers/consts');
|
||||
const generators = require('./helpers/generators');
|
||||
const { setDefaults } = require('./helpers/request');
|
||||
|
||||
module.exports = {
|
||||
Issuer,
|
||||
Registry,
|
||||
Strategy,
|
||||
TokenSet,
|
||||
errors: {
|
||||
OPError,
|
||||
RPError,
|
||||
},
|
||||
custom: {
|
||||
setHttpOptionsDefaults: setDefaults,
|
||||
http_options: HTTP_OPTIONS,
|
||||
clock_tolerance: CLOCK_TOLERANCE,
|
||||
},
|
||||
generators,
|
||||
};
|
10
node_modules/openid-client/lib/index.mjs
generated
vendored
Normal file
10
node_modules/openid-client/lib/index.mjs
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
import mod from './index.js';
|
||||
|
||||
export default mod;
|
||||
export const Issuer = mod.Issuer;
|
||||
export const Registry = mod.Registry;
|
||||
export const Strategy = mod.Strategy;
|
||||
export const TokenSet = mod.TokenSet;
|
||||
export const errors = mod.errors;
|
||||
export const custom = mod.custom;
|
||||
export const generators = mod.generators;
|
282
node_modules/openid-client/lib/issuer.js
generated
vendored
Normal file
282
node_modules/openid-client/lib/issuer.js
generated
vendored
Normal file
@ -0,0 +1,282 @@
|
||||
/* eslint-disable max-classes-per-file */
|
||||
|
||||
const { inspect } = require('util');
|
||||
const url = require('url');
|
||||
|
||||
const AggregateError = require('aggregate-error');
|
||||
const jose = require('jose');
|
||||
const LRU = require('lru-cache');
|
||||
const objectHash = require('object-hash');
|
||||
|
||||
const { RPError } = require('./errors');
|
||||
const getClient = require('./client');
|
||||
const registry = require('./issuer_registry');
|
||||
const processResponse = require('./helpers/process_response');
|
||||
const webfingerNormalize = require('./helpers/webfinger_normalize');
|
||||
const instance = require('./helpers/weak_cache');
|
||||
const request = require('./helpers/request');
|
||||
const { assertIssuerConfiguration } = require('./helpers/assert');
|
||||
const {
|
||||
ISSUER_DEFAULTS, OIDC_DISCOVERY, OAUTH2_DISCOVERY, WEBFINGER, REL, AAD_MULTITENANT_DISCOVERY,
|
||||
} = require('./helpers/consts');
|
||||
|
||||
const AAD_MULTITENANT = Symbol('AAD_MULTITENANT');
|
||||
|
||||
class Issuer {
|
||||
/**
|
||||
* @name constructor
|
||||
* @api public
|
||||
*/
|
||||
constructor(meta = {}) {
|
||||
const aadIssValidation = meta[AAD_MULTITENANT];
|
||||
delete meta[AAD_MULTITENANT];
|
||||
|
||||
['introspection', 'revocation'].forEach((endpoint) => {
|
||||
// if intro/revocation endpoint auth specific meta is missing use the token ones if they
|
||||
// are defined
|
||||
if (
|
||||
meta[`${endpoint}_endpoint`]
|
||||
&& meta[`${endpoint}_endpoint_auth_methods_supported`] === undefined
|
||||
&& meta[`${endpoint}_endpoint_auth_signing_alg_values_supported`] === undefined
|
||||
) {
|
||||
if (meta.token_endpoint_auth_methods_supported) {
|
||||
meta[`${endpoint}_endpoint_auth_methods_supported`] = meta.token_endpoint_auth_methods_supported;
|
||||
}
|
||||
if (meta.token_endpoint_auth_signing_alg_values_supported) {
|
||||
meta[`${endpoint}_endpoint_auth_signing_alg_values_supported`] = meta.token_endpoint_auth_signing_alg_values_supported;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Object.entries(meta).forEach(([key, value]) => {
|
||||
instance(this).get('metadata').set(key, value);
|
||||
if (!this[key]) {
|
||||
Object.defineProperty(this, key, {
|
||||
get() { return instance(this).get('metadata').get(key); },
|
||||
enumerable: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
instance(this).set('cache', new LRU({ max: 100 }));
|
||||
|
||||
registry.set(this.issuer, this);
|
||||
|
||||
const Client = getClient(this, aadIssValidation);
|
||||
|
||||
Object.defineProperties(this, {
|
||||
Client: { value: Client },
|
||||
FAPIClient: { value: class FAPIClient extends Client {} },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @name keystore
|
||||
* @api public
|
||||
*/
|
||||
async keystore(reload = false) {
|
||||
assertIssuerConfiguration(this, 'jwks_uri');
|
||||
|
||||
const keystore = instance(this).get('keystore');
|
||||
const cache = instance(this).get('cache');
|
||||
|
||||
if (reload || !keystore) {
|
||||
cache.reset();
|
||||
const response = await request.call(this, {
|
||||
method: 'GET',
|
||||
responseType: 'json',
|
||||
url: this.jwks_uri,
|
||||
});
|
||||
const jwks = processResponse(response);
|
||||
|
||||
const joseKeyStore = jose.JWKS.asKeyStore(jwks, { ignoreErrors: true });
|
||||
cache.set('throttle', true, 60 * 1000);
|
||||
instance(this).set('keystore', joseKeyStore);
|
||||
return joseKeyStore;
|
||||
}
|
||||
|
||||
return keystore;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name queryKeyStore
|
||||
* @api private
|
||||
*/
|
||||
async queryKeyStore({
|
||||
kid, kty, alg, use, key_ops: ops,
|
||||
}, { allowMulti = false } = {}) {
|
||||
const cache = instance(this).get('cache');
|
||||
|
||||
const def = {
|
||||
kid, kty, alg, use, key_ops: ops,
|
||||
};
|
||||
|
||||
const defHash = objectHash(def, {
|
||||
algorithm: 'sha256',
|
||||
ignoreUnknown: true,
|
||||
unorderedArrays: true,
|
||||
unorderedSets: true,
|
||||
});
|
||||
|
||||
// refresh keystore on every unknown key but also only upto once every minute
|
||||
const freshJwksUri = cache.get(defHash) || cache.get('throttle');
|
||||
|
||||
const keystore = await this.keystore(!freshJwksUri);
|
||||
const keys = keystore.all(def);
|
||||
|
||||
if (keys.length === 0) {
|
||||
throw new RPError({
|
||||
printf: ["no valid key found in issuer's jwks_uri for key parameters %j", def],
|
||||
jwks: keystore,
|
||||
});
|
||||
}
|
||||
|
||||
if (!allowMulti && keys.length > 1 && !kid) {
|
||||
throw new RPError({
|
||||
printf: ["multiple matching keys found in issuer's jwks_uri for key parameters %j, kid must be provided in this case", def],
|
||||
jwks: keystore,
|
||||
});
|
||||
}
|
||||
|
||||
cache.set(defHash, true);
|
||||
|
||||
return new jose.JWKS.KeyStore(keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* @name metadata
|
||||
* @api public
|
||||
*/
|
||||
get metadata() {
|
||||
const copy = {};
|
||||
instance(this).get('metadata').forEach((value, key) => {
|
||||
copy[key] = value;
|
||||
});
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name webfinger
|
||||
* @api public
|
||||
*/
|
||||
static async webfinger(input) {
|
||||
const resource = webfingerNormalize(input);
|
||||
const { host } = url.parse(resource);
|
||||
const webfingerUrl = `https://${host}${WEBFINGER}`;
|
||||
|
||||
const response = await request.call(this, {
|
||||
method: 'GET',
|
||||
url: webfingerUrl,
|
||||
responseType: 'json',
|
||||
searchParams: { resource, rel: REL },
|
||||
followRedirect: true,
|
||||
});
|
||||
const body = processResponse(response);
|
||||
|
||||
const location = Array.isArray(body.links) && body.links.find((link) => typeof link === 'object' && link.rel === REL && link.href);
|
||||
|
||||
if (!location) {
|
||||
throw new RPError({
|
||||
message: 'no issuer found in webfinger response',
|
||||
body,
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof location.href !== 'string' || !location.href.startsWith('https://')) {
|
||||
throw new RPError({
|
||||
printf: ['invalid issuer location %s', location.href],
|
||||
body,
|
||||
});
|
||||
}
|
||||
|
||||
const expectedIssuer = location.href;
|
||||
if (registry.has(expectedIssuer)) {
|
||||
return registry.get(expectedIssuer);
|
||||
}
|
||||
|
||||
const issuer = await this.discover(expectedIssuer);
|
||||
|
||||
if (issuer.issuer !== expectedIssuer) {
|
||||
registry.delete(issuer.issuer);
|
||||
throw new RPError('discovered issuer mismatch, expected %s, got: %s', expectedIssuer, issuer.issuer);
|
||||
}
|
||||
return issuer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name discover
|
||||
* @api public
|
||||
*/
|
||||
static async discover(uri) {
|
||||
const parsed = url.parse(uri);
|
||||
|
||||
if (parsed.pathname.includes('/.well-known/')) {
|
||||
const response = await request.call(this, {
|
||||
method: 'GET',
|
||||
responseType: 'json',
|
||||
url: uri,
|
||||
});
|
||||
const body = processResponse(response);
|
||||
return new Issuer({
|
||||
...ISSUER_DEFAULTS,
|
||||
...body,
|
||||
[AAD_MULTITENANT]: !!AAD_MULTITENANT_DISCOVERY.find(
|
||||
(discoveryURL) => uri.startsWith(discoveryURL),
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
const pathnames = [];
|
||||
if (parsed.pathname.endsWith('/')) {
|
||||
pathnames.push(`${parsed.pathname}${OIDC_DISCOVERY.substring(1)}`);
|
||||
} else {
|
||||
pathnames.push(`${parsed.pathname}${OIDC_DISCOVERY}`);
|
||||
}
|
||||
if (parsed.pathname === '/') {
|
||||
pathnames.push(`${OAUTH2_DISCOVERY}`);
|
||||
} else {
|
||||
pathnames.push(`${OAUTH2_DISCOVERY}${parsed.pathname}`);
|
||||
}
|
||||
|
||||
const errors = [];
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const pathname of pathnames) {
|
||||
try {
|
||||
const wellKnownUri = url.format({ ...parsed, pathname });
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const response = await request.call(this, {
|
||||
method: 'GET',
|
||||
responseType: 'json',
|
||||
url: wellKnownUri,
|
||||
});
|
||||
const body = processResponse(response);
|
||||
return new Issuer({
|
||||
...ISSUER_DEFAULTS,
|
||||
...body,
|
||||
[AAD_MULTITENANT]: !!AAD_MULTITENANT_DISCOVERY.find(
|
||||
(discoveryURL) => wellKnownUri.startsWith(discoveryURL),
|
||||
),
|
||||
});
|
||||
} catch (err) {
|
||||
errors.push(err);
|
||||
}
|
||||
}
|
||||
|
||||
const err = new AggregateError(errors);
|
||||
err.message = `Issuer.discover() failed.${err.message.split('\n')
|
||||
.filter((line) => !line.startsWith(' at')).join('\n')}`;
|
||||
throw err;
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
[inspect.custom]() {
|
||||
return `${this.constructor.name} ${inspect(this.metadata, {
|
||||
depth: Infinity,
|
||||
colors: process.stdout.isTTY,
|
||||
compact: false,
|
||||
sorted: true,
|
||||
})}`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Issuer;
|
3
node_modules/openid-client/lib/issuer_registry.js
generated
vendored
Normal file
3
node_modules/openid-client/lib/issuer_registry.js
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
const REGISTRY = new Map();
|
||||
|
||||
module.exports = REGISTRY;
|
188
node_modules/openid-client/lib/passport_strategy.js
generated
vendored
Normal file
188
node_modules/openid-client/lib/passport_strategy.js
generated
vendored
Normal file
@ -0,0 +1,188 @@
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
|
||||
const url = require('url');
|
||||
const { format } = require('util');
|
||||
|
||||
const cloneDeep = require('./helpers/deep_clone');
|
||||
const { RPError, OPError } = require('./errors');
|
||||
const { BaseClient } = require('./client');
|
||||
const { random, codeChallenge } = require('./helpers/generators');
|
||||
const pick = require('./helpers/pick');
|
||||
const { resolveResponseType, resolveRedirectUri } = require('./helpers/client');
|
||||
|
||||
function verified(err, user, info = {}) {
|
||||
if (err) {
|
||||
this.error(err);
|
||||
} else if (!user) {
|
||||
this.fail(info);
|
||||
} else {
|
||||
this.success(user, info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @name constructor
|
||||
* @api public
|
||||
*/
|
||||
function OpenIDConnectStrategy({
|
||||
client,
|
||||
params = {},
|
||||
passReqToCallback = false,
|
||||
sessionKey,
|
||||
usePKCE = true,
|
||||
extras = {},
|
||||
} = {}, verify) {
|
||||
if (!(client instanceof BaseClient)) {
|
||||
throw new TypeError('client must be an instance of openid-client Client');
|
||||
}
|
||||
|
||||
if (typeof verify !== 'function') {
|
||||
throw new TypeError('verify callback must be a function');
|
||||
}
|
||||
|
||||
if (!client.issuer || !client.issuer.issuer) {
|
||||
throw new TypeError('client must have an issuer with an identifier');
|
||||
}
|
||||
|
||||
this._client = client;
|
||||
this._issuer = client.issuer;
|
||||
this._verify = verify;
|
||||
this._passReqToCallback = passReqToCallback;
|
||||
this._usePKCE = usePKCE;
|
||||
this._key = sessionKey || `oidc:${url.parse(this._issuer.issuer).hostname}`;
|
||||
this._params = cloneDeep(params);
|
||||
this._extras = cloneDeep(extras);
|
||||
|
||||
if (!this._params.response_type) this._params.response_type = resolveResponseType.call(client);
|
||||
if (!this._params.redirect_uri) this._params.redirect_uri = resolveRedirectUri.call(client);
|
||||
if (!this._params.scope) this._params.scope = 'openid';
|
||||
|
||||
if (this._usePKCE === true) {
|
||||
const supportedMethods = Array.isArray(this._issuer.code_challenge_methods_supported)
|
||||
? this._issuer.code_challenge_methods_supported : false;
|
||||
|
||||
if (supportedMethods && supportedMethods.includes('S256')) {
|
||||
this._usePKCE = 'S256';
|
||||
} else if (supportedMethods && supportedMethods.includes('plain')) {
|
||||
this._usePKCE = 'plain';
|
||||
} else if (supportedMethods) {
|
||||
throw new TypeError('neither code_challenge_method supported by the client is supported by the issuer');
|
||||
} else {
|
||||
this._usePKCE = 'S256';
|
||||
}
|
||||
} else if (typeof this._usePKCE === 'string' && !['plain', 'S256'].includes(this._usePKCE)) {
|
||||
throw new TypeError(`${this._usePKCE} is not valid/implemented PKCE code_challenge_method`);
|
||||
}
|
||||
|
||||
this.name = url.parse(client.issuer.issuer).hostname;
|
||||
}
|
||||
|
||||
OpenIDConnectStrategy.prototype.authenticate = function authenticate(req, options) {
|
||||
(async () => {
|
||||
const client = this._client;
|
||||
if (!req.session) {
|
||||
throw new TypeError('authentication requires session support');
|
||||
}
|
||||
const reqParams = client.callbackParams(req);
|
||||
const sessionKey = this._key;
|
||||
|
||||
/* start authentication request */
|
||||
if (Object.keys(reqParams).length === 0) {
|
||||
// provide options object with extra authentication parameters
|
||||
const params = {
|
||||
state: random(),
|
||||
...this._params,
|
||||
...options,
|
||||
};
|
||||
|
||||
if (!params.nonce && params.response_type.includes('id_token')) {
|
||||
params.nonce = random();
|
||||
}
|
||||
|
||||
req.session[sessionKey] = pick(params, 'nonce', 'state', 'max_age', 'response_type');
|
||||
|
||||
if (this._usePKCE && params.response_type.includes('code')) {
|
||||
const verifier = random();
|
||||
req.session[sessionKey].code_verifier = verifier;
|
||||
|
||||
switch (this._usePKCE) { // eslint-disable-line default-case
|
||||
case 'S256':
|
||||
params.code_challenge = codeChallenge(verifier);
|
||||
params.code_challenge_method = 'S256';
|
||||
break;
|
||||
case 'plain':
|
||||
params.code_challenge = verifier;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.redirect(client.authorizationUrl(params));
|
||||
return;
|
||||
}
|
||||
/* end authentication request */
|
||||
|
||||
/* start authentication response */
|
||||
|
||||
const session = req.session[sessionKey];
|
||||
if (Object.keys(session || {}).length === 0) {
|
||||
throw new Error(format('did not find expected authorization request details in session, req.session["%s"] is %j', sessionKey, session));
|
||||
}
|
||||
|
||||
const {
|
||||
state, nonce, max_age: maxAge, code_verifier: codeVerifier, response_type: responseType,
|
||||
} = session;
|
||||
|
||||
try {
|
||||
delete req.session[sessionKey];
|
||||
} catch (err) {}
|
||||
|
||||
const opts = {
|
||||
redirect_uri: this._params.redirect_uri,
|
||||
...options,
|
||||
};
|
||||
|
||||
const checks = {
|
||||
state,
|
||||
nonce,
|
||||
max_age: maxAge,
|
||||
code_verifier: codeVerifier,
|
||||
response_type: responseType,
|
||||
};
|
||||
|
||||
const tokenset = await client.callback(opts.redirect_uri, reqParams, checks, this._extras);
|
||||
|
||||
const passReq = this._passReqToCallback;
|
||||
const loadUserinfo = this._verify.length > (passReq ? 3 : 2) && client.issuer.userinfo_endpoint;
|
||||
|
||||
const args = [tokenset, verified.bind(this)];
|
||||
|
||||
if (loadUserinfo) {
|
||||
if (!tokenset.access_token) {
|
||||
throw new RPError({
|
||||
message: 'expected access_token to be returned when asking for userinfo in verify callback',
|
||||
tokenset,
|
||||
});
|
||||
}
|
||||
const userinfo = await client.userinfo(tokenset);
|
||||
args.splice(1, 0, userinfo);
|
||||
}
|
||||
|
||||
if (passReq) {
|
||||
args.unshift(req);
|
||||
}
|
||||
|
||||
this._verify(...args);
|
||||
/* end authentication response */
|
||||
})().catch((error) => {
|
||||
if (
|
||||
(error instanceof OPError && error.error !== 'server_error' && !error.error.startsWith('invalid'))
|
||||
|| error instanceof RPError
|
||||
) {
|
||||
this.fail(error);
|
||||
} else {
|
||||
this.error(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = OpenIDConnectStrategy;
|
50
node_modules/openid-client/lib/token_set.js
generated
vendored
Normal file
50
node_modules/openid-client/lib/token_set.js
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
const base64url = require('./helpers/base64url');
|
||||
const now = require('./helpers/unix_timestamp');
|
||||
|
||||
class TokenSet {
|
||||
/**
|
||||
* @name constructor
|
||||
* @api public
|
||||
*/
|
||||
constructor(values) {
|
||||
Object.assign(this, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @name expires_in=
|
||||
* @api public
|
||||
*/
|
||||
set expires_in(value) { // eslint-disable-line camelcase
|
||||
this.expires_at = now() + Number(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @name expires_in
|
||||
* @api public
|
||||
*/
|
||||
get expires_in() { // eslint-disable-line camelcase
|
||||
return Math.max.apply(null, [this.expires_at - now(), 0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @name expired
|
||||
* @api public
|
||||
*/
|
||||
expired() {
|
||||
return this.expires_in === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name claims
|
||||
* @api public
|
||||
*/
|
||||
claims() {
|
||||
if (!this.id_token) {
|
||||
throw new TypeError('id_token not present in TokenSet');
|
||||
}
|
||||
|
||||
return JSON.parse(base64url.decode(this.id_token.split('.')[1]));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TokenSet;
|
121
node_modules/openid-client/package.json
generated
vendored
Normal file
121
node_modules/openid-client/package.json
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
{
|
||||
"name": "openid-client",
|
||||
"version": "4.9.1",
|
||||
"description": "OpenID Connect Relying Party (RP, Client) implementation for Node.js runtime, supports passportjs",
|
||||
"keywords": [
|
||||
"auth",
|
||||
"authentication",
|
||||
"basic",
|
||||
"certified",
|
||||
"client",
|
||||
"connect",
|
||||
"dynamic",
|
||||
"electron",
|
||||
"hybrid",
|
||||
"identity",
|
||||
"implicit",
|
||||
"oauth",
|
||||
"oauth2",
|
||||
"oidc",
|
||||
"openid",
|
||||
"passport",
|
||||
"relying party",
|
||||
"strategy"
|
||||
],
|
||||
"homepage": "https://github.com/panva/node-openid-client",
|
||||
"repository": "panva/node-openid-client",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "Filip Skokan <panva.ip@gmail.com>",
|
||||
"exports": {
|
||||
"import": "./lib/index.mjs",
|
||||
"require": "./lib/index.js"
|
||||
},
|
||||
"main": "lib/index.js",
|
||||
"types": "types/index.d.ts",
|
||||
"files": [
|
||||
"lib",
|
||||
"types/index.d.ts"
|
||||
],
|
||||
"scripts": {
|
||||
"coverage": "nyc mocha test/**/*.test.js",
|
||||
"lint": "eslint lib test",
|
||||
"lint-fix": "eslint lib test --fix",
|
||||
"test": "mocha test/**/*.test.js"
|
||||
},
|
||||
"nyc": {
|
||||
"reporter": [
|
||||
"lcov",
|
||||
"text-summary"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"aggregate-error": "^3.1.0",
|
||||
"got": "^11.8.0",
|
||||
"jose": "^2.0.5",
|
||||
"lru-cache": "^6.0.0",
|
||||
"make-error": "^1.3.6",
|
||||
"object-hash": "^2.0.1",
|
||||
"oidc-token-hash": "^5.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/passport": "^1.0.4",
|
||||
"base64url": "^3.0.1",
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^7.12.1",
|
||||
"eslint-config-airbnb-base": "^14.2.0",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"mocha": "^8.2.0",
|
||||
"nock": "^13.0.2",
|
||||
"nyc": "^15.1.0",
|
||||
"readable-mock-req": "^0.2.2",
|
||||
"sinon": "^9.2.0",
|
||||
"timekeeper": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10.19.0 || >=12.0.0 < 13 || >=13.7.0 < 14 || >= 14.2.0"
|
||||
},
|
||||
"standard-version": {
|
||||
"scripts": {
|
||||
"postchangelog": "sed -i '' -e 's/### \\[/## [/g' CHANGELOG.md"
|
||||
},
|
||||
"types": [
|
||||
{
|
||||
"type": "feat",
|
||||
"section": "Features"
|
||||
},
|
||||
{
|
||||
"type": "fix",
|
||||
"section": "Bug Fixes"
|
||||
},
|
||||
{
|
||||
"type": "chore",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"type": "docs",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"type": "style",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"type": "refactor",
|
||||
"section": "Refactor",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"type": "perf",
|
||||
"section": "Performance",
|
||||
"hidden": false
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"hidden": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
968
node_modules/openid-client/types/index.d.ts
generated
vendored
Normal file
968
node_modules/openid-client/types/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,968 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference lib="dom"/>
|
||||
// TypeScript Version: 3.6
|
||||
|
||||
/**
|
||||
* @see https://github.com/panva/node-openid-client/blob/master/docs/README.md
|
||||
*/
|
||||
import * as http from "http";
|
||||
import * as http2 from "http2";
|
||||
|
||||
import { Options as GotOptions, CancelableRequest, Response } from "got";
|
||||
import { URL } from "url";
|
||||
import * as jose from "jose";
|
||||
import * as crypto from "crypto";
|
||||
|
||||
export type HttpOptions = GotOptions;
|
||||
export type RetryFunction = (retry: number, error: Error) => number;
|
||||
export type CustomHttpOptionsProvider = (options: HttpOptions) => HttpOptions;
|
||||
export type TokenTypeHint = "access_token" | "refresh_token" | string;
|
||||
export type DPoPInput =
|
||||
| crypto.KeyObject
|
||||
| crypto.PrivateKeyInput
|
||||
| jose.JWKRSAKey
|
||||
| jose.JWKECKey
|
||||
| jose.JWKOKPKey;
|
||||
|
||||
interface UnknownObject {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://github.com/panva/node-openid-client/blob/master/lib/index.js
|
||||
*/
|
||||
export const custom: {
|
||||
setHttpOptionsDefaults(params: HttpOptions): undefined;
|
||||
readonly http_options: unique symbol;
|
||||
readonly clock_tolerance: unique symbol;
|
||||
};
|
||||
|
||||
/**
|
||||
* @see https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660
|
||||
*/
|
||||
export type ResponseType =
|
||||
| "code"
|
||||
| "id_token"
|
||||
| "code id_token"
|
||||
| "id_token token"
|
||||
| "code token"
|
||||
| "code id_token token"
|
||||
| "none";
|
||||
/**
|
||||
* @see https://github.com/panva/node-openid-client/blob/master/docs/README.md#client-authentication-methods
|
||||
*/
|
||||
export type ClientAuthMethod =
|
||||
| "client_secret_basic"
|
||||
| "client_secret_post"
|
||||
| "client_secret_jwt"
|
||||
| "private_key_jwt"
|
||||
| "tls_client_auth"
|
||||
| "self_signed_tls_client_auth"
|
||||
| "none";
|
||||
|
||||
/**
|
||||
* @see https://github.com/panva/node-openid-client/blob/master/docs/README.md#new-clientmetadata-jwks
|
||||
*/
|
||||
export interface ClientMetadata {
|
||||
// important
|
||||
client_id: string;
|
||||
id_token_signed_response_alg?: string;
|
||||
token_endpoint_auth_method?: ClientAuthMethod;
|
||||
client_secret?: string;
|
||||
redirect_uris?: string[];
|
||||
response_types?: ResponseType[];
|
||||
post_logout_redirect_uris?: string[];
|
||||
default_max_age?: number;
|
||||
require_auth_time?: boolean;
|
||||
tls_client_certificate_bound_access_tokens?: boolean;
|
||||
request_object_signing_alg?: string;
|
||||
|
||||
// less important
|
||||
id_token_encrypted_response_alg?: string;
|
||||
id_token_encrypted_response_enc?: string;
|
||||
introspection_endpoint_auth_method?: ClientAuthMethod;
|
||||
introspection_endpoint_auth_signing_alg?: string;
|
||||
request_object_encryption_alg?: string;
|
||||
request_object_encryption_enc?: string;
|
||||
revocation_endpoint_auth_method?: ClientAuthMethod;
|
||||
revocation_endpoint_auth_signing_alg?: string;
|
||||
token_endpoint_auth_signing_alg?: string;
|
||||
userinfo_encrypted_response_alg?: string;
|
||||
userinfo_encrypted_response_enc?: string;
|
||||
userinfo_signed_response_alg?: string;
|
||||
authorization_encrypted_response_alg?: string;
|
||||
authorization_encrypted_response_enc?: string;
|
||||
authorization_signed_response_alg?: string;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface ClaimsParameterMember {
|
||||
essential?: boolean;
|
||||
value?: string;
|
||||
values?: string[];
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface AuthorizationParameters {
|
||||
acr_values?: string;
|
||||
audience?: string;
|
||||
claims?:
|
||||
| string
|
||||
| {
|
||||
id_token?: {
|
||||
[key: string]: null | ClaimsParameterMember;
|
||||
};
|
||||
userinfo?: {
|
||||
[key: string]: null | ClaimsParameterMember;
|
||||
};
|
||||
};
|
||||
claims_locales?: string;
|
||||
client_id?: string;
|
||||
code_challenge_method?: string;
|
||||
code_challenge?: string;
|
||||
display?: string;
|
||||
id_token_hint?: string;
|
||||
login_hint?: string;
|
||||
max_age?: number;
|
||||
nonce?: string;
|
||||
prompt?: string;
|
||||
redirect_uri?: string;
|
||||
registration?: string;
|
||||
request_uri?: string;
|
||||
request?: string;
|
||||
resource?: string | string[];
|
||||
response_mode?: string;
|
||||
response_type?: string;
|
||||
scope?: string;
|
||||
state?: string;
|
||||
ui_locales?: string;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface EndSessionParameters {
|
||||
id_token_hint?: TokenSet | string;
|
||||
post_logout_redirect_uri?: string;
|
||||
state?: string;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface CallbackParamsType {
|
||||
access_token?: string;
|
||||
code?: string;
|
||||
error?: string;
|
||||
error_description?: string;
|
||||
error_uri?: string;
|
||||
expires_in?: string;
|
||||
id_token?: string;
|
||||
state?: string;
|
||||
token_type?: string;
|
||||
session_state?: string;
|
||||
response?: string;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface OAuthCallbackChecks {
|
||||
/**
|
||||
* When provided the authorization response will be checked for presence of required parameters for a
|
||||
* given response_type. Use of this check is recommended.
|
||||
*/
|
||||
response_type?: string;
|
||||
/**
|
||||
* When provided the authorization response's state parameter will be checked to be the this expected one.
|
||||
* Use of this check is required if you sent a state parameter into an authorization request.
|
||||
*/
|
||||
state?: string;
|
||||
/**
|
||||
* PKCE code_verifier to be sent to the token endpoint during code exchange. Use of this check is required
|
||||
* if you sent a code_challenge parameter into an authorization request.
|
||||
*/
|
||||
code_verifier?: string;
|
||||
/**
|
||||
* This must be set to true when requesting JARM responses.
|
||||
*/
|
||||
jarm?: boolean;
|
||||
}
|
||||
|
||||
export interface OpenIDCallbackChecks extends OAuthCallbackChecks {
|
||||
/**
|
||||
* When provided the authorization response's ID Token auth_time parameter will be checked to be conform to the
|
||||
* max_age value. Use of this check is required if you sent a max_age parameter into an authorization request.
|
||||
*/
|
||||
max_age?: number;
|
||||
/**
|
||||
* When provided the authorization response's ID Token nonce parameter will be checked to be the this expected
|
||||
* one. Use of this check is required if you sent a nonce parameter into an authorization request.
|
||||
*/
|
||||
nonce?: string;
|
||||
}
|
||||
|
||||
export interface CallbackExtras {
|
||||
/**
|
||||
* extra request body properties to be sent to the AS during code exchange.
|
||||
*/
|
||||
exchangeBody?: object;
|
||||
/**
|
||||
* extra client assertion payload parameters to be sent as part of a client JWT assertion. This is only used
|
||||
* when the client's token_endpoint_auth_method is either client_secret_jwt or private_key_jwt.
|
||||
*/
|
||||
clientAssertionPayload?: object;
|
||||
/**
|
||||
* Private key to sign the DPoP Proof JWT with. This can be a crypto.KeyObject, crypto.createPrivateKey valid
|
||||
* inputs, or a JWK formatted private key.
|
||||
*/
|
||||
DPoP?: DPoPInput;
|
||||
}
|
||||
|
||||
export interface RefreshExtras {
|
||||
/**
|
||||
* extra request body properties to be sent to the AS during refresh token exchange.
|
||||
*/
|
||||
exchangeBody?: object;
|
||||
/**
|
||||
* extra client assertion payload parameters to be sent as part of a client JWT assertion.
|
||||
* This is only used when the client's token_endpoint_auth_method is either client_secret_jwt or private_key_jwt.
|
||||
*/
|
||||
clientAssertionPayload?: object;
|
||||
/**
|
||||
* Private key to sign the DPoP Proof JWT with. This can be a crypto.KeyObject, crypto.createPrivateKey valid
|
||||
* inputs, or a JWK formatted private key.
|
||||
*/
|
||||
DPoP?: DPoPInput;
|
||||
}
|
||||
|
||||
export interface GrantBody {
|
||||
grant_type: string;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface GrantExtras {
|
||||
/**
|
||||
* extra client assertion payload parameters to be sent as part of a client JWT assertion.
|
||||
* This is only used when the client's token_endpoint_auth_method is either client_secret_jwt or private_key_jwt.
|
||||
*/
|
||||
clientAssertionPayload?: object;
|
||||
/**
|
||||
* Private key to sign the DPoP Proof JWT with. This can be a crypto.KeyObject, crypto.createPrivateKey valid
|
||||
* inputs, or a JWK formatted private key.
|
||||
*/
|
||||
DPoP?: DPoPInput;
|
||||
}
|
||||
|
||||
export interface IntrospectExtras {
|
||||
/**
|
||||
* extra request body properties to be sent to the introspection endpoint.
|
||||
*/
|
||||
introspectBody?: object;
|
||||
/**
|
||||
* extra client assertion payload parameters to be sent as part of a client JWT assertion.
|
||||
* This is only used when the client's token_endpoint_auth_method is either client_secret_jwt or private_key_jwt.
|
||||
*/
|
||||
clientAssertionPayload?: object;
|
||||
}
|
||||
|
||||
export interface RevokeExtras {
|
||||
/**
|
||||
* extra request body properties to be sent to the revocation endpoint.
|
||||
*/
|
||||
revokeBody?: object;
|
||||
/**
|
||||
* extra client assertion payload parameters to be sent as part of a client JWT assertion.
|
||||
* This is only used when the client's token_endpoint_auth_method is either client_secret_jwt or private_key_jwt.
|
||||
*/
|
||||
clientAssertionPayload?: object;
|
||||
}
|
||||
|
||||
export interface RequestObjectPayload extends AuthorizationParameters {
|
||||
client_id?: string;
|
||||
iss?: string;
|
||||
aud?: string;
|
||||
iat?: number;
|
||||
exp?: number;
|
||||
jti?: string;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface RegisterOther {
|
||||
/**
|
||||
* JWK Set formatted object with private keys used for signing client assertions or decrypting responses.
|
||||
* When neither jwks_uri or jwks is present in metadata the key's public parts will be registered as jwks.
|
||||
*/
|
||||
jwks?: jose.JSONWebKeySet;
|
||||
/**
|
||||
* Initial Access Token to use as a Bearer token during the registration call.
|
||||
*/
|
||||
initialAccessToken?: string;
|
||||
}
|
||||
|
||||
export interface DeviceAuthorizationParameters {
|
||||
client_id?: string;
|
||||
scope?: string;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface DeviceAuthorizationExtras {
|
||||
/**
|
||||
* extra request body properties to be sent to the AS during the Device Access Token Request
|
||||
*/
|
||||
exchangeBody?: object;
|
||||
/**
|
||||
* extra client assertion payload parameters to be sent as part of a client JWT assertion.
|
||||
* This is only used when the client's token_endpoint_auth_method is either client_secret_jwt or private_key_jwt.
|
||||
*/
|
||||
clientAssertionPayload?: object;
|
||||
/**
|
||||
* Private key to sign the DPoP Proof JWT with. This can be a crypto.KeyObject, crypto.createPrivateKey valid
|
||||
* inputs, or a JWK formatted private key.
|
||||
*/
|
||||
DPoP?: DPoPInput;
|
||||
}
|
||||
|
||||
export interface PushedAuthorizationRequestExtras {
|
||||
clientAssertionPayload?: object;
|
||||
}
|
||||
|
||||
export type Address<ExtendedAddress extends {} = UnknownObject> = Override<
|
||||
{
|
||||
formatted?: string;
|
||||
street_address?: string;
|
||||
locality?: string;
|
||||
region?: string;
|
||||
postal_code?: string;
|
||||
country?: string;
|
||||
},
|
||||
ExtendedAddress
|
||||
>;
|
||||
|
||||
export type UserinfoResponse<
|
||||
UserInfo extends {} = UnknownObject,
|
||||
ExtendedAddress extends {} = UnknownObject
|
||||
> = Override<
|
||||
{
|
||||
sub: string;
|
||||
name?: string;
|
||||
given_name?: string;
|
||||
family_name?: string;
|
||||
middle_name?: string;
|
||||
nickname?: string;
|
||||
preferred_username?: string;
|
||||
profile?: string;
|
||||
picture?: string;
|
||||
website?: string;
|
||||
email?: string;
|
||||
email_verified?: boolean;
|
||||
gender?: string;
|
||||
birthdate?: string;
|
||||
zoneinfo?: string;
|
||||
locale?: string;
|
||||
phone_number?: string;
|
||||
updated_at?: number;
|
||||
address?: Address<ExtendedAddress>;
|
||||
},
|
||||
UserInfo
|
||||
>;
|
||||
|
||||
export interface IntrospectionResponse {
|
||||
active: boolean;
|
||||
client_id?: string;
|
||||
exp?: number;
|
||||
iat?: number;
|
||||
sid?: string;
|
||||
iss?: string;
|
||||
jti?: string;
|
||||
username?: string;
|
||||
aud?: string | string[];
|
||||
scope: string;
|
||||
sub?: string;
|
||||
nbf?: number;
|
||||
token_type?: string;
|
||||
cnf?: {
|
||||
"x5t#S256"?: string;
|
||||
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface ClientOptions {
|
||||
additionalAuthorizedParties?: string | string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates a dynamically registered, discovered or instantiated OpenID Connect Client (Client),
|
||||
* Relying Party (RP), and its metadata, its instances hold the methods for getting an authorization URL,
|
||||
* consuming callbacks, triggering token endpoint grants, revoking and introspecting tokens.
|
||||
*/
|
||||
export class Client {
|
||||
constructor(
|
||||
metadata: ClientMetadata,
|
||||
jwks?: jose.JSONWebKeySet,
|
||||
options?: ClientOptions
|
||||
);
|
||||
[custom.http_options]: CustomHttpOptionsProvider;
|
||||
[custom.clock_tolerance]: number;
|
||||
metadata: ClientMetadata;
|
||||
issuer: Issuer<this>;
|
||||
static issuer: Issuer<Client>;
|
||||
|
||||
/**
|
||||
* Returns the target authorization redirect URI to redirect End-Users to using the provided parameters.
|
||||
* @param parameters Authorization Request parameters
|
||||
*/
|
||||
authorizationUrl(parameters?: AuthorizationParameters): string;
|
||||
|
||||
/**
|
||||
* Returns the target logout redirect URI to redirect End-Users to using the provided parameters.
|
||||
* @param parameters RP-Initiated Logout Request parameters
|
||||
*/
|
||||
endSessionUrl(parameters?: EndSessionParameters): string;
|
||||
|
||||
/**
|
||||
* Returns recognized callback parameters from a provided input.
|
||||
*
|
||||
* - When input is of type string it will be parsed using url.parse and its query component will be returned
|
||||
* - When input is a GET http/http2 request object its url property will be parsed using url.parse and its
|
||||
* query component will be returned
|
||||
* - When input is a POST http/http2 request object its body property will be parsed or returned if it is already
|
||||
* an object. Note: the request read stream will not be parsed, it is expected that you will have a body parser
|
||||
* prior to calling this method. This parser would set the req.body property
|
||||
*/
|
||||
callbackParams(
|
||||
input: string | http.IncomingMessage | http2.Http2ServerRequest
|
||||
): CallbackParamsType;
|
||||
|
||||
/**
|
||||
* Performs the callback for Authorization Server's authorization response.
|
||||
* @param redirectUri redirect_uri used for the authorization request
|
||||
* @param parameters returned authorization response, see client.callbackParams if you need help getting them.
|
||||
* @param checks checks to perform on the Authorization Response
|
||||
* @param extras add extra parameters to the Token Endpoint Request and/or Client Authentication JWT Assertion
|
||||
*/
|
||||
callback(
|
||||
redirectUri: string | undefined,
|
||||
parameters: CallbackParamsType,
|
||||
checks?: OpenIDCallbackChecks,
|
||||
extras?: CallbackExtras
|
||||
): Promise<TokenSet>;
|
||||
|
||||
/**
|
||||
* Pure OAuth 2.0 version of callback().
|
||||
* @param redirectUri redirect_uri used for the authorization request
|
||||
* @param parameters returned authorization response, see client.callbackParams if you need help getting them.
|
||||
* @param checks checks to perform on the Authorization Response
|
||||
* @param extras add extra parameters to the Token Endpoint Request and/or Client Authentication JWT Assertion
|
||||
*/
|
||||
oauthCallback(
|
||||
redirectUri: string | undefined,
|
||||
parameters: CallbackParamsType,
|
||||
checks?: OAuthCallbackChecks,
|
||||
extras?: CallbackExtras
|
||||
): Promise<TokenSet>;
|
||||
|
||||
/**
|
||||
* Performs refresh_token grant type exchange.
|
||||
* @param refreshToken Refresh Token value. When TokenSet instance is provided its refresh_token property
|
||||
* will be used automatically.
|
||||
* @param extras Add extra parameters to the Token Endpoint Request and/or Client Authentication JWT Assertion
|
||||
*/
|
||||
refresh(
|
||||
refreshToken: TokenSet | string,
|
||||
extras?: RefreshExtras
|
||||
): Promise<TokenSet>;
|
||||
|
||||
/**
|
||||
* Fetches the OIDC userinfo response with the provided Access Token. Also handles signed and/or
|
||||
* encrypted userinfo responses. When TokenSet is provided as an argument the userinfo sub property
|
||||
* will also be checked to match the on in the TokenSet's ID Token.
|
||||
*
|
||||
* @param accessToken Access Token value. When TokenSet instance is provided its access_token property
|
||||
* will be used automatically.
|
||||
* @param options Options for the UserInfo request.
|
||||
*/
|
||||
userinfo<
|
||||
TUserInfo extends {} = UnknownObject,
|
||||
TAddress extends {} = UnknownObject
|
||||
>(
|
||||
accessToken: TokenSet | string,
|
||||
options?: {
|
||||
method?: "GET" | "POST";
|
||||
via?: "header" | "body" | "query";
|
||||
tokenType?: string;
|
||||
params?: object;
|
||||
DPoP?: DPoPInput;
|
||||
}
|
||||
): Promise<UserinfoResponse<TUserInfo, TAddress>>;
|
||||
|
||||
/**
|
||||
* Fetches an arbitrary resource with the provided Access Token in an Authorization header.
|
||||
*
|
||||
* @param resourceUrl Resource URL to request a response from.
|
||||
* @param accessToken Access Token value. When TokenSet instance is provided its access_token property
|
||||
* will be used automatically.
|
||||
* @param options Options for the request.
|
||||
*/
|
||||
requestResource(
|
||||
resourceUrl: string | URL,
|
||||
accessToken: TokenSet | string,
|
||||
options?: {
|
||||
headers?: object;
|
||||
body?: string | Buffer;
|
||||
method?: "GET" | "POST" | "PUT" | "HEAD" | "DELETE" | "OPTIONS" | "TRACE" | "PATCH";
|
||||
tokenType?: string;
|
||||
DPoP?: DPoPInput;
|
||||
}
|
||||
): CancelableRequest<Response<Buffer>>;
|
||||
|
||||
/**
|
||||
* Performs an arbitrary grant_type exchange at the token_endpoint.
|
||||
*/
|
||||
grant(body: GrantBody, extras?: GrantExtras): Promise<TokenSet>;
|
||||
|
||||
/**
|
||||
* Introspects a token at the Authorization Server's introspection_endpoint.
|
||||
*/
|
||||
introspect(
|
||||
token: string,
|
||||
tokenTypeHint?: TokenTypeHint,
|
||||
extras?: IntrospectExtras
|
||||
): Promise<IntrospectionResponse>;
|
||||
|
||||
/**
|
||||
* Revokes a token at the Authorization Server's revocation_endpoint.
|
||||
*/
|
||||
revoke(
|
||||
token: string,
|
||||
tokenTypeHint?: TokenTypeHint,
|
||||
extras?: RevokeExtras
|
||||
): Promise<undefined>;
|
||||
|
||||
/**
|
||||
* Creates a signed and optionally encrypted Request Object to send to the AS. Uses the client's
|
||||
* request_object_signing_alg, request_object_encryption_alg, request_object_encryption_enc metadata for
|
||||
* determining the algorithms to use.
|
||||
*/
|
||||
requestObject(payload: RequestObjectPayload): Promise<string>;
|
||||
|
||||
/**
|
||||
* Starts a Device Authorization Request at the issuer's device_authorization_endpoint and returns a handle
|
||||
* for subsequent Device Access Token Request polling.
|
||||
*/
|
||||
deviceAuthorization(
|
||||
parameters?: DeviceAuthorizationParameters,
|
||||
extras?: DeviceAuthorizationExtras
|
||||
): Promise<DeviceFlowHandle<Client>>;
|
||||
pushedAuthorizationRequest(
|
||||
parameters?: AuthorizationParameters,
|
||||
extras?: PushedAuthorizationRequestExtras,
|
||||
): Promise<{
|
||||
request_uri: string;
|
||||
expires_in: number;
|
||||
[key: string]: unknown;
|
||||
}>;
|
||||
static register(
|
||||
metadata: object,
|
||||
other?: RegisterOther & ClientOptions
|
||||
): Promise<Client>;
|
||||
static fromUri(
|
||||
registrationClientUri: string,
|
||||
registrationAccessToken: string,
|
||||
jwks?: jose.JSONWebKeySet,
|
||||
clientOptions?: ClientOptions
|
||||
): Promise<Client>;
|
||||
static [custom.http_options]: CustomHttpOptionsProvider;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface DeviceFlowPollOptions {
|
||||
signal?: AbortSignal,
|
||||
}
|
||||
|
||||
export class DeviceFlowHandle<TClient extends Client> {
|
||||
// tslint:disable-line:no-unnecessary-generics
|
||||
poll(options?: DeviceFlowPollOptions): Promise<TokenSet>;
|
||||
abort(): void;
|
||||
expired(): boolean;
|
||||
expires_at: number;
|
||||
client: TClient;
|
||||
user_code: string;
|
||||
device_code: string;
|
||||
verification_uri: string;
|
||||
verification_uri_complete: string;
|
||||
expires_in: number;
|
||||
}
|
||||
|
||||
export interface IssuerMetadata {
|
||||
issuer: string;
|
||||
authorization_endpoint?: string;
|
||||
token_endpoint?: string;
|
||||
jwks_uri?: string;
|
||||
userinfo_endpoint?: string;
|
||||
revocation_endpoint?: string;
|
||||
end_session_endpoint?: string;
|
||||
registration_endpoint?: string;
|
||||
token_endpoint_auth_methods_supported?: string[];
|
||||
token_endpoint_auth_signing_alg_values_supported?: string[];
|
||||
introspection_endpoint_auth_methods_supported?: string[];
|
||||
introspection_endpoint_auth_signing_alg_values_supported?: string[];
|
||||
revocation_endpoint_auth_methods_supported?: string[];
|
||||
revocation_endpoint_auth_signing_alg_values_supported?: string[];
|
||||
request_object_signing_alg_values_supported?: string[];
|
||||
mtls_endpoint_aliases?: MtlsEndpointAliases;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface MtlsEndpointAliases {
|
||||
token_endpoint?: string;
|
||||
userinfo_endpoint?: string;
|
||||
revocation_endpoint?: string;
|
||||
introspection_endpoint?: string;
|
||||
device_authorization_endpoint?: string;
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/40249906/using-a-generic-type-argument-with-typeof-t
|
||||
// https://stackoverflow.com/questions/39622778/what-is-new-in-typescript
|
||||
// https://github.com/Microsoft/TypeScript/issues/204
|
||||
export interface TypeOfGenericClient<TClient extends Client> {
|
||||
new (
|
||||
metadata: ClientMetadata,
|
||||
jwks?: jose.JSONWebKeySet,
|
||||
options?: ClientOptions
|
||||
): TClient;
|
||||
[custom.http_options]: CustomHttpOptionsProvider;
|
||||
[custom.clock_tolerance]: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates a discovered or instantiated OpenID Connect Issuer (Issuer), Identity Provider (IdP),
|
||||
* Authorization Server (AS) and its metadata.
|
||||
*/
|
||||
export class Issuer<TClient extends Client> {
|
||||
// tslint:disable-line:no-unnecessary-generics
|
||||
constructor(metadata: IssuerMetadata);
|
||||
|
||||
/**
|
||||
* Returns the <Client> class tied to this issuer.
|
||||
*/
|
||||
Client: TypeOfGenericClient<TClient>;
|
||||
|
||||
/**
|
||||
* Returns the <FAPIClient> class tied to this issuer.
|
||||
*/
|
||||
FAPIClient: TypeOfGenericClient<TClient>;
|
||||
|
||||
/**
|
||||
* Returns metadata from the issuer's discovery document.
|
||||
*/
|
||||
metadata: IssuerMetadata;
|
||||
[custom.http_options]: CustomHttpOptionsProvider;
|
||||
|
||||
/**
|
||||
* Returns the issuer's jwks_uri keys as a `jose` parsed JWKS.Keystore.
|
||||
* @param forceReload forces a reload of the issuer's jwks_uri
|
||||
*/
|
||||
keystore(forceReload?: boolean): Promise<jose.JWKS.KeyStore>;
|
||||
|
||||
/**
|
||||
* Loads OpenID Connect 1.0 and/or OAuth 2.0 Authorization Server Metadata documents.
|
||||
* When the issuer argument contains '.well-known' only that document is loaded, otherwise
|
||||
* performs both openid-configuration and oauth-authorization-server requests.
|
||||
* @param issuer Issuer Identifier or metadata URL
|
||||
*/
|
||||
static discover(issuer: string): Promise<Issuer<Client>>;
|
||||
|
||||
/**
|
||||
* Performs OpenID Provider Issuer Discovery based on End-User input.
|
||||
* @param input EMAIL, URL, Hostname and Port, acct or syntax input
|
||||
*/
|
||||
static webfinger(input: string): Promise<Issuer<Client>>;
|
||||
|
||||
static [custom.http_options]: CustomHttpOptionsProvider;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface TokenSetParameters {
|
||||
/**
|
||||
* The raw access token in JWT format
|
||||
*/
|
||||
access_token?: string;
|
||||
/**
|
||||
* Usually "Bearer"
|
||||
*/
|
||||
token_type?: string;
|
||||
/**
|
||||
* The raw id token in JWT format
|
||||
*/
|
||||
id_token?: string;
|
||||
/**
|
||||
* Refresh token, opaque string value
|
||||
*/
|
||||
refresh_token?: string;
|
||||
/**
|
||||
* space-separated scope(s) used for the authentication request
|
||||
*/
|
||||
scope?: string;
|
||||
|
||||
/**
|
||||
* When the token set was received the expires_at field was calculated based on current timestamp
|
||||
* and the expires_in. When recalling a TokenSet instance just the expires_at should be passed in.
|
||||
*/
|
||||
expires_at?: number;
|
||||
/**
|
||||
* State value passed in the authentication request
|
||||
*/
|
||||
session_state?: string;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface IdTokenClaims extends UserinfoResponse {
|
||||
acr?: string;
|
||||
amr?: string[];
|
||||
at_hash?: string;
|
||||
aud: string | string[];
|
||||
auth_time?: number;
|
||||
azp?: string;
|
||||
c_hash?: string;
|
||||
exp: number;
|
||||
iat: number;
|
||||
iss: string;
|
||||
nonce?: string;
|
||||
s_hash?: string;
|
||||
sub: string;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new TokenSet from the provided response. E.g. parsed token endpoint response, parsed callback
|
||||
* parameters. You only need to instantiate a TokenSet yourself if you recall it from e.g. distributed cache
|
||||
* storage or a database. Note: manually constructed TokenSet instances do not undergo any validations.
|
||||
*/
|
||||
export class TokenSet implements TokenSetParameters {
|
||||
access_token?: string;
|
||||
token_type?: string;
|
||||
id_token?: string;
|
||||
refresh_token?: string;
|
||||
expires_in?: number;
|
||||
expires_at?: number;
|
||||
session_state?: string;
|
||||
scope?: string;
|
||||
|
||||
constructor(input?: TokenSetParameters);
|
||||
|
||||
/**
|
||||
* Given that the instance has expires_at / expires_in this function returns true / false when the
|
||||
* access token (which expires properties are for) is beyond its lifetime.
|
||||
*/
|
||||
expired(): boolean;
|
||||
|
||||
/**
|
||||
* Given that the instance has an id_token this function returns its parsed payload object.
|
||||
* Does not perform any validations as these were done prior to openid-client returning the
|
||||
* tokenset in the first place.
|
||||
*/
|
||||
claims(): IdTokenClaims;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export type StrategyVerifyCallbackUserInfo<
|
||||
TUser,
|
||||
TUserInfo extends {} = UnknownObject,
|
||||
TAddress extends {} = UnknownObject
|
||||
> = (
|
||||
tokenset: TokenSet,
|
||||
userinfo: UserinfoResponse<TUserInfo, TAddress>,
|
||||
done: (err: any, user?: TUser) => void
|
||||
) => void;
|
||||
export type StrategyVerifyCallback<TUser> = (
|
||||
tokenset: TokenSet,
|
||||
done: (err: any, user?: TUser) => void
|
||||
) => void;
|
||||
export type StrategyVerifyCallbackReqUserInfo<
|
||||
TUser,
|
||||
TUserInfo extends {} = UnknownObject,
|
||||
TAddress extends {} = UnknownObject
|
||||
> = (
|
||||
req: http.IncomingMessage,
|
||||
tokenset: TokenSet,
|
||||
userinfo: UserinfoResponse<TUserInfo, TAddress>,
|
||||
done: (err: any, user?: TUser) => void
|
||||
) => void;
|
||||
export type StrategyVerifyCallbackReq<TUser> = (
|
||||
req: http.IncomingMessage,
|
||||
tokenset: TokenSet,
|
||||
done: (err: any, user?: TUser) => void
|
||||
) => void;
|
||||
|
||||
export interface StrategyOptions<TClient extends Client> {
|
||||
client: TClient;
|
||||
/**
|
||||
* Authorization Request parameters. The strategy will use these.
|
||||
*/
|
||||
params?: AuthorizationParameters;
|
||||
|
||||
/**
|
||||
* "extras" argument value passed to the client.callback() call.
|
||||
*/
|
||||
extras?: CallbackExtras;
|
||||
/**
|
||||
* Boolean specifying whether the verify function should get the request object as first argument instead.
|
||||
*/
|
||||
passReqToCallback?: boolean;
|
||||
/**
|
||||
* The PKCE method to use. When 'true' it will resolve based on the issuer metadata, when 'false' no PKCE will be
|
||||
* used.
|
||||
*/
|
||||
usePKCE?: boolean | string;
|
||||
/**
|
||||
* The property name to store transaction information such as nonce, state, max_age, code_verifier, and response_type.
|
||||
*/
|
||||
sessionKey?: string;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:no-unnecessary-class
|
||||
export class Strategy<TUser, TClient extends Client> {
|
||||
// tslint:disable-line:no-unnecessary-generics
|
||||
constructor(
|
||||
options: StrategyOptions<TClient>,
|
||||
verify:
|
||||
| StrategyVerifyCallback<TUser>
|
||||
| StrategyVerifyCallbackUserInfo<TUser>
|
||||
| StrategyVerifyCallbackReq<TUser>
|
||||
| StrategyVerifyCallbackReqUserInfo<TUser>
|
||||
);
|
||||
|
||||
authenticate(req: any, options?: any): void;
|
||||
success(user: any, info?: any): void;
|
||||
fail(challenge: any, status: number): void;
|
||||
fail(status: number): void;
|
||||
redirect(url: string, status?: number): void;
|
||||
pass(): void;
|
||||
error(err: Error): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://github.com/panva/node-openid-client/blob/master/lib/helpers/generators.js
|
||||
*/
|
||||
export namespace generators {
|
||||
/**
|
||||
* Generates random bytes and encodes them in url safe base64.
|
||||
* @param bytes Number indicating the number of bytes to generate.
|
||||
*/
|
||||
function random(bytes?: number): string;
|
||||
|
||||
/**
|
||||
* Generates random bytes and encodes them in url safe base64.
|
||||
* @param bytes Number indicating the number of bytes to generate.
|
||||
*/
|
||||
function state(bytes?: number): string;
|
||||
|
||||
/**
|
||||
* Generates random bytes and encodes them in url safe base64.
|
||||
* @param bytes Number indicating the number of bytes to generate.
|
||||
*/
|
||||
function nonce(bytes?: number): string;
|
||||
|
||||
/**
|
||||
* Generates random bytes and encodes them in url safe base64.
|
||||
* @param bytes Number indicating the number of bytes to generate.
|
||||
*/
|
||||
function codeVerifier(bytes?: number): string;
|
||||
/**
|
||||
* Calculates the S256 PKCE code challenge for an arbitrary code verifier.
|
||||
* Encodes in url safe base64.
|
||||
* @param verifier Code verifier to calculate the S256 code challenge for
|
||||
*/
|
||||
function codeChallenge(verifier: string): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://github.com/panva/node-openid-client/blob/master/lib/errors.js
|
||||
*/
|
||||
export namespace errors {
|
||||
/**
|
||||
* Error class thrown when a regular OAuth 2.0 / OIDC style error is returned by the AS or an
|
||||
* unexpected response is sent by the OP.
|
||||
*/
|
||||
class OPError extends Error {
|
||||
/**
|
||||
* The 'error_description' parameter from the AS response.
|
||||
*/
|
||||
error_description?: string;
|
||||
/**
|
||||
* The 'error' parameter from the AS response.
|
||||
*/
|
||||
error?: string;
|
||||
/**
|
||||
* The 'error_uri' parameter from the AS response.
|
||||
*/
|
||||
error_uri?: string;
|
||||
/**
|
||||
* The 'state' parameter from the AS response.
|
||||
*/
|
||||
state?: string;
|
||||
/**
|
||||
* The 'scope' parameter from the AS response.
|
||||
*/
|
||||
scope?: string;
|
||||
/**
|
||||
* The 'session_state' parameter from the AS response.
|
||||
*/
|
||||
session_state?: string;
|
||||
|
||||
/**
|
||||
* When the error is related to an http(s) request this propetty will hold the response object
|
||||
* from got.
|
||||
*/
|
||||
response?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Error class thrown when client-side response expectations/validations fail to pass.
|
||||
* Depending on the context it may or may not have additional context-based properties like
|
||||
* checks, jwt, params or body.
|
||||
*/
|
||||
class RPError extends Error {
|
||||
jwt?: string;
|
||||
checks?: object;
|
||||
params?: object;
|
||||
body?: object;
|
||||
/**
|
||||
* When the error is related to an http(s) request this propetty will hold the response object
|
||||
* from got.
|
||||
*/
|
||||
response?: any;
|
||||
now?: number;
|
||||
tolerance?: number;
|
||||
nbf?: number;
|
||||
exp?: number;
|
||||
iat?: number;
|
||||
auth_time?: number;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is very useful to allow applications to override property types
|
||||
* without making types in this package too weird
|
||||
*/
|
||||
// https://github.com/Microsoft/TypeScript/issues/25987#issuecomment-441224690
|
||||
type KnownKeys<T> = {
|
||||
[K in keyof T]: string extends K ? never : number extends K ? never : K;
|
||||
} extends { [_ in keyof T]: infer U }
|
||||
? {} extends U
|
||||
? never
|
||||
: U
|
||||
: never;
|
||||
type Override<T1, T2> = Omit<T1, keyof Omit<T2, keyof KnownKeys<T2>>> & T2;
|
Reference in New Issue
Block a user