Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

oidc-provider

Package Overview
Dependencies
Maintainers
1
Versions
339
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

oidc-provider - npm Package Compare versions

Comparing version 1.3.0 to 1.4.0

17

CHANGELOG.md

@@ -8,2 +8,4 @@ # oidc-provider CHANGELOG

<!-- TOC START min:2 max:2 link:true update:true -->
- [Version 1.4.0](#version-140)
- [Version 1.3.0](#version-130)
- [Version 1.2.0](#version-120)

@@ -20,2 +22,15 @@ - [Version 1.1.0](#version-110)

## Version 1.4.0
- [DIFF](https://github.com/panva/node-oidc-provider/compare/v1.3.0...v1.4.0)
- added optional support for [OAuth 2.0 for Native Apps BCP - draft 06][feature-oauth-native-apps]
- enable with configuration `features.oauthNativeApps = true`;
- offline_access scope is now ignored when consent prompt is missing instead of being rejected as invalid_request
- unrecognized authentication requests scopes are now ignored instead of being rejected as invalid_request
- renamed the refreshToken feature flag to a more appropriate alwaysIssueRefresh
## Version 1.3.0
- [DIFF](https://github.com/panva/node-oidc-provider/compare/v1.2.0...v1.3.0)
- added optional Registration Access Token rotation strategy for Dynamic Client Registration Management Protocol
- added request ctx bind to findById
## Version 1.2.0

@@ -171,1 +186,3 @@ - [DIFF](https://github.com/panva/node-oidc-provider/compare/v1.1.0...v1.2.0)

prohibiting any tampering with the payload and header content.
[feature-oauth-native-apps]: https://tools.ietf.org/html/draft-ietf-oauth-native-apps-06

15

lib/actions/authorization/check_scope.js

@@ -13,10 +13,6 @@ 'use strict';

module.exports = provider => function* checkScope(next) {
const scopes = this.oidc.params.scope.split(' ');
const scopes = _.intersection(this.oidc.params.scope.split(' '), instance(provider).configuration('scopes'));
const responseType = this.oidc.params.response_type;
const prompts = this.oidc.prompts;
const unsupported = _.difference(scopes, instance(provider).configuration('scopes'));
this.assert(_.isEmpty(unsupported), new errors.InvalidRequestError(
`invalid scope value(s) provided. (${unsupported.join(',')})`));
this.assert(scopes.indexOf('openid') !== -1,

@@ -34,12 +30,11 @@ new errors.InvalidRequestError('openid is required scope'));

if (scopes.indexOf('offline_access') !== -1) {
if (responseType.includes('code')) {
this.assert(prompts.indexOf('consent') !== -1,
new errors.InvalidRequestError('offline_access scope requires consent prompt'));
} else {
this.oidc.params.scope = _.pull(scopes, 'offline_access').join(' ');
if (!responseType.includes('code') || prompts.indexOf('consent') === -1) {
_.pull(scopes, 'offline_access').join(' ');
}
}
this.oidc.params.scope = scopes.join(' ');
yield next;
};

@@ -75,3 +75,3 @@ 'use strict';

const grantPresent = this.oidc.client.grantTypes.indexOf('refresh_token') !== -1;
const shouldIssue = instance(provider).configuration('features.refreshToken') ||
const shouldIssue = instance(provider).configuration('features.alwaysIssueRefresh') ||
code.scope.split(' ').indexOf('offline_access') !== -1;

@@ -78,0 +78,0 @@

@@ -10,2 +10,6 @@ 'use strict';

function invalidate(message) {
throw new errors.InvalidClientMetadata(message);
}
const RECOGNIZED_METADATA = [

@@ -147,2 +151,4 @@ 'application_type',

const LOOPBACKS = ['localhost', '127.0.0.1', '::1'];
module.exports = function getSchema(provider) {

@@ -204,2 +210,3 @@ const ENUM = {

this.redirectUris();
this.normalizeNativeAppUris();

@@ -209,3 +216,3 @@ // MAX AGE FORMAT

if (!Number.isInteger(this.default_max_age) || this.default_max_age <= 0) {
throw new errors.InvalidClientMetadata('default_max_age must be a positive integer');
invalidate('default_max_age must be a positive integer');
}

@@ -222,4 +229,3 @@ }

if (_.includes(this.grant_types, 'authorization_code')) {
throw new errors.InvalidClientMetadata(
'grant_types must not use token endpoint when token_endpoint_auth_method is none');
invalidate('grant_types must not use token endpoint when token_endpoint_auth_method is none');
}

@@ -229,4 +235,3 @@ }

if (_.includes(rts, 'code') && !_.includes(this.grant_types, 'authorization_code')) {
throw new errors.InvalidClientMetadata(
'grant_types must contain authorization_code when code is amongst response_types');
invalidate('grant_types must contain authorization_code when code is amongst response_types');
}

@@ -236,4 +241,3 @@

if (!_.includes(this.grant_types, 'implicit')) {
throw new errors.InvalidClientMetadata(
'grant_types must contain implicit when id_token or token are amongst response_types');
invalidate('grant_types must contain implicit when id_token or token are amongst response_types');
}

@@ -257,3 +261,3 @@ }

if (validateSecretPresence && !this.client_secret) {
throw new errors.InvalidClientMetadata('client_secret is mandatory property');
invalidate('client_secret is mandatory property');
}

@@ -263,3 +267,3 @@

if (this.client_secret.length < validateSecretLength) {
throw new errors.InvalidClientMetadata('insufficient client_secret length');
invalidate('insufficient client_secret length');
}

@@ -278,4 +282,3 @@ }

} else {
throw new errors.InvalidClientMetadata(
'sector_identifier_uri is required when using multiple hosts in your redirect_uris');
invalidate('sector_identifier_uri is required when using multiple hosts in your redirect_uris');
}

@@ -287,4 +290,3 @@ } else if (this.sector_identifier_uri) {

if (this.jwks !== undefined && this.jwks_uri !== undefined) {
throw new errors.InvalidClientMetadata(
'jwks and jwks_uri must not be used at the same time');
invalidate('jwks and jwks_uri must not be used at the same time');
}

@@ -294,6 +296,6 @@

if (!Array.isArray(this.jwks.keys)) {
throw new errors.InvalidClientMetadata('jwks must be a JWK Set');
invalidate('jwks must be a JWK Set');
}
if (!this.jwks.keys.length) {
throw new errors.InvalidClientMetadata('jwks.keys must not be empty');
invalidate('jwks.keys must not be empty');
}

@@ -306,3 +308,3 @@ }

if (!this[prop]) {
throw new errors.InvalidClientMetadata(`${prop} is mandatory property`);
invalidate(`${prop} is mandatory property`);
}

@@ -317,3 +319,3 @@ });

if (requireJwks && !this.jwks && !this.jwks_uri) {
throw new errors.InvalidClientMetadata('jwks or jwks_uri is mandatory for this client');
invalidate('jwks or jwks_uri is mandatory for this client');
}

@@ -328,5 +330,5 @@ }

if (typeof val !== 'string' || !val.length) {
throw new errors.InvalidClientMetadata(
isAry ? `${prop} must only contain strings` :
`${prop} must be a non-empty string if provided`);
invalidate(isAry ?
`${prop} must only contain strings` :
`${prop} must be a non-empty string if provided`);
}

@@ -346,4 +348,5 @@ });

if (!validUrl[method](val)) {
throw new errors.InvalidClientMetadata(
isAry ? `${prop} must only contain ${type} uris` : `${prop} must be a ${type} uri`);
invalidate(isAry ?
`${prop} must only contain ${type} uris` :
`${prop} must be a ${type} uri`);
}

@@ -359,3 +362,3 @@ });

if (!Array.isArray(this[prop])) {
throw new errors.InvalidClientMetadata(`${prop} must be an array`);
invalidate(`${prop} must be an array`);
}

@@ -373,3 +376,3 @@ this[prop] = _.uniq(this[prop]);

if (this[prop] !== undefined && !this[prop].length) {
throw new errors.InvalidClientMetadata(`${prop} must contain members`);
invalidate(`${prop} must contain members`);
}

@@ -383,3 +386,3 @@ });

if (typeof this[prop] !== 'boolean') {
throw new errors.InvalidClientMetadata(`${prop} must be a boolean`);
invalidate(`${prop} must be a boolean`);
}

@@ -393,3 +396,3 @@ }

if (this[when] !== undefined && this[then[0]] === undefined) {
throw new errors.InvalidClientMetadata(`${then[0]} is mandatory property`);
invalidate(`${then[0]} is mandatory property`);
} else if (this[when] === undefined && this[then[0]] !== undefined) {

@@ -415,5 +418,5 @@ this[when] = then[1];

})) {
throw new errors.InvalidClientMetadata(`${prop} can only contain members [${only}]`);
invalidate(`${prop} can only contain members [${only}]`);
} else if (!isAry && only.indexOf(this[prop]) === -1) {
throw new errors.InvalidClientMetadata(`${prop} must be one of [${only}]`);
invalidate(`${prop} must be one of [${only}]`);
}

@@ -424,6 +427,22 @@ }

normalizeNativeAppUris() {
if (this.application_type === 'web') return;
if (!instance(provider).configuration('features.oauthNativeApps')) return;
this.redirect_uris = _.map(this.redirect_uris, (redirectUri) => {
if (redirectUri.startsWith('http:')) { // this removes the port component, making dynamic ports allowed
return url.format(Object.assign(url.parse(redirectUri), {
host: null,
port: null,
}));
}
return redirectUri;
});
}
redirectUris() {
this.redirect_uris.forEach((redirectUri) => {
if (redirectUri.indexOf('#') !== -1) {
throw new errors.InvalidClientMetadata('redirect_uris must not contain fragments');
invalidate('redirect_uris must not contain fragments');
}

@@ -434,13 +453,11 @@

if (!validUrl.isWebUri(redirectUri)) {
throw new errors.InvalidClientMetadata('redirect_uris must only contain valid web uris');
invalidate('redirect_uris must only contain valid web uris');
}
if (this.grant_types.indexOf('implicit') !== -1 && redirectUri.startsWith('http:')) {
throw new errors.InvalidClientMetadata(
'redirect_uris for web clients using implicit flow MUST only register URLs using the https scheme');
invalidate('redirect_uris for web clients using implicit flow MUST only register URLs using the https scheme');
}
if (url.parse(redirectUri).hostname === 'localhost') {
throw new errors.InvalidClientMetadata(
'redirect_uris for web clients must not be using localhost');
invalidate('redirect_uris for web clients must not be using localhost');
}

@@ -450,12 +467,33 @@ break;

if (!validUrl.isUri(redirectUri)) {
throw new errors.InvalidClientMetadata('redirect_uris must only contain valid uris');
invalidate('redirect_uris must only contain valid uris');
}
if (redirectUri.startsWith('https:')) {
throw new errors.InvalidClientMetadata(
'redirect_uris for native clients must not be using https URI scheme');
if (instance(provider).configuration('features.oauthNativeApps')) {
const uri = url.parse(redirectUri);
switch (uri.protocol) {
case 'http:': // Loopback URI Redirection
if (LOOPBACKS.indexOf(uri.hostname) === -1) {
invalidate('redirect_uris for native clients using http as a protocol can only use loopback addresses as hostnames');
}
break;
case 'https:': // App-claimed HTTPS URI Redirection
if (LOOPBACKS.indexOf(uri.hostname) !== -1) {
invalidate(`redirect_uris for native clients using claimed HTTPS URIs must not be using ${uri.hostname} as hostname`);
}
break;
default: // App-declared Custom URI Scheme Redirection
if (uri.hostname !== 'localhost') {
invalidate('redirect_uris for native clients using custom URI scheme must be using localhost as hostname');
}
}
} else {
if (redirectUri.startsWith('https:')) {
invalidate('redirect_uris for native clients must not be using https URI scheme');
}
if (url.parse(redirectUri).hostname !== 'localhost') {
invalidate('redirect_uris for native clients must be using localhost as hostname');
}
}
if (url.parse(redirectUri).hostname !== 'localhost') {
throw new errors.InvalidClientMetadata(
'redirect_uris for native clients must be using localhost as hostname');
}
break;

@@ -462,0 +500,0 @@ }

'use strict';
const _ = require('lodash');
const util = require('util');
const defaults = require('./defaults');

@@ -59,2 +60,7 @@

const rtFlagRename = util.deprecate(/* istanbul ignore next */ function refreshTokenRename() {
this.features.alwaysIssueRefresh = this.features.refreshToken;
delete this.features.refreshToken;
}, 'features.refreshToken: Use features.alwaysIssueRefresh instead');
module.exports = class ConfigurationSchema {

@@ -91,3 +97,9 @@ constructor(config) {

if (this.features.refreshToken || this.scopes.indexOf('offline_access') !== -1) {
// 2.0 DEPRECATED
/* istanbul ignore if */
if (Object.keys(this.features).indexOf('refreshToken') !== -1) {
rtFlagRename.call(this);
}
if (this.features.alwaysIssueRefresh || this.scopes.indexOf('offline_access') !== -1) {
this.grantTypes.add('refresh_token');

@@ -94,0 +106,0 @@ }

@@ -11,3 +11,3 @@ 'use strict';

'introspection',
'refreshToken',
'alwaysIssueRefresh',
'registration',

@@ -14,0 +14,0 @@ 'request',

@@ -97,3 +97,3 @@ 'use strict';

introspection: false,
refreshToken: false,
alwaysIssueRefresh: false,
registration: false,

@@ -104,2 +104,3 @@ registrationManagement: false,

revocation: false,
oauthNativeApps: false,
sessionManagement: false,

@@ -106,0 +107,0 @@ },

@@ -225,4 +225,19 @@ 'use strict';

redirectUriAllowed(uri) {
return this.redirectUris.indexOf(uri) !== -1;
redirectUriAllowed(redirectUri) {
const checkedUri = (() => {
if (this.applicationType === 'native' &&
redirectUri.startsWith('http:') &&
instance(provider).configuration('features.oauthNativeApps')
) {
return url.format(Object.assign(url.parse(redirectUri), {
host: null,
port: null,
}));
}
return redirectUri;
})();
return this.redirectUris.indexOf(checkedUri) !== -1;
}

@@ -229,0 +244,0 @@

@@ -66,3 +66,3 @@ {

},
"version": "1.3.0",
"version": "1.4.0",
"files": [

@@ -69,0 +69,0 @@ "lib"

@@ -27,3 +27,4 @@ # oidc-provider

The following specifications are implemented by oidc-provider.
The following specifications are implemented by oidc-provider. Note that not all features are
enabled by default, check the configuration section on how to enable them.

@@ -53,2 +54,3 @@ - [OpenID Connect Core 1.0 incorporating errata set 1][feature-core]

- [RFC7592 - OAuth 2.0 Dynamic Client Registration Management Protocol (Update and Delete)][feature-registration-management]
- [OAuth 2.0 for Native Apps BCP - draft 06][feature-oauth-native-apps]

@@ -141,1 +143,2 @@ Updates to drafts and experimental specification versions are released as MINOR library versions.

[feature-registration-management]: https://tools.ietf.org/html/rfc7592
[feature-oauth-native-apps]: https://tools.ietf.org/html/draft-ietf-oauth-native-apps-06
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc