diff --git a/Units.flitter.js b/Units.flitter.js index 2354c6b..0d06b3b 100644 --- a/Units.flitter.js +++ b/Units.flitter.js @@ -42,6 +42,7 @@ const FlitterUnits = { 'LDAPMiddleware': require('./app/unit/LDAPMiddlewareUnit'), 'LDAPController': require('./app/unit/LDAPControllerUnit'), 'LDAPRoutingUnit': require('./app/unit/LDAPRoutingUnit'), + 'OpenIDConnect' : require('./app/unit/OpenIDConnectUnit'), /* * The Core Flitter Units diff --git a/app/assets/app/dash/SideBar.component.js b/app/assets/app/dash/SideBar.component.js index ddcf7bc..f5c7c36 100644 --- a/app/assets/app/dash/SideBar.component.js +++ b/app/assets/app/dash/SideBar.component.js @@ -67,6 +67,12 @@ export default class SideBarComponent extends Component { type: 'resource', resource: 'oauth/Client', }, + { + text: 'OpenID Connect Clients', + action: 'list', + type: 'resource', + resource: 'openid/Client', + }, { text: 'SAML Service Providers', action: 'list', diff --git a/app/assets/app/resource/App.resource.js b/app/assets/app/resource/App.resource.js index 7a3022e..09e19fc 100644 --- a/app/assets/app/resource/App.resource.js +++ b/app/assets/app/resource/App.resource.js @@ -101,6 +101,16 @@ class AppResource extends CRUDBase { value: 'id', }, }, + { + name: 'Associated OpenID Connect Clients', + field: 'openid_client_ids', + type: 'select.dynamic.multiple', + options: { + resource: 'openid/Client', + display: 'client_name', + value: 'id', + }, + }, { name: 'Associated SAML Service Providers', field: 'saml_service_provider_ids', diff --git a/app/assets/app/resource/openid/Client.resource.js b/app/assets/app/resource/openid/Client.resource.js new file mode 100644 index 0000000..6ae0d05 --- /dev/null +++ b/app/assets/app/resource/openid/Client.resource.js @@ -0,0 +1,97 @@ +import CRUDBase from '../CRUDBase.js' +import { session } from '../../service/Session.service.js' + +class ClientResource extends CRUDBase { + endpoint = '/openid/clients' + required_fields = ['client_name', 'grant_types', 'redirect_uri'] + permission_base = 'v1:openid:clients' + + item = 'OpenID Connect Client' + plural = 'OpenID Connect Clients' + + listing_definition = { + display: ` + OpenID Connect clients are applications that support authentication over the OpenID Connect protocol. This allows you to add a "Sign-In with XXX" button for ${session.get('app.name')} to the application in question. To do this, the application need only comply with the OpenID standards. + `, + columns: [ + { + name: 'Client Name', + field: 'client_name', + }, + { + name: 'Redirect URI', + field: 'redirect_uri', + }, + ], + actions: [ + { + type: 'resource', + position: 'main', + action: 'insert', + text: 'Create New', + color: 'success', + }, + { + type: 'resource', + position: 'row', + action: 'update', + icon: 'fa fa-edit', + color: 'primary', + }, + { + type: 'resource', + position: 'row', + action: 'delete', + icon: 'fa fa-times', + color: 'danger', + confirm: true, + }, + ], + } + + form_definition = { + fields: [ + { + name: 'Client Name', + field: 'client_name', + placeholder: 'Awesome External App', + required: true, + type: 'text', + }, + { + name: 'Redirect URI', + field: 'redirect_uri', + placeholder: 'https://awesome.app/oauth2/callback', + required: true, + type: 'text', + }, + { + name: 'Grant Types', + field: 'grant_types', + type: 'select.multiple', + options: [ + { display: 'Refresh Token', value: 'refresh_token' }, + { display: 'Authorization Code', value: 'authorization_code' }, + ], + required: true, + }, + { + name: 'Client ID', + field: 'client_id', + type: 'text', + readonly: true, + hidden: ['insert'], + }, + { + name: 'Client Secret', + field: 'client_secret', + type: 'text', + readonly: true, + hidden: ['insert'], + }, + ], + } +} + +const openid_client = new ClientResource() +export { openid_client } diff --git a/app/classes/oidc/CoreIDAdapter.js b/app/classes/oidc/CoreIDAdapter.js new file mode 100644 index 0000000..63e0583 --- /dev/null +++ b/app/classes/oidc/CoreIDAdapter.js @@ -0,0 +1,89 @@ +const Connection = require('flitter-orm/src/services/Connection') +const { ObjectId } = require('mongodb') + +let DB + +/** + * An OpenID Connect provider adapter with some CoreID specific tweaks. + */ +class CoreIDAdapter { + constructor(name) { + this.name = 'openid_' + name + } + + async upsert(_id, payload, expiresIn) { + let expiresAt + + if (expiresIn) { + expiresAt = new Date(Date.now() + (expiresIn * 1000)) + } + + await this.coll().updateOne( + { _id }, + { $set: { payload, ...(expiresAt ? { expiresAt } : undefined) } }, + { upsert: true }, + ) + } + + async find(_id) { + if ( this.name === 'openid_Client' ) _id = ObjectId(_id) + + const result = await this.coll().find( + { _id }, + { payload: 1 }, + ).limit(1).next() + + if (!result) return undefined + return result.payload + } + + async findByUserCode(userCode) { + const result = await this.coll().find( + { 'payload.userCode': userCode }, + { payload: 1 }, + ).limit(1).next() + + if (!result) return undefined + return result.payload + } + + async findByUid(uid) { + const result = await this.coll().find( + { 'payload.uid': uid }, + { payload: 1 }, + ).limit(1).next() + + if (!result) return undefined + return result.payload + } + + async destroy(_id) { + await this.coll().deleteOne({ _id }) + } + + async revokeByGrantId(grantId) { + await this.coll().deleteMany({ 'payload.grantId': grantId }) + } + + async consume(_id) { + await this.coll().findOneAndUpdate( + { _id }, + { $set: { 'payload.consumed': Math.floor(Date.now() / 1000) } }, + ) + } + + coll(name) { + return this.constructor.coll(name || this.name) + } + + static coll(name) { + return DB.collection(name) + } + + static connect(app) { + DB = app.di().get(Connection.name) + } +} + +module.exports = CoreIDAdapter + diff --git a/app/controllers/OpenID.controller.js b/app/controllers/OpenID.controller.js new file mode 100644 index 0000000..408dd3d --- /dev/null +++ b/app/controllers/OpenID.controller.js @@ -0,0 +1,266 @@ +const Controller = require('libflitter/controller/Controller') +const is_absolute_url = require('is-absolute-url') + +class OpenIDController extends Controller { + static get services() { + return [...super.services, 'Vue', 'openid_connect', 'configs', 'models', 'output'] + } + + fail(res, message) { + return this.Vue.auth_message(res, { message, next_destination: '/dash' }) + } + + async get_clients(req, res) { + const Client = this.models.get('openid:Client') + const clients = await Client.find() + + const data = [] + for ( const client of clients ) { + data.push(await client.to_api()) + } + + return res.api(data) + } + + async get_client(req, res) { + const Client = this.models.get('openid:Client') + const client = await Client.findById(req.params.id) + + if ( !client ) + return res.status(404) + .message(req.T('api.client_not_found')) + .api() + + return res.api(await client.to_api()) + } + + async create_client(req, res) { + const required_fields = ['client_name', 'grant_types', 'redirect_uri'] + for ( const field of required_fields ) { + if ( !req.body[field] ) + return res.status(400) + .message(`${req.T('api.missing_field')} ${field}`) + .api() + } + + if ( !Array.isArray(req.body.grant_types) ) + return res.status(400) + .message(`${req.T('api.improper_field')} grant_types ${req.T('api.array')}`) + .api() + + if ( !is_absolute_url(req.body.redirect_uri) ) + return res.status(400) + .message(`${req.T('api.improper_field')} redirect_uri ${req.T('api.absolute_url')}`) + .api() + + const payload = { + client_name: req.body.client_name, + grant_types: req.body.grant_types, + redirect_uris: [req.body.redirect_uri], + } + + const Client = this.models.get('openid:Client') + const client = new Client({ payload }) + + await client.save() + return res.api(await client.to_api()) + } + + async update_client(req, res) { + const Client = this.models.get('openid:Client') + const client = await Client.findById(req.params.id) + + if ( !client ) + return res.status(404) + .message(req.T('api.client_not_found')) + .api() + + const required_fields = ['client_name', 'grant_types', 'redirect_uri'] + for ( const field of required_fields ) { + if ( !req.body[field] ) + return res.status(400) + .message(`${req.T('api.missing_field')} ${field}`) + .api() + } + + if ( !Array.isArray(req.body.grant_types) ) + return res.status(400) + .message(`${req.T('api.improper_field')} grant_types ${req.T('api.array')}`) + .api() + + if ( !is_absolute_url(req.body.redirect_uri) ) + return res.status(400) + .message(`${req.T('api.improper_field')} redirect_uri ${req.T('api.absolute_url')}`) + .api() + + client.payload.client_name = req.body.client_name + client.payload.grant_types = req.body.grant_types + client.payload.redirect_uris = [req.body.redirect_uri] + + await client.save() + return res.api() + } + + async delete_client(req, res, next) { + const Client = this.models.get('openid:Client') + const client = await Client.findById(req.params.id) + + if ( !client || !client.active ) + return res.status(404) + .message(req.T('api.client_not_found')) + .api() + + await client.delete() + return res.api() + } + + async handle_interaction(req, res) { + const { + uid, prompt, params, session, + } = await this.openid_connect.provider.interactionDetails(req, res) + + console.log({uid, prompt, params, session}) + + const name = prompt.name + if ( typeof this[name] !== 'function' ) { + return this.fail(res, 'Sorry, something has gone wrong.') + } + + return this[name](req, res, { uid, prompt, params, session }) + } + + async consent(req, res, { uid, prompt, params, session }) { + const Client = this.models.get('openid:Client') + const { details: { scopes, claims } } = prompt + const { client_id, redirect_uri } = params + + const client_raw = await Client.findById(client_id) + const client = client_raw.to_api() + const uri = new URL(redirect_uri) + + const Application = this.models.get('Application') + const Policy = this.models.get('iam:Policy') + const application = await Application.findOne({ openid_client_ids: params.client_id }) + if ( !application ) { + this.output.warning('IAM Denial!') + return this.Vue.auth_message(res, { + message: req.T('saml.no_access').replace('APP_NAME', 'this application'), + next_destination: '/dash', + }) + } else if ( !(await Policy.check_user_access(req.user, application.id)) ) { + this.output.warning('IAM Denial!') + return this.Vue.auth_message(res, { + message: req.T('saml.no_access').replace('APP_NAME', application.name), + next_destination: '/dash', + }) + } + + return res.page('public:message', { + ...this.Vue.data({ + message: `

Authorize ${application.name}?

+
+ ${req.T('auth.oauth_prompt').replace('CLIENT_NAME', application.name).replace('APP_NAME', this.configs.get('app.name'))} +


+ ${req.T('auth.will_redirect')} ${uri.host}`, + + actions: [ + { + text: req.T('common.deny'), + action: 'redirect', + next: '/dash', + }, + { + text: req.T('common.grant'), + action: 'redirect', + next: `/openid/interaction/${uid}/grant`, + }, + ], + }) + }) + } + + async login(req, res, { uid, prompt, params, session }) { + return res.redirect(`/openid/interaction/${uid}/start-session`) + } + + /** + * The user has been logged in, so set the OpenID acccount ID and redirect. + * @param req + * @param res + * @returns {Promise} + */ + async start_session(req, res) { + const { + uid, prompt, params, session, + } = await this.openid_connect.provider.interactionDetails(req, res) + + if ( prompt.name !== 'login' ) { + return this.fail(res,'Sorry, something has gone wrong.') + } + + const Application = this.models.get('Application') + const Policy = this.models.get('iam:Policy') + const application = await Application.findOne({ openid_client_ids: params.client_id }) + if ( !application ) { + this.output.warning('IAM Denial!') + return this.Vue.auth_message(res, { + message: req.T('saml.no_access').replace('APP_NAME', 'this application'), + next_destination: '/dash', + }) + } else if ( !(await Policy.check_user_access(req.user, application.id)) ) { + this.output.warning('IAM Denial!') + return this.Vue.auth_message(res, { + message: req.T('saml.no_access').replace('APP_NAME', application.name), + next_destination: '/dash', + }) + } + + const result = { + select_account: {}, + login: { + account: req.user.id, + }, + } + + await this.openid_connect.provider.interactionFinished(req, res, result, { mergeWithLastSubmission: true }) + } + + async process_grant(req, res) { + const { + uid, prompt, params, session, + } = await this.openid_connect.provider.interactionDetails(req, res) + + if ( prompt.name !== 'consent' ) { + return this.fail(res,'Sorry, something has gone wrong.') + } + + const Application = this.models.get('Application') + const Policy = this.models.get('iam:Policy') + const application = await Application.findOne({ openid_client_ids: params.client_id }) + if ( !application ) { + this.output.warning('IAM Denial!') + return this.Vue.auth_message(res, { + message: req.T('saml.no_access').replace('APP_NAME', 'this application'), + next_destination: '/dash', + }) + } else if ( !(await Policy.check_user_access(req.user, application.id)) ) { + this.output.warning('IAM Denial!') + return this.Vue.auth_message(res, { + message: req.T('saml.no_access').replace('APP_NAME', application.name), + next_destination: '/dash', + }) + } + + // TODO allow listing of scopes/claims and rejecting + const consent = { + rejectedScopes: [], + rejectedClaims: [], + replace: false, + } + + const result = { consent } + await this.openid_connect.provider.interactionFinished(req, res, result, { mergeWithLastSubmission: true }) + } +} + +module.exports = OpenIDController diff --git a/app/controllers/api/v1/App.controller.js b/app/controllers/api/v1/App.controller.js index 7a30862..0fa88ae 100644 --- a/app/controllers/api/v1/App.controller.js +++ b/app/controllers/api/v1/App.controller.js @@ -115,6 +115,28 @@ class AppController extends Controller { application.oauth_client_ids = oauth_client_ids } + // Verify OpenID client IDs + const OpenIDClient = this.models.get('openid:Client') + if ( req.body.openid_client_ids ) { + const parsed = typeof req.body.openid_client_ids === 'string' ? this.utility.infer(req.body.openid_client_ids) : req.body.openid_client_ids + const openid_client_ids = Array.isArray(parsed) ? parsed : [parsed] + for ( const id of openid_client_ids ) { + const client = await OpenIDClient.findById(id) + if ( !client ) + return res.status(400) + .message(`${req.T('api.invalid_oauth_client_id')} ${id}`) + .api() + + const other_assoc_app = await Application.findOne({ openid_client_ids: client.id }) + if ( other_assoc_app ) + return res.status(400) // TODO translate this + .message(`The OpenID Connect client ${client.name} is already associated with an existing application (${other_assoc_app.name}).`) + .api() + } + + application.openid_client_ids = openid_client_ids + } + // Verify SAML service provider IDs const ServiceProvider = this.models.get('saml:ServiceProvider') if ( req.body.saml_service_provider_ids ) { @@ -220,6 +242,28 @@ class AppController extends Controller { application.oauth_client_ids = oauth_client_ids } else application.oauth_client_ids = [] + // Verify OpenID client IDs + const OpenIDClient = this.models.get('openid:Client') + if ( req.body.openid_client_ids ) { + const parsed = typeof req.body.openid_client_ids === 'string' ? this.utility.infer(req.body.openid_client_ids) : req.body.openid_client_ids + const openid_client_ids = Array.isArray(parsed) ? parsed : [parsed] + for ( const id of openid_client_ids ) { + const client = await OpenIDClient.findById(id) + if ( !client ) + return res.status(400) + .message(`${req.T('api.invalid_oauth_client_id')} ${id}`) + .api() + + const other_assoc_app = await Application.findOne({ openid_client_ids: client.id }) + if ( other_assoc_app && other_assoc_app.id !== application.id ) + return res.status(400) // TODO translate this + .message(`The OpenID Connect client ${client.name} is already associated with an existing application (${other_assoc_app.name}).`) + .api() + } + + application.openid_client_ids = openid_client_ids + } else application.openid_client_ids = [] + // Verify SAML service provider IDs const ServiceProvider = this.models.get('saml:ServiceProvider') if ( req.body.saml_service_provider_ids ) { diff --git a/app/controllers/api/v1/Reflect.controller.js b/app/controllers/api/v1/Reflect.controller.js index bc44e6d..b396927 100644 --- a/app/controllers/api/v1/Reflect.controller.js +++ b/app/controllers/api/v1/Reflect.controller.js @@ -1,5 +1,5 @@ const { Controller } = require('libflitter') -const uuid = require('uuid/v4') +const uuid = require('uuid').v4 class ReflectController extends Controller { static get services() { diff --git a/app/models/Application.model.js b/app/models/Application.model.js index f4733e8..d91b53d 100644 --- a/app/models/Application.model.js +++ b/app/models/Application.model.js @@ -10,6 +10,7 @@ class ApplicationModel extends Model { saml_service_provider_ids: [String], ldap_client_ids: [String], oauth_client_ids: [String], + openid_client_ids: [String], } } @@ -22,6 +23,7 @@ class ApplicationModel extends Model { saml_service_provider_ids: this.saml_service_provider_ids, ldap_client_ids: this.ldap_client_ids, oauth_client_ids: this.oauth_client_ids, + openid_client_ids: this.openid_client_ids, } } } diff --git a/app/models/auth/AppPassword.model.js b/app/models/auth/AppPassword.model.js index fdea2dd..33c2e44 100644 --- a/app/models/auth/AppPassword.model.js +++ b/app/models/auth/AppPassword.model.js @@ -1,6 +1,6 @@ const { Model } = require('flitter-orm') const bcrypt = require('bcrypt') -const uuid = require('uuid/v4') +const uuid = require('uuid').v4 class AppPasswordModel extends Model { static get schema() { diff --git a/app/models/auth/MFAToken.model.js b/app/models/auth/MFAToken.model.js index 7527afc..e607536 100644 --- a/app/models/auth/MFAToken.model.js +++ b/app/models/auth/MFAToken.model.js @@ -1,7 +1,7 @@ const { Model } = require('flitter-orm') const speakeasy = require('speakeasy') const MFARecoveryCode = require('./MFARecoveryCode.model') -const uuid = require('uuid/v4') +const uuid = require('uuid').v4 class MFATokenModel extends Model { static get services() { diff --git a/app/models/auth/User.model.js b/app/models/auth/User.model.js index bb13226..8562787 100644 --- a/app/models/auth/User.model.js +++ b/app/models/auth/User.model.js @@ -6,7 +6,7 @@ const MFAToken = require('./MFAToken.model') const PasswordReset = require('./PasswordReset.model') const AppAuthorization = require('./AppAuthorization.model') const AppPassword = require('./AppPassword.model') -const uuid = require('uuid/v4') +const uuid = require('uuid').v4 /* * Auth user model. This inherits fields and methods from the default @@ -197,6 +197,37 @@ class User extends AuthUser { get dn() { return LDAP.parseDN(`uid=${this.uid},${this.ldap_server.auth_dn().format(this.configs.get('ldap:server.format'))}`) } + + // The following are used by OpenID connect + + async claims(use, scope) { + return { + sub: this.id, + email: this.email, + email_verified: true, // TODO + family_name: this.last_name, + given_name: this.first_name, + locale: 'en_US', // TODO + name: `${this.first_name} ${this.last_name}`, + preferred_username: this.uid, + username: this.uid, + } + } + + static async findByLogin(login) { + return this.findOne({ + active: true, + uid: login, + }) + } + + static async findAccount(ctx, id, token) { + return this.findById(id) + } + + get accountId() { + return this.id + } } module.exports = exports = User diff --git a/app/models/oauth/Client.model.js b/app/models/oauth/Client.model.js index df4b48b..655152a 100644 --- a/app/models/oauth/Client.model.js +++ b/app/models/oauth/Client.model.js @@ -1,5 +1,5 @@ const { Model } = require('flitter-orm') -const uuid = require('uuid/v4') +const uuid = require('uuid').v4 /* * OAuth2 Client Model diff --git a/app/models/openid/Client.model.js b/app/models/openid/Client.model.js new file mode 100644 index 0000000..0de2bde --- /dev/null +++ b/app/models/openid/Client.model.js @@ -0,0 +1,44 @@ +const { Model } = require('flitter-orm') +const uuid = require('uuid').v4 + +class ClientModel extends Model { + static get services() { + return [...super.services, 'models'] + } + + static get schema() { + return { + payload: { + client_id: { type: String, default: uuid }, + client_secret: { type: String, default: uuid }, + client_name: String, + grant_types: [String], + redirect_uris: [String], + }, + } + } + + to_api() { + const vals = ['client_id', 'client_secret', 'client_name', 'grant_types'] + const val = {} + for ( const item of vals ) { + val[item] = this.payload[item] + } + val.redirect_uri = this.payload?.redirect_uris?.[0] + val.id = this.id + return val + } + + async save() { + await super.save() + this.payload.client_id = this.id + return super.save() + } + + async application() { + const Application = this.models.get('Application') + return Application.findOne({ active: true, oauth_client_ids: this.id }) + } +} + +module.exports = exports = ClientModel diff --git a/app/models/saml/SessionParticipant.model.js b/app/models/saml/SessionParticipant.model.js index 812fd5a..c867b29 100644 --- a/app/models/saml/SessionParticipant.model.js +++ b/app/models/saml/SessionParticipant.model.js @@ -1,5 +1,5 @@ const { Model } = require('flitter-orm') -const uuid = require('uuid/v4') +const uuid = require('uuid').v4 class SessionParticipantModel extends Model { static get schema() { diff --git a/app/routing/middleware/auth/TrustTokenUtility.middleware.js b/app/routing/middleware/auth/TrustTokenUtility.middleware.js index 1f1edd3..fb0d457 100644 --- a/app/routing/middleware/auth/TrustTokenUtility.middleware.js +++ b/app/routing/middleware/auth/TrustTokenUtility.middleware.js @@ -1,6 +1,6 @@ const { Middleware } = require('libflitter') const moment = require('moment') -const uuid = require('uuid/v4') +const uuid = require('uuid').v4 class TrustManager { assume_trust = false diff --git a/app/routing/routers/openid.routes.js b/app/routing/routers/openid.routes.js new file mode 100644 index 0000000..0e97c7c --- /dev/null +++ b/app/routing/routers/openid.routes.js @@ -0,0 +1,52 @@ +const openid = { + + prefix: '/openid', + + middleware: [ + + ], + + get: { + '/interaction/:uid': [ + 'controller::OpenID.handle_interaction', + ], + '/interaction/:uid/start-session': [ + 'middleware::auth:UserOnly', 'controller::OpenID.start_session', + ], + '/interaction/:uid/grant': [ + 'middleware::auth:UserOnly', 'controller::OpenID.process_grant', + ], + + '/clients': [ + ['middleware::api:Permission', { check: 'v1:openid:clients:list' }], + 'controller::OpenID.get_clients', + ], + '/clients/:id': [ + ['middleware::api:Permission', { check: 'v1:openid:clients:get' }], + 'controller::OpenID.get_client', + ], + }, + + post: { + '/clients': [ + ['middleware::api:Permission', { check: 'v1:openid:clients:create' }], + 'controller::OpenID.create_client', + ], + }, + + patch: { + '/clients/:id': [ + ['middleware::api:Permission', { check: 'v1:openid:clients:update' }], + 'controller::OpenID.update_client', + ], + }, + + delete: { + '/clients/:id': [ + ['middleware::api:Permission', { check: 'v1:openid:clients:delete' }], + 'controller::OpenID.delete_client', + ], + }, +} + +module.exports = exports = openid diff --git a/app/unit/OpenIDConnectUnit.js b/app/unit/OpenIDConnectUnit.js new file mode 100644 index 0000000..bbea515 --- /dev/null +++ b/app/unit/OpenIDConnectUnit.js @@ -0,0 +1,98 @@ +const Unit = require('libflitter/Unit') +const { Provider, interactionPolicy: { Prompt, base: policy } } = require('oidc-provider') +const uuid = require('uuid').v4 +const CoreIDAdapter = require('../classes/oidc/CoreIDAdapter') +const RequestLocalizationHelper = require('flitter-i18n/src/RequestLocalizationHelper') +const ResponseSystemMiddleware = require('libflitter/routing/ResponseSystemMiddleware') + +class OpenIDConnectUnit extends Unit { + static get name() { + return 'openid_connect' + } + + static get services() { + return [...super.services, 'output', 'configs', 'models'] + } + + async go(app) { + this.Vue = this.app.di().get('Vue') + const issuer = this.configs.get('app.url') + const configuration = this.configs.get('oidc.provider') + const interactions = policy() + const User = this.models.get('auth:User') + + CoreIDAdapter.connect(app) + + this.provider = new Provider(issuer, { + adapter: CoreIDAdapter, + clients: [], + interactions: { + interactions, + url: (ctx, interaction) => `/openid/interaction/${ctx.oidc.uid}`, + }, + cookies: { + long: { signed: true, maxAge: 24 * 60 * 60 * 1000 }, // 1 day, ms + short: { signed: true }, + keys: [this.configs.get('server.session.secret') || uuid()] + }, + claims: { + email: ['email'], + profile: [ + 'first_name', 'last_name', 'picture', 'tagline', 'username', + ], + }, + features: { + devInteractions: { enabled: false }, + deviceFlow: { enabled: true }, + introspection: { enabled: true }, + revocation: { enabled: true }, + }, + ttl: { + AccessToken: 60 * 60, // 1 hour in seconds + AuthorizationCode: 10 * 60, // 10 minutes in seconds + IdToken: 60 * 60, // 1 hour in seconds + DeviceCode: 10 * 60, // 10 minutes in seconds + RefreshToken: 24 * 60 * 60, // 1 day in seconds + }, + findAccount: (...args) => User.findAccount(...args), + ...configuration, + }) + + app.express.use('/oidc', this.wrap(this.provider.callback)) + } + + wrap(callback) { + return async (req, res, next) => { + const client_id = req?.query?.client_id + + // Provide some basic Flitter niceties in the request + req.i18n = new RequestLocalizationHelper(req, res) + new ResponseSystemMiddleware(this.app, res, req) + + // If we got a client ID, make sure the current user has access to it + if ( req?.session?.auth?.user_id && client_id ) { + const User = this.models.get('auth:User') + const Application = this.models.get('Application') + const Policy = this.models.get('iam:Policy') + + const user = await User.findById(req.session.auth.user_id) + const application = await Application.findOne({ openid_client_ids: client_id }) + if ( !application ) { + return this.Vue.auth_message(res, { + message: req.T('saml.no_access').replace('APP_NAME', 'this application'), + next_destination: '/dash', + }) + } else if ( !(await Policy.check_user_access(user, application.id)) ) { + return this.Vue.auth_message(res, { + message: req.T('saml.no_access').replace('APP_NAME', application.name), + next_destination: '/dash', + }) + } + } + + return callback(req, res, next) + } + } +} + +module.exports = exports = OpenIDConnectUnit diff --git a/config/oidc.config.js b/config/oidc.config.js new file mode 100644 index 0000000..26532e3 --- /dev/null +++ b/config/oidc.config.js @@ -0,0 +1,14 @@ +const oidc_config = { + provider: { + clients: [ + { + client_id: 'foo', + client_secret: 'foo', + redirect_uris: ['http://localhost:3000/user/oauth2/CoreID/callback'], + grant_types: ['refresh_token', 'authorization_code'], + } + ], + } +} + +module.exports = exports = oidc_config diff --git a/package.json b/package.json index 6fb01ca..4af0123 100644 --- a/package.json +++ b/package.json @@ -36,9 +36,11 @@ "moment": "^2.24.0", "mongodb": "^3.5.9", "nodemailer": "^6.4.6", + "oidc-provider": "^6.29.0", "qrcode": "^1.4.4", "samlp": "^3.4.1", "speakeasy": "^2.0.0", + "uuid": "^8.3.0", "zxcvbn": "^4.4.2" } } diff --git a/yarn.lock b/yarn.lock index 5e17b8f..a7fd993 100644 --- a/yarn.lock +++ b/yarn.lock @@ -197,6 +197,23 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== +"@koa/cors@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@koa/cors/-/cors-3.1.0.tgz#618bb073438cfdbd3ebd0e648a76e33b84f3a3b2" + integrity sha512-7ulRC1da/rBa6kj6P4g2aJfnET3z8Uf3SWu60cjbtxTA5g8lxRdX/Bd2P92EagGwwAhANeNw8T8if99rJliR6Q== + dependencies: + vary "^1.1.2" + +"@panva/asn1.js@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@panva/asn1.js/-/asn1.js-1.0.0.tgz#dd55ae7b8129e02049f009408b97c61ccf9032f6" + integrity sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw== + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + "@sinonjs/commons@^1", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.7.2": version "1.7.2" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.2.tgz#505f55c74e0272b43f6c52d81946bed7058fc0e2" @@ -250,6 +267,20 @@ resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@types/accepts@*": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.5.tgz#c34bec115cfc746e04fe5a059df4ce7e7b391575" + integrity sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ== + dependencies: + "@types/node" "*" + "@types/babel-types@*", "@types/babel-types@^7.0.0": version "7.0.6" resolved "https://registry.yarnpkg.com/@types/babel-types/-/babel-types-7.0.6.tgz#a7cfaaeee96e90c4c54da0e580aaff3f4cffacac" @@ -262,11 +293,65 @@ dependencies: "@types/babel-types" "*" +"@types/body-parser@*": + version "1.19.0" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" + integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== + dependencies: + "@types/connect" "*" + "@types/node" "*" + "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== +"@types/connect@*": + version "3.4.33" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.33.tgz#31610c901eca573b8713c3330abc6e6b9f588546" + integrity sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A== + dependencies: + "@types/node" "*" + +"@types/content-disposition@*": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@types/content-disposition/-/content-disposition-0.5.3.tgz#0aa116701955c2faa0717fc69cd1596095e49d96" + integrity sha512-P1bffQfhD3O4LW0ioENXUhZ9OIa0Zn+P7M+pWgkCKaT53wVLSq0mrKksCID/FGHpFhRSxRGhgrQmfhRuzwtKdg== + +"@types/cookies@*": + version "0.7.4" + resolved "https://registry.yarnpkg.com/@types/cookies/-/cookies-0.7.4.tgz#26dedf791701abc0e36b5b79a5722f40e455f87b" + integrity sha512-oTGtMzZZAVuEjTwCjIh8T8FrC8n/uwy+PG0yTvQcdZ7etoel7C7/3MSd7qrukENTgQtotG7gvBlBojuVs7X5rw== + dependencies: + "@types/connect" "*" + "@types/express" "*" + "@types/keygrip" "*" + "@types/node" "*" + +"@types/express-serve-static-core@*": + version "4.17.9" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.9.tgz#2d7b34dcfd25ec663c25c85d76608f8b249667f1" + integrity sha512-DG0BYg6yO+ePW+XoDENYz8zhNGC3jDDEpComMYn7WJc4mY1Us8Rw9ax2YhJXxpyk2SF47PQAoQ0YyVT1a0bEkA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*": + version "4.17.7" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.7.tgz#42045be6475636d9801369cd4418ef65cdb0dd59" + integrity sha512-dCOT5lcmV/uC2J9k0rPafATeeyz+99xTt54ReX11/LObZgfzJqZNcW27zGhYyX+9iSEGXGt5qLPwRSvBZcLvtQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "*" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/http-assert@*": + version "1.5.1" + resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.1.tgz#d775e93630c2469c2f980fc27e3143240335db3b" + integrity sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ== + "@types/ioredis@^4.0.13": version "4.16.2" resolved "https://registry.yarnpkg.com/@types/ioredis/-/ioredis-4.16.2.tgz#192c487a003463a6881dbc29faaa168902ef851a" @@ -274,17 +359,65 @@ dependencies: "@types/node" "*" +"@types/keygrip@*": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72" + integrity sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw== + +"@types/koa-compose@*": + version "3.2.5" + resolved "https://registry.yarnpkg.com/@types/koa-compose/-/koa-compose-3.2.5.tgz#85eb2e80ac50be95f37ccf8c407c09bbe3468e9d" + integrity sha512-B8nG/OoE1ORZqCkBVsup/AKcvjdgoHnfi4pZMn5UwAPCbhk/96xyv284eBYW8JlQbQ7zDmnpFr68I/40mFoIBQ== + dependencies: + "@types/koa" "*" + +"@types/koa@*", "@types/koa@^2.11.3": + version "2.11.3" + resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.11.3.tgz#540ece376581b12beadf9a417dd1731bc31c16ce" + integrity sha512-ABxVkrNWa4O/Jp24EYI/hRNqEVRlhB9g09p48neQp4m3xL1TJtdWk2NyNQSMCU45ejeELMQZBYyfstyVvO2H3Q== + dependencies: + "@types/accepts" "*" + "@types/content-disposition" "*" + "@types/cookies" "*" + "@types/http-assert" "*" + "@types/keygrip" "*" + "@types/koa-compose" "*" + "@types/node" "*" + +"@types/mime@*": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.3.tgz#c893b73721db73699943bfc3653b1deb7faa4a3a" + integrity sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q== + "@types/node@*": version "14.0.5" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.5.tgz#3d03acd3b3414cf67faf999aed11682ed121f22b" integrity sha512-90hiq6/VqtQgX8Sp0EzeIsv3r+ellbGj4URKj5j30tLlZvRUpnAe9YbYnjl3pJM93GyXU0tghHhvXHq+5rnCKA== +"@types/qs@*": + version "6.9.4" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.4.tgz#a59e851c1ba16c0513ea123830dd639a0a15cb6a" + integrity sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ== + +"@types/range-parser@*": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" + integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== + +"@types/serve-static@*": + version "1.13.5" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.5.tgz#3d25d941a18415d3ab092def846e135a08bbcf53" + integrity sha512-6M64P58N+OXjU432WoLLBQxbA0LRGBCRm7aAGQJ+SMC1IMl0dgRVi9EFfoDcS2a7Xogygk/eGN94CfwU9UF7UQ== + dependencies: + "@types/express-serve-static-core" "*" + "@types/mime" "*" + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -accepts@^1.3.7: +accepts@^1.3.5, accepts@^1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== @@ -406,6 +539,11 @@ ansi-styles@^4.0.0: "@types/color-name" "^1.1.1" color-convert "^2.0.1" +any-promise@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + anymatch@~3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" @@ -536,6 +674,11 @@ ast-types@0.9.6: resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9" integrity sha1-ECyenpAF0+fjgpvwxPok7oYu6bk= +async@0.9.x: + version "0.9.2" + resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" + integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= + async@^2.1.5: version "2.6.3" resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" @@ -944,6 +1087,27 @@ bytes@3.1.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== +cache-content-type@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-content-type/-/cache-content-type-1.0.1.tgz#035cde2b08ee2129f4a8315ea8f00a00dba1453c" + integrity sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA== + dependencies: + mime-types "^2.1.18" + ylru "^1.2.0" + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + caching-transform@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-4.0.0.tgz#00d297a4206d71e2163c39eaffa8157ac0651f0f" @@ -1080,6 +1244,13 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" @@ -1095,6 +1266,11 @@ cluster-key-slot@^1.1.0: resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz#30474b2a981fb12172695833052bc0d01336d10d" integrity sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw== +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" @@ -1219,6 +1395,13 @@ content-disposition@0.5.2: resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= +content-disposition@~0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + content-type@^1.0.4, content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" @@ -1253,6 +1436,14 @@ cookie@0.3.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= +cookies@~0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.8.0.tgz#1293ce4b391740a8406e3c9870e828c4b54f3f90" + integrity sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow== + dependencies: + depd "~2.0.0" + keygrip "~1.1.0" + core-js@^1.0.0: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" @@ -1313,7 +1504,7 @@ debug@3.2.6: dependencies: ms "^2.1.1" -debug@=3.1.0: +debug@=3.1.0, debug@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== @@ -1332,6 +1523,13 @@ decamelize@^1.0.0, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + deep-eql@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" @@ -1339,6 +1537,11 @@ deep-eql@^3.0.1: dependencies: type-detect "^4.0.0" +deep-equal@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= + deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -1351,6 +1554,11 @@ default-require-extensions@^3.0.0: dependencies: strip-bom "^4.0.0" +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -1406,7 +1614,7 @@ denque@^1.1.0, denque@^1.4.1: resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf" integrity sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ== -depd@~1.1.2: +depd@^1.1.2, depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= @@ -1416,7 +1624,7 @@ depd@~2.0.0: resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -destroy@~1.0.4: +destroy@^1.0.4, destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= @@ -1494,6 +1702,11 @@ dtrace-provider@~0.8: dependencies: nan "^2.14.0" +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -1527,6 +1740,13 @@ ejs@^2.5.6: resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== +ejs@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.3.tgz#514d967a8894084d18d3d47bd169a1c0560f093d" + integrity sha512-wmtrUGyfSC23GC/B1SMv2ogAUgbQEtDmTIhfqielrG5ExIM9TP4UoYdi90jLF1aTcsWCJNEO0UrgKzP0y3nTSg== + dependencies: + jake "^10.6.1" + email-validator@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/email-validator/-/email-validator-2.0.4.tgz#b8dfaa5d0dae28f1b03c95881d904d4e40bfe7ed" @@ -1542,11 +1762,18 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -encodeurl@~1.0.2: +encodeurl@^1.0.2, encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + errno@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" @@ -1597,7 +1824,7 @@ es6-promisify@^6.0.1: resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-6.0.1.tgz#6edaa45f3bd570ffe08febce66f7116be4b1cdb6" integrity sha512-J3ZkwbEnnO+fGAKrjVpeUAnZshAdfZvbhQpqfIH9kSAspReRC4nJnu8ewm55b4y9ElyeuhCTzJD0XiH8Tsbhlw== -escape-html@~1.0.3: +escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= @@ -1760,6 +1987,13 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== +filelist@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.1.tgz#f10d1a3ae86c1694808e8f20906f43d4c9132dbb" + integrity sha512-8zSK6Nu0DQIC08mUC46sWGXi+q3GGpKydAG36k+JDba6VRpkevvOWUW5a/PhShij4+vHT9M+ghgG7eM+a9JDUQ== + dependencies: + minimatch "^3.0.4" + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -1987,7 +2221,7 @@ forwarded@~0.1.2: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= -fresh@0.5.2: +fresh@0.5.2, fresh@~0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= @@ -2074,6 +2308,20 @@ get-stdin@^4.0.1: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= +get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -2155,6 +2403,23 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" +got@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + graceful-fs@^4.1.15: version "4.2.3" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" @@ -2250,6 +2515,19 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +http-assert@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.4.1.tgz#c5f725d677aa7e873ef736199b89686cceb37878" + integrity sha512-rdw7q6GTlibqVVbXr0CKelfV5iY8G2HqEUkhSk297BMbSpSL8crXC+9rjKoMcZZEsksX30le6f/4ul4E28gegw== + dependencies: + deep-equal "~1.0.1" + http-errors "~1.7.2" + +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: version "1.6.3" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" @@ -2260,7 +2538,7 @@ http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" -http-errors@1.7.3, http-errors@^1.7.3: +http-errors@1.7.3, http-errors@^1.7.3, http-errors@~1.7.2: version "1.7.3" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== @@ -2271,6 +2549,17 @@ http-errors@1.7.3, http-errors@^1.7.3: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" +http-errors@^1.6.3: + version "1.8.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.0.tgz#75d1bbe497e1044f51e4ee9e704a62f28d336507" + integrity sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -2451,6 +2740,11 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-generator-function@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" + integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw== + is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" @@ -2631,6 +2925,23 @@ iterall@^1.2.2: resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.2.2.tgz#92d70deb8028e0c39ff3164fdbf4d8b088130cd7" integrity sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA== +jake@^10.6.1: + version "10.8.2" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.2.tgz#ebc9de8558160a66d82d0eadc6a2e58fbc500a7b" + integrity sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A== + dependencies: + async "0.9.x" + chalk "^2.4.2" + filelist "^1.0.1" + minimatch "^3.0.4" + +jose@^1.27.2: + version "1.28.0" + resolved "https://registry.yarnpkg.com/jose/-/jose-1.28.0.tgz#0803f8c71f43cd293a9d931c555c30531f5ca5dc" + integrity sha512-JmfDRzt/HSj8ipd9TsDtEHoLUnLYavG+7e8F6s1mx2jfVSfXOTaFQsJUydbjJpTnTDHP1+yKL9Ke7ktS/a0Eiw== + dependencies: + "@panva/asn1.js" "^1.0.0" + js-beautify@^1.10.2: version "1.10.2" resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.10.2.tgz#88c9099cd6559402b124cfab18754936f8a7b178" @@ -2675,11 +2986,21 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +jsesc@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.1.tgz#0e10205322721c22716cd5bbdbf80e71fb9c8344" + integrity sha512-w+MMxnByppM4jwskitZotEtvtO3a2C7WOz31NxJToGisHuysCAQQU7umb/pA/6soPFe8LGjXFEFbuPuLEPm7Ag== + jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -2737,6 +3058,20 @@ just-extend@^4.0.2: resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.1.0.tgz#7278a4027d889601640ee0ce0e5a00b992467da4" integrity sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA== +keygrip@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" + integrity sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ== + dependencies: + tsscmp "1.0.6" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + kind-of@^3.0.2: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -2751,6 +3086,55 @@ klaw@^1.0.0: optionalDependencies: graceful-fs "^4.1.9" +koa-compose@^3.0.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-3.2.1.tgz#a85ccb40b7d986d8e5a345b3a1ace8eabcf54de7" + integrity sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec= + dependencies: + any-promise "^1.1.0" + +koa-compose@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877" + integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw== + +koa-convert@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-1.2.0.tgz#da40875df49de0539098d1700b50820cebcd21d0" + integrity sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA= + dependencies: + co "^4.6.0" + koa-compose "^3.0.0" + +koa@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/koa/-/koa-2.13.0.tgz#25217e05efd3358a7e5ddec00f0a380c9b71b501" + integrity sha512-i/XJVOfPw7npbMv67+bOeXr3gPqOAw6uh5wFyNs3QvJ47tUx3M3V9rIE0//WytY42MKz4l/MXKyGkQ2LQTfLUQ== + dependencies: + accepts "^1.3.5" + cache-content-type "^1.0.0" + content-disposition "~0.5.2" + content-type "^1.0.4" + cookies "~0.8.0" + debug "~3.1.0" + delegates "^1.0.0" + depd "^1.1.2" + destroy "^1.0.4" + encodeurl "^1.0.2" + escape-html "^1.0.3" + fresh "~0.5.2" + http-assert "^1.3.0" + http-errors "^1.6.3" + is-generator-function "^1.0.7" + koa-compose "^4.1.0" + koa-convert "^1.2.0" + on-finished "^2.3.0" + only "~0.0.2" + parseurl "^1.3.2" + statuses "^1.5.0" + type-is "^1.6.16" + vary "^1.1.2" + lazy-cache@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" @@ -2956,6 +3340,16 @@ longest@^1.0.1: resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + lru-cache@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -2964,6 +3358,13 @@ lru-cache@^4.1.5: pseudomap "^1.0.2" yallist "^2.1.2" +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + make-dir@^3.0.0, make-dir@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.0.2.tgz#04a1acbf22221e1d6ef43559f43e05a90dbb4392" @@ -3001,6 +3402,11 @@ mime-db@1.43.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== +mime-db@1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== + mime-db@~1.38.0: version "1.38.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad" @@ -3013,6 +3419,13 @@ mime-types@^2.1.12, mime-types@~2.1.19: dependencies: mime-db "1.43.0" +mime-types@^2.1.18: + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + dependencies: + mime-db "1.44.0" + mime-types@~2.1.18: version "2.1.22" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.22.tgz#fe6b355a190926ab7698c9a0556a11199b2199bd" @@ -3037,6 +3450,11 @@ mime@^1.4.1: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + "minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -3241,6 +3659,11 @@ nan@~1.5.1: resolved "https://registry.yarnpkg.com/nan/-/nan-1.5.3.tgz#4cd0ecc133b7b0700a492a646add427ae8a318eb" integrity sha1-TNDswTO3sHAKSSpkat1CeuijGOs= +nanoid@^3.1.10: + version "3.1.12" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.12.tgz#6f7736c62e8d39421601e4a0c77623a97ea69654" + integrity sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A== + ncp@^2.0.0, ncp@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" @@ -3377,6 +3800,11 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +normalize-url@^4.1.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" + integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== + npm-bundled@^1.0.1: version "1.0.6" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" @@ -3452,6 +3880,11 @@ object-hash@^2.0.1: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.0.1.tgz#cef18a0c940cc60aa27965ecf49b782cbf101d96" integrity sha512-HgcGMooY4JC2PBt9sdUdJ6PMzpin+YtY3r/7wg0uTifP+HJWW8rammseSEHuyt0UeShI183UGssCJqm1bJR7QA== +object-hash@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.0.3.tgz#d12db044e03cd2ca3d77c0570d87225b02e1e6ea" + integrity sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg== + object-inspect@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" @@ -3480,7 +3913,32 @@ object.getownpropertydescriptors@^2.0.3: define-properties "^1.1.3" es-abstract "^1.17.0-next.1" -on-finished@~2.3.0: +oidc-provider@^6.29.0: + version "6.29.0" + resolved "https://registry.yarnpkg.com/oidc-provider/-/oidc-provider-6.29.0.tgz#a233c6af35a0c79b47afddb4f6dd84c4428e35ce" + integrity sha512-NOEZMW2twrQ18axutMbYJHNuQkoOPrOewx8uHRSF3A4TP7lSNppFHTvYPZ09ER2NeKLssnFiZJ9dTCpKfVUKzQ== + dependencies: + "@koa/cors" "^3.1.0" + "@types/koa" "^2.11.3" + debug "^4.1.1" + ejs "^3.1.3" + got "^9.6.0" + jose "^1.27.2" + jsesc "^3.0.1" + koa "^2.13.0" + koa-compose "^4.1.0" + lru-cache "^6.0.0" + nanoid "^3.1.10" + object-hash "^2.0.3" + oidc-token-hash "^5.0.0" + raw-body "^2.4.1" + +oidc-token-hash@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.0.tgz#acdfb1f4310f58e64d5d74a4e8671a426986e888" + integrity sha512-8Yr4CZSv+Tn8ZkN3iN2i2w2G92mUKClp4z7EGUfdsERiYSbj7P4i/NHm72ft+aUdsiFx9UdIPSTwbyzQ6C4URg== + +on-finished@^2.3.0, on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= @@ -3499,13 +3957,18 @@ once@1.3.1: dependencies: wrappy "1" -once@^1.3.0, once@^1.4.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" +only@~0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" + integrity sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q= + "optimist@>=0.3.5 <1": version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" @@ -3548,6 +4011,11 @@ output-file-sync@^1.1.0: mkdirp "^0.5.1" object-assign "^4.1.0" +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -3596,6 +4064,11 @@ package-hash@^4.0.0: lodash.flattendeep "^4.4.0" release-zalgo "^1.0.0" +parseurl@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + parseurl@~1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" @@ -3707,6 +4180,11 @@ precond@0.2: resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" integrity sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw= +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + private@^0.1.6, private@~0.1.5: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" @@ -3864,6 +4342,14 @@ pug@^2.0.3: pug-runtime "^2.0.4" pug-strip-comments "^1.0.3" +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" @@ -4179,6 +4665,13 @@ resolve@^1.3.2: dependencies: path-parse "^1.0.6" +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + right-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" @@ -4337,6 +4830,11 @@ setprototypeof@1.1.1: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -4505,7 +5003,7 @@ status-codes@^0.0.1: resolved "https://registry.yarnpkg.com/status-codes/-/status-codes-0.0.1.tgz#2245d642c2e2f004ff9c9256cb4da6a8e378989a" integrity sha1-IkXWQsLi8AT/nJJWy02mqON4mJo= -"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2": +"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= @@ -4717,6 +5215,11 @@ to-fast-properties@^2.0.0: resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -4769,6 +5272,11 @@ tslib@^1.10.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== +tsscmp@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" + integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA== + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -4791,6 +5299,14 @@ type-fest@^0.8.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-is@^1.6.16: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + type-is@~1.6.16: version "1.6.16" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" @@ -4850,6 +5366,13 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + user-home@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" @@ -4880,6 +5403,11 @@ uuid@^3.3.3, uuid@^3.4.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +uuid@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea" + integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ== + uuid@~1.4.1: version "1.4.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-1.4.2.tgz#453019f686966a6df83cdc5244e7c990ecc332fc" @@ -4895,7 +5423,7 @@ validator@^10.11.0: resolved "https://registry.yarnpkg.com/validator/-/validator-10.11.0.tgz#003108ea6e9a9874d31ccc9e5006856ccd76b228" integrity sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw== -vary@~1.1.2: +vary@^1.1.2, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= @@ -5107,6 +5635,11 @@ yallist@^3.0.0, yallist@^3.0.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + yargs-parser@13.1.2, yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" @@ -5187,6 +5720,11 @@ yargs@~3.27.0: window-size "^0.1.2" y18n "^3.2.0" +ylru@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.2.1.tgz#f576b63341547989c1de7ba288760923b27fe84f" + integrity sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ== + zxcvbn@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/zxcvbn/-/zxcvbn-4.4.2.tgz#28ec17cf09743edcab056ddd8b1b06262cc73c30"