diff --git a/server/db/migrations/3.0.0.mjs b/server/db/migrations/3.0.0.mjs
index bd2ad60135..acef92f524 100644
--- a/server/db/migrations/3.0.0.mjs
+++ b/server/db/migrations/3.0.0.mjs
@@ -344,6 +344,7 @@ export async function up (knex) {
table.string('name').notNullable()
table.jsonb('auth').notNullable().defaultTo('{}')
table.jsonb('meta').notNullable().defaultTo('{}')
+ table.jsonb('passkeys').notNullable().defaultTo('{}')
table.jsonb('prefs').notNullable().defaultTo('{}')
table.boolean('hasAvatar').notNullable().defaultTo(false)
table.boolean('isSystem').notNullable().defaultTo(false)
diff --git a/server/graph/resolvers/authentication.mjs b/server/graph/resolvers/authentication.mjs
index 5d3c73f958..cf419b3910 100644
--- a/server/graph/resolvers/authentication.mjs
+++ b/server/graph/resolvers/authentication.mjs
@@ -3,6 +3,8 @@ import { generateError, generateSuccess } from '../../helpers/graph.mjs'
import jwt from 'jsonwebtoken'
import ms from 'ms'
import { DateTime } from 'luxon'
+import { v4 as uuid } from 'uuid'
+import { generateRegistrationOptions, verifyRegistrationResponse } from '@simplewebauthn/server'
export default {
Query: {
@@ -122,6 +124,52 @@ export default {
return generateError(err)
}
},
+ /**
+ * Setup TFA
+ */
+ async setupTFA (obj, args, context) {
+ try {
+ const userId = context.req.user?.id
+ if (!userId) {
+ throw new Error('ERR_USER_NOT_AUTHENTICATED')
+ }
+
+ const usr = await WIKI.db.users.query().findById(userId)
+ if (!usr) {
+ throw new Error('ERR_INVALID_USER')
+ }
+
+ const str = WIKI.auth.strategies[args.strategyId]
+ if (!str) {
+ throw new Error('ERR_INVALID_STRATEGY')
+ }
+
+ if (!usr.auth[args.strategyId]) {
+ throw new Error('ERR_INVALID_STRATEGY')
+ }
+
+ if (usr.auth[args.strategyId].tfaIsActive) {
+ throw new Error('ERR_TFA_ALREADY_ACTIVE')
+ }
+
+ const tfaQRImage = await usr.generateTFA(args.strategyId, args.siteId)
+ const tfaToken = await WIKI.db.userKeys.generateToken({
+ kind: 'tfaSetup',
+ userId: usr.id,
+ meta: {
+ strategyId: args.strategyId
+ }
+ })
+
+ return {
+ operation: generateSuccess('TFA setup started'),
+ continuationToken: tfaToken,
+ tfaQRImage
+ }
+ } catch (err) {
+ return generateError(err)
+ }
+ },
/**
* Deactivate 2FA
*/
@@ -164,6 +212,158 @@ export default {
return generateError(err)
}
},
+ /**
+ * Setup Passkey
+ */
+ async setupPasskey (obj, args, context) {
+ try {
+ const userId = context.req.user?.id
+ if (!userId) {
+ throw new Error('ERR_USER_NOT_AUTHENTICATED')
+ }
+
+ const usr = await WIKI.db.users.query().findById(userId)
+ if (!usr) {
+ throw new Error('ERR_INVALID_USER')
+ }
+
+ const site = WIKI.sites[args.siteId]
+ if (!site) {
+ throw new Error('ERR_INVALID_SITE')
+ } else if (site.hostname === '*') {
+ WIKI.logger.warn('Cannot use passkeys with a wildcard site hostname. Enter a valid hostname under the Administration Area > General.')
+ throw new Error('ERR_PK_HOSTNAME_MISSING')
+ }
+
+ const options = await generateRegistrationOptions({
+ rpName: site.config.title,
+ rpId: site.hostname,
+ userID: usr.id,
+ userName: usr.email,
+ userDisplayName: usr.name,
+ attestationType: 'none',
+ authenticatorSelection: {
+ residentKey: 'required',
+ userVerification: 'preferred'
+ },
+ excludeCredentials: usr.passkeys.authenticators?.map(authenticator => ({
+ id: new Uint8Array(authenticator.credentialID),
+ type: 'public-key',
+ transports: authenticator.transports
+ })) ?? []
+ })
+
+ usr.passkeys.reg = {
+ challenge: options.challenge,
+ rpId: site.hostname,
+ siteId: site.id
+ }
+
+ await usr.$query().patch({
+ passkeys: usr.passkeys
+ })
+
+ return {
+ operation: generateSuccess('Passkey registration options generated successfully.'),
+ registrationOptions: options
+ }
+ } catch (err) {
+ return generateError(err)
+ }
+ },
+ /**
+ * Finalize Passkey Registration
+ */
+ async finalizePasskey (obj, args, context) {
+ try {
+ const userId = context.req.user?.id
+ if (!userId) {
+ throw new Error('ERR_USER_NOT_AUTHENTICATED')
+ }
+
+ const usr = await WIKI.db.users.query().findById(userId)
+ if (!usr) {
+ throw new Error('ERR_INVALID_USER')
+ } else if (!usr.passkeys?.reg) {
+ throw new Error('ERR_PASSKEY_NOT_SETUP')
+ }
+
+ if (!args.name || args.name.trim().length < 1 || args.name.length > 255) {
+ throw new Error('ERR_PK_NAME_MISSING_OR_INVALID')
+ }
+
+ const verification = await verifyRegistrationResponse({
+ response: args.registrationResponse,
+ expectedChallenge: usr.passkeys.reg.challenge,
+ expectedOrigin: `https://${usr.passkeys.reg.rpId}`,
+ expectedRPID: usr.passkeys.reg.rpId,
+ requireUserVerification: true
+ })
+
+ if (!verification.verified) {
+ throw new Error('ERR_PK_VERIFICATION_FAILED')
+ }
+
+ if (!usr.passkeys.authenticators) {
+ usr.passkeys.authenticators = []
+ }
+ usr.passkeys.authenticators.push({
+ ...verification.registrationInfo,
+ id: uuid(),
+ createdAt: new Date(),
+ name: args.name,
+ siteId: usr.passkeys.reg.siteId,
+ transports: args.registrationResponse.response.transports
+ })
+
+ delete usr.passkeys.reg
+
+ await usr.$query().patch({
+ passkeys: JSON.stringify(usr.passkeys, (k, v) => {
+ if (v instanceof Uint8Array) {
+ return Array.apply([], v)
+ }
+ return v
+ })
+ })
+
+ return {
+ operation: generateSuccess('Passkey registered successfully.')
+ }
+ } catch (err) {
+ return generateError(err)
+ }
+ },
+ /**
+ * Deactivate a passkey
+ */
+ async deactivatePasskey (obj, args, context) {
+ try {
+ const userId = context.req.user?.id
+ if (!userId) {
+ throw new Error('ERR_USER_NOT_AUTHENTICATED')
+ }
+
+ const usr = await WIKI.db.users.query().findById(userId)
+ if (!usr) {
+ throw new Error('ERR_INVALID_USER')
+ } else if (!usr.passkeys?.authenticators) {
+ throw new Error('ERR_PASSKEY_NOT_SETUP')
+ }
+
+ usr.passkeys.authenticators = usr.passkeys.authenticators.filter(a => a.id !== args.id)
+
+ await usr.$query().patch({
+ passkeys: usr.passkeys
+ })
+
+ return {
+ operation: generateSuccess('Passkey deactivated successfully.')
+ }
+ } catch (err) {
+ return generateError(err)
+ }
+ },
/**
* Perform Password Change
*/
diff --git a/server/graph/resolvers/user.mjs b/server/graph/resolvers/user.mjs
index accb6dc967..9271af79bc 100644
--- a/server/graph/resolvers/user.mjs
+++ b/server/graph/resolvers/user.mjs
@@ -2,6 +2,7 @@ import { generateError, generateSuccess } from '../../helpers/graph.mjs'
import _, { isNil } from 'lodash-es'
import path from 'node:path'
import fs from 'fs-extra'
+import { DateTime } from 'luxon'
export default {
Query: {
@@ -59,6 +60,13 @@ export default {
return auth
})
+ usr.passkeys = usr.passkeys.authenticators?.map(a => ({
+ id: a.id,
+ createdAt: DateTime.fromISO(a.createdAt).toJSDate(),
+ name: a.name,
+ siteHostname: a.rpID
+ })) ?? []
+
return usr
},
// async profile (obj, args, context, info) {
diff --git a/server/graph/schemas/authentication.graphql b/server/graph/schemas/authentication.graphql
index cc24a91e94..b7fdca4c7c 100644
--- a/server/graph/schemas/authentication.graphql
+++ b/server/graph/schemas/authentication.graphql
@@ -41,10 +41,28 @@ extend type Mutation {
setup: Boolean
): AuthenticationAuthResponse @rateLimit(limit: 5, duration: 60)
+ setupTFA(
+ strategyId: UUID!
+ siteId: UUID!
+ ): AuthenticationSetupTFAResponse
+
deactivateTFA(
strategyId: UUID!
): DefaultResponse
+ setupPasskey(
+ siteId: UUID!
+ ): AuthenticationSetupPasskeyResponse
+
+ finalizePasskey(
+ registrationResponse: JSON!
+ name: String!
+ ): DefaultResponse
+
+ deactivatePasskey(
+ id: UUID!
+ ): DefaultResponse
+
changePassword(
continuationToken: String
currentPassword: String
@@ -135,6 +153,17 @@ type AuthenticationTokenResponse {
jwt: String
}
+type AuthenticationSetupTFAResponse {
+ operation: Operation
+ continuationToken: String
+ tfaQRImage: String
+}
+
+type AuthenticationSetupPasskeyResponse {
+ operation: Operation
+ registrationOptions: JSON
+}
+
input AuthenticationStrategyInput {
key: String!
strategyKey: String!
diff --git a/server/graph/schemas/user.graphql b/server/graph/schemas/user.graphql
index 048f11db14..3473da3060 100644
--- a/server/graph/schemas/user.graphql
+++ b/server/graph/schemas/user.graphql
@@ -132,6 +132,7 @@ type User {
name: String
email: String
auth: [UserAuth]
+ passkeys: [UserPasskey]
hasAvatar: Boolean
isSystem: Boolean
isActive: Boolean
@@ -152,6 +153,13 @@ type UserAuth {
config: JSON
}
+type UserPasskey {
+ id: UUID
+ name: String
+ createdAt: Date
+ siteHostname: String
+}
+
type UserDefaults {
timezone: String
dateFormat: String
diff --git a/server/locales/en.json b/server/locales/en.json
index aaf2a7722c..9120fe0e38 100644
--- a/server/locales/en.json
+++ b/server/locales/en.json
@@ -1613,6 +1613,9 @@
"editor.unsaved.body": "You have unsaved changes. Are you sure you want to leave the editor and discard any modifications you made since the last save?",
"editor.unsaved.title": "Discard Unsaved Changes?",
"editor.unsavedWarning": "You have unsaved edits. Are you sure you want to leave the editor?",
+ "error.ERR_PK_ALREADY_REGISTERED": "It looks like this authenticator is already registered.",
+ "error.ERR_PK_HOSTNAME_MISSING": "Your administrator must set a valid site hostname before passkeys can be used.",
+ "error.ERR_PK_USER_CANCELLED": "Passkey registration aborted. Make sure to remove the key from your device.",
"fileman.7zFileType": "7zip Archive",
"fileman.aacFileType": "AAC Audio File",
"fileman.aiFileType": "Adobe Illustrator Document",
@@ -1755,6 +1758,7 @@
"profile.authLoadingFailed": "Failed to load authentication methods.",
"profile.authModifyTfa": "Modify 2FA",
"profile.authSetTfa": "Set 2FA",
+ "profile.authSetTfaLoading": "Setting up 2FA... Please wait",
"profile.avatar": "Avatar",
"profile.avatarClearFailed": "Failed to clear profile picture.",
"profile.avatarClearSuccess": "Profile picture cleared successfully.",
@@ -1798,6 +1802,18 @@
"profile.pages.refreshSuccess": "Page list has been refreshed.",
"profile.pages.subtitle": "List of pages I created or last modified",
"profile.pages.title": "Pages",
+ "profile.passkeys": "Passkeys",
+ "profile.passkeysAdd": "Add Passkey",
+ "profile.passkeysDeactivateConfirm": "Are you sure you want to deactivate this passkey?",
+ "profile.passkeysDeactivateFailed": "Failed to deactivate the passkey.",
+ "profile.passkeysDeactivateSuccess": "Passkey deactivated successfully. You may still need to remove the passkey from your device.",
+ "profile.passkeysIntro": "Passkeys are a replacement for passwords for a faster, easier and more secure login. It relies on your device existing biometrics (phone, computer, security key) to validate your identity.",
+ "profile.passkeysInvalidName": "Passkey name is missing or invalid.",
+ "profile.passkeysName": "Passkey Name",
+ "profile.passkeysNameHint": "Enter a name for your passkey:",
+ "profile.passkeysSetupFailed": "Failed to setup new passkey.",
+ "profile.passkeysSetupSuccess": "Passkey registered successfully.",
+ "profile.passkeysUnsupported": "Passkeys are not supported on your device.",
"profile.preferences": "Preferences",
"profile.pronouns": "Pronouns",
"profile.pronounsHint": "Let people know which pronouns should they use when referring to you.",
diff --git a/server/models/users.mjs b/server/models/users.mjs
index 95a7edade6..b24994f7b8 100644
--- a/server/models/users.mjs
+++ b/server/models/users.mjs
@@ -74,7 +74,7 @@ export class User extends Model {
async generateTFA(strategyId, siteId) {
WIKI.logger.debug(`Generating new TFA secret for user ${this.id}...`)
- const site = WIKI.sites[siteId] ?? WIKI.sites[0] ?? { config: { title: 'Wiki' }}
+ const site = WIKI.sites[siteId] ?? WIKI.sites[0] ?? { config: { title: 'Wiki' } }
const tfaInfo = tfa.generateSecret({
name: site.config.title,
account: this.email
@@ -485,7 +485,7 @@ export class User extends Model {
}
if (user) {
- user.auth[strategyId].password = await bcrypt.hash(newPassword, 12),
+ user.auth[strategyId].password = await bcrypt.hash(newPassword, 12)
user.auth[strategyId].mustChangePwd = false
await user.$query().patch({
auth: user.auth
diff --git a/server/modules/rendering/image-prefetch/renderer.mjs b/server/modules/rendering/image-prefetch/renderer.mjs
index 07b8afa710..3e3eecd7be 100644
--- a/server/modules/rendering/image-prefetch/renderer.mjs
+++ b/server/modules/rendering/image-prefetch/renderer.mjs
@@ -1,30 +1,30 @@
-const request = require('request-promise')
+// TODO: refactor to use fetch()
-const prefetch = async (element) => {
- const url = element.attr(`src`)
- let response
- try {
- response = await request({
- method: `GET`,
- url,
- resolveWithFullResponse: true
- })
- } catch (err) {
- WIKI.logger.warn(`Failed to prefetch ${url}`)
- WIKI.logger.warn(err)
- return
- }
- const contentType = response.headers[`content-type`]
- const image = Buffer.from(response.body).toString('base64')
- element.attr('src', `data:${contentType};base64,${image}`)
- element.removeClass('prefetch-candidate')
-}
+// const prefetch = async (element) => {
+// const url = element.attr(`src`)
+// let response
+// try {
+// response = await request({
+// method: `GET`,
+// url,
+// resolveWithFullResponse: true
+// })
+// } catch (err) {
+// WIKI.logger.warn(`Failed to prefetch ${url}`)
+// WIKI.logger.warn(err)
+// return
+// }
+// const contentType = response.headers[`content-type`]
+// const image = Buffer.from(response.body).toString('base64')
+// element.attr('src', `data:${contentType};base64,${image}`)
+// element.removeClass('prefetch-candidate')
+// }
module.exports = {
async init($) {
- const promises = $('img.prefetch-candidate').map((index, element) => {
- return prefetch($(element))
- }).toArray()
- await Promise.all(promises)
+ // const promises = $('img.prefetch-candidate').map((index, element) => {
+ // return prefetch($(element))
+ // }).toArray()
+ // await Promise.all(promises)
}
}
diff --git a/server/package.json b/server/package.json
index 09aed2444a..4b299a23c0 100644
--- a/server/package.json
+++ b/server/package.json
@@ -42,9 +42,11 @@
"@graphql-tools/schema": "10.0.0",
"@graphql-tools/utils": "10.0.6",
"@joplin/turndown-plugin-gfm": "1.0.50",
+ "@node-saml/passport-saml": "4.0.4",
"@root/csr": "0.8.1",
"@root/keypairs": "0.10.3",
"@root/pem": "1.0.4",
+ "@simplewebauthn/server": "8.2.0",
"acme": "3.0.3",
"akismet-api": "6.0.0",
"aws-sdk": "2.1472.0",
@@ -83,8 +85,6 @@
"graphql-upload": "16.0.2",
"he": "1.2.0",
"highlight.js": "11.8.0",
- "i18next": "23.5.1",
- "i18next-node-fs-backend": "2.1.3",
"image-size": "1.0.2",
"js-base64": "3.7.5",
"js-binary": "1.2.0",
@@ -138,7 +138,6 @@
"passport-oauth2": "1.7.0",
"passport-okta-oauth": "0.0.1",
"passport-openidconnect": "0.1.1",
- "passport-saml": "3.2.4",
"passport-slack-oauth2": "1.2.0",
"passport-twitch-strategy": "2.2.0",
"pem-jwk": "2.0.0",
@@ -152,8 +151,6 @@
"puppeteer-core": "21.3.8",
"qr-image": "3.2.0",
"remove-markdown": "0.5.0",
- "request": "2.88.2",
- "request-promise": "4.2.6",
"safe-regex": "2.1.1",
"sanitize-filename": "1.6.3",
"scim-query-filter-parser": "2.0.4",
@@ -179,7 +176,6 @@
"eslint-plugin-import": "2.28.1",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-promise": "6.1.1",
- "eslint-plugin-standard": "5.0.0",
"nodemon": "3.0.1"
},
"overrides": {
diff --git a/server/pnpm-lock.yaml b/server/pnpm-lock.yaml
index c6cfcec062..d8e14137ae 100644
--- a/server/pnpm-lock.yaml
+++ b/server/pnpm-lock.yaml
@@ -23,6 +23,9 @@ dependencies:
'@joplin/turndown-plugin-gfm':
specifier: 1.0.50
version: 1.0.50
+ '@node-saml/passport-saml':
+ specifier: 4.0.4
+ version: 4.0.4
'@root/csr':
specifier: 0.8.1
version: 0.8.1
@@ -32,6 +35,9 @@ dependencies:
'@root/pem':
specifier: 1.0.4
version: 1.0.4
+ '@simplewebauthn/server':
+ specifier: 8.2.0
+ version: 8.2.0
acme:
specifier: 3.0.3
version: 3.0.3
@@ -146,12 +152,6 @@ dependencies:
highlight.js:
specifier: 11.8.0
version: 11.8.0
- i18next:
- specifier: 23.5.1
- version: 23.5.1
- i18next-node-fs-backend:
- specifier: 2.1.3
- version: 2.1.3
image-size:
specifier: 1.0.2
version: 1.0.2
@@ -311,9 +311,6 @@ dependencies:
passport-openidconnect:
specifier: 0.1.1
version: 0.1.1
- passport-saml:
- specifier: 3.2.4
- version: 3.2.4
passport-slack-oauth2:
specifier: 1.2.0
version: 1.2.0
@@ -353,12 +350,6 @@ dependencies:
remove-markdown:
specifier: 0.5.0
version: 0.5.0
- request:
- specifier: 2.88.2
- version: 2.88.2
- request-promise:
- specifier: 4.2.6
- version: 4.2.6(request@2.88.2)
safe-regex:
specifier: 2.1.1
version: 2.1.1
@@ -430,9 +421,6 @@ devDependencies:
eslint-plugin-promise:
specifier: 6.1.1
version: 6.1.1(eslint@8.51.0)
- eslint-plugin-standard:
- specifier: 5.0.0
- version: 5.0.0(eslint@8.51.0)
nodemon:
specifier: 3.0.1
version: 3.0.1
@@ -754,12 +742,53 @@ packages:
- encoding
dev: false
- /@babel/runtime@7.23.1:
- resolution: {integrity: sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==}
- engines: {node: '>=6.9.0'}
- dependencies:
- regenerator-runtime: 0.14.0
+ /@cbor-extract/cbor-extract-darwin-arm64@2.1.1:
+ resolution: {integrity: sha512-blVBy5MXz6m36Vx0DfLd7PChOQKEs8lK2bD1WJn/vVgG4FXZiZmZb2GECHFvVPA5T7OnODd9xZiL3nMCv6QUhA==}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@cbor-extract/cbor-extract-darwin-x64@2.1.1:
+ resolution: {integrity: sha512-h6KFOzqk8jXTvkOftyRIWGrd7sKQzQv2jVdTL9nKSf3D2drCvQB/LHUxAOpPXo3pv2clDtKs3xnHalpEh3rDsw==}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@cbor-extract/cbor-extract-linux-arm64@2.1.1:
+ resolution: {integrity: sha512-SxAaRcYf8S0QHaMc7gvRSiTSr7nUYMqbUdErBEu+HYA4Q6UNydx1VwFE68hGcp1qvxcy9yT5U7gA+a5XikfwSQ==}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@cbor-extract/cbor-extract-linux-arm@2.1.1:
+ resolution: {integrity: sha512-ds0uikdcIGUjPyraV4oJqyVE5gl/qYBpa/Wnh6l6xLE2lj/hwnjT2XcZCChdXwW/YFZ1LUHs6waoYN8PmK0nKQ==}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@cbor-extract/cbor-extract-linux-x64@2.1.1:
+ resolution: {integrity: sha512-GVK+8fNIE9lJQHAlhOROYiI0Yd4bAZ4u++C2ZjlkS3YmO6hi+FUxe6Dqm+OKWTcMpL/l71N6CQAmaRcb4zyJuA==}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@cbor-extract/cbor-extract-win32-x64@2.1.1:
+ resolution: {integrity: sha512-2Niq1C41dCRIDeD8LddiH+mxGlO7HJ612Ll3D/E73ZWBmycued+8ghTr/Ho3CMOWPUEr08XtyBMVXAjqF+TcKw==}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
dev: false
+ optional: true
/@eslint-community/eslint-utils@4.4.0(eslint@8.51.0):
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
@@ -880,6 +909,10 @@ packages:
graphql: 16.8.1
dev: false
+ /@hexagon/base64@1.1.28:
+ resolution: {integrity: sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==}
+ dev: false
+
/@humanwhocodes/config-array@0.11.11:
resolution: {integrity: sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==}
engines: {node: '>=10.10.0'}
@@ -920,6 +953,39 @@ packages:
resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==}
dev: false
+ /@node-saml/node-saml@4.0.5:
+ resolution: {integrity: sha512-J5DglElbY1tjOuaR1NPtjOXkXY5bpUhDoKVoeucYN98A3w4fwgjIOPqIGcb6cQsqFq2zZ6vTCeKn5C/hvefSaw==}
+ engines: {node: '>= 14'}
+ dependencies:
+ '@types/debug': 4.1.9
+ '@types/passport': 1.0.13
+ '@types/xml-crypto': 1.4.3
+ '@types/xml-encryption': 1.2.2
+ '@types/xml2js': 0.4.12
+ '@xmldom/xmldom': 0.8.10
+ debug: 4.3.4
+ xml-crypto: 3.2.0
+ xml-encryption: 3.0.2
+ xml2js: 0.5.0
+ xmlbuilder: 15.1.1
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /@node-saml/passport-saml@4.0.4:
+ resolution: {integrity: sha512-xFw3gw0yo+K1mzlkW15NeBF7cVpRHN/4vpjmBKzov5YFImCWh/G0LcTZ8krH3yk2/eRPc3Or8LRPudVJBjmYaw==}
+ engines: {node: '>= 14'}
+ dependencies:
+ '@node-saml/node-saml': 4.0.5
+ '@types/express': 4.17.18
+ '@types/passport': 1.0.13
+ '@types/passport-strategy': 0.2.36
+ passport: 0.6.0
+ passport-strategy: 1.0.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
/@nodelib/fs.scandir@2.1.5:
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -1194,6 +1260,50 @@ packages:
engines: {node: '>=8.0.0'}
dev: false
+ /@peculiar/asn1-android@2.3.6:
+ resolution: {integrity: sha512-zkYh4DsiRhiNfg6tWaUuRc+huwlb9XJbmeZLrjTz9v76UK1Ehq3EnfJFED6P3sdznW/nqWe46LoM9JrqxcD58g==}
+ dependencies:
+ '@peculiar/asn1-schema': 2.3.6
+ asn1js: 3.0.5
+ tslib: 2.6.2
+ dev: false
+
+ /@peculiar/asn1-ecc@2.3.6:
+ resolution: {integrity: sha512-Hu1xzMJQWv8/GvzOiinaE6XiD1/kEhq2C/V89UEoWeZ2fLUcGNIvMxOr/pMyL0OmpRWj/mhCTXOZp4PP+a0aTg==}
+ dependencies:
+ '@peculiar/asn1-schema': 2.3.6
+ '@peculiar/asn1-x509': 2.3.6
+ asn1js: 3.0.5
+ tslib: 2.6.2
+ dev: false
+
+ /@peculiar/asn1-rsa@2.3.6:
+ resolution: {integrity: sha512-DswjJyAXZnvESuImGNTvbNKvh1XApBVqU+r3UmrFFTAI23gv62byl0f5OFKWTNhCf66WQrd3sklpsCZc/4+jwA==}
+ dependencies:
+ '@peculiar/asn1-schema': 2.3.6
+ '@peculiar/asn1-x509': 2.3.6
+ asn1js: 3.0.5
+ tslib: 2.6.2
+ dev: false
+
+ /@peculiar/asn1-schema@2.3.6:
+ resolution: {integrity: sha512-izNRxPoaeJeg/AyH8hER6s+H7p4itk+03QCa4sbxI3lNdseQYCuxzgsuNK8bTXChtLTjpJz6NmXKA73qLa3rCA==}
+ dependencies:
+ asn1js: 3.0.5
+ pvtsutils: 1.3.5
+ tslib: 2.6.2
+ dev: false
+
+ /@peculiar/asn1-x509@2.3.6:
+ resolution: {integrity: sha512-dRwX31R1lcbIdzbztiMvLNTDoGptxdV7HocNx87LfKU0fEWh7fTWJjx4oV+glETSy6heF/hJHB2J4RGB3vVSYg==}
+ dependencies:
+ '@peculiar/asn1-schema': 2.3.6
+ asn1js: 3.0.5
+ ipaddr.js: 2.1.0
+ pvtsutils: 1.3.5
+ tslib: 2.6.2
+ dev: false
+
/@protobufjs/aspromise@1.1.2:
resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==}
dev: false
@@ -1306,6 +1416,27 @@ packages:
'@root/encoding': 1.0.1
dev: false
+ /@simplewebauthn/server@8.2.0:
+ resolution: {integrity: sha512-nknf7kCa5V61Kk2zn1vTuKeAlyut9aWduIcbHNQWpMCEJqH/m8cXpb+9UV42MEQRIk8JVC1GSNeEx56QVTfJHw==}
+ engines: {node: '>=16.0.0'}
+ dependencies:
+ '@hexagon/base64': 1.1.28
+ '@peculiar/asn1-android': 2.3.6
+ '@peculiar/asn1-ecc': 2.3.6
+ '@peculiar/asn1-rsa': 2.3.6
+ '@peculiar/asn1-schema': 2.3.6
+ '@peculiar/asn1-x509': 2.3.6
+ '@simplewebauthn/typescript-types': 8.0.0
+ cbor-x: 1.5.4
+ cross-fetch: 4.0.0
+ transitivePeerDependencies:
+ - encoding
+ dev: false
+
+ /@simplewebauthn/typescript-types@8.0.0:
+ resolution: {integrity: sha512-d7Izb2H+LZJteXMkS8DmpAarD6mZdpIOu/av/yH4/u/3Pd6DKFLyBM3j8BMmUvUqpzvJvHARNrRfQYto58mtTQ==}
+ dev: false
+
/@socket.io/component-emitter@3.1.0:
resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==}
dev: false
@@ -1360,6 +1491,12 @@ packages:
'@types/node': 20.8.3
dev: false
+ /@types/debug@4.1.9:
+ resolution: {integrity: sha512-8Hz50m2eoS56ldRlepxSBa6PWEVCtzUo/92HgLc2qTMnotJNIm7xP+UZhyWoYsyOdd5dxZ+NZLb24rsKyFs2ow==}
+ dependencies:
+ '@types/ms': 0.7.32
+ dev: false
+
/@types/express-serve-static-core@4.17.37:
resolution: {integrity: sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==}
dependencies:
@@ -1425,6 +1562,10 @@ packages:
resolution: {integrity: sha512-Wj+fqpTLtTbG7c0tH47dkahefpLKEbB+xAZuLq7b4/IDHPl/n6VoXcyUQ2bypFlbSwvCr0y+bD4euTTqTJsPxQ==}
dev: false
+ /@types/ms@0.7.32:
+ resolution: {integrity: sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g==}
+ dev: false
+
/@types/node-fetch@2.6.6:
resolution: {integrity: sha512-95X8guJYhfqiuVVhRFxVQcf4hW/2bCuoPwDasMf/531STFoNoWTT7YDnWdXHEZKqAGUigmpG31r2FE70LwnzJw==}
dependencies:
@@ -1446,6 +1587,19 @@ packages:
resolution: {integrity: sha512-STkyj0IQkgbmohF1afXQN64KucE3w7EgSbNJxqkJoq0KHVBV4nU5Pyku+TM9UCiCLXhZlkEFd8zq38P8lDFi6g==}
dev: false
+ /@types/passport-strategy@0.2.36:
+ resolution: {integrity: sha512-hotVZuaCt04LJYXfZD5B+5UeCcRVG8IjKaLLGTJ1eFp0wiFQA2XfsqslGGInWje+OysNNLPH/ducce5GXHDC1Q==}
+ dependencies:
+ '@types/express': 4.17.18
+ '@types/passport': 1.0.13
+ dev: false
+
+ /@types/passport@1.0.13:
+ resolution: {integrity: sha512-XXURryL+EZAWtbQFOHX1eNB+RJwz5XMPPz1xrGpEKr2xUZCXM4NCPkHMtZQ3B2tTSG/1IRaAcTHjczRA4sSFCw==}
+ dependencies:
+ '@types/express': 4.17.18
+ dev: false
+
/@types/qs@6.9.8:
resolution: {integrity: sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==}
dev: false
@@ -1475,6 +1629,25 @@ packages:
'@types/node': 20.8.3
dev: false
+ /@types/xml-crypto@1.4.3:
+ resolution: {integrity: sha512-pnvKYb7vUsUIMc+C6JM/j779YWQgOMcwjnqHJ9cdaWXwWEBE1hAqthzeszRx62V5RWMvS+XS9w9tXMOYyUc8zg==}
+ dependencies:
+ '@types/node': 20.8.3
+ xpath: 0.0.27
+ dev: false
+
+ /@types/xml-encryption@1.2.2:
+ resolution: {integrity: sha512-UeuYOqW3ZzUQfwb/mb3GNZ2/DlVdh5mjJNmB/yFXgQr8/pwlVJ9I2w+AHPfRDzLshe7YpgUB4T1//qgbk6U87Q==}
+ dependencies:
+ '@types/node': 20.8.3
+ dev: false
+
+ /@types/xml2js@0.4.12:
+ resolution: {integrity: sha512-CZPpQKBZ8db66EP5hCjwvYrLThgZvnyZrPXK2W+UI1oOaWezGt34iOaUCX4Jah2X8+rQqjvl9VKEIT8TR1I0rA==}
+ dependencies:
+ '@types/node': 20.8.3
+ dev: false
+
/@types/yauzl@2.10.1:
resolution: {integrity: sha512-CHzgNU3qYBnp/O4S3yv2tXPlvMTq0YWSTVg2/JYLqWZGHwwgJGAwd00poay/11asPq8wLFwHzubyInqHIFmmiw==}
requiresBuild: true
@@ -1519,8 +1692,8 @@ packages:
dev: false
optional: true
- /@xmldom/xmldom@0.7.13:
- resolution: {integrity: sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==}
+ /@xmldom/xmldom@0.8.10:
+ resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==}
engines: {node: '>=10.0.0'}
dev: false
@@ -1607,6 +1780,7 @@ packages:
fast-json-stable-stringify: 2.1.0
json-schema-traverse: 0.4.1
uri-js: 4.4.1
+ dev: true
/ajv@8.12.0:
resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==}
@@ -1658,12 +1832,6 @@ packages:
resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==}
dev: false
- /argparse@1.0.10:
- resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
- dependencies:
- sprintf-js: 1.0.3
- dev: false
-
/argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
@@ -1752,6 +1920,15 @@ packages:
safer-buffer: 2.1.2
dev: false
+ /asn1js@3.0.5:
+ resolution: {integrity: sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==}
+ engines: {node: '>=12.0.0'}
+ dependencies:
+ pvtsutils: 1.3.5
+ pvutils: 1.1.3
+ tslib: 2.6.2
+ dev: false
+
/assert-plus@1.0.0:
resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==}
engines: {node: '>=0.8'}
@@ -1802,14 +1979,6 @@ packages:
xml2js: 0.5.0
dev: false
- /aws-sign2@0.7.0:
- resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==}
- dev: false
-
- /aws4@1.12.0:
- resolution: {integrity: sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==}
- dev: false
-
/axios@0.27.2:
resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==}
dependencies:
@@ -1852,12 +2021,6 @@ packages:
engines: {node: '>=10.0.0'}
dev: false
- /bcrypt-pbkdf@1.0.2:
- resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
- dependencies:
- tweetnacl: 0.14.5
- dev: false
-
/bcryptjs@2.4.3:
resolution: {integrity: sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==}
dev: false
@@ -2042,8 +2205,26 @@ packages:
engines: {node: '>=6'}
dev: true
- /caseless@0.12.0:
- resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
+ /cbor-extract@2.1.1:
+ resolution: {integrity: sha512-1UX977+L+zOJHsp0mWFG13GLwO6ucKgSmSW6JTl8B9GUvACvHeIVpFqhU92299Z6PfD09aTXDell5p+lp1rUFA==}
+ hasBin: true
+ requiresBuild: true
+ dependencies:
+ node-gyp-build-optional-packages: 5.0.3
+ optionalDependencies:
+ '@cbor-extract/cbor-extract-darwin-arm64': 2.1.1
+ '@cbor-extract/cbor-extract-darwin-x64': 2.1.1
+ '@cbor-extract/cbor-extract-linux-arm': 2.1.1
+ '@cbor-extract/cbor-extract-linux-arm64': 2.1.1
+ '@cbor-extract/cbor-extract-linux-x64': 2.1.1
+ '@cbor-extract/cbor-extract-win32-x64': 2.1.1
+ dev: false
+ optional: true
+
+ /cbor-x@1.5.4:
+ resolution: {integrity: sha512-PVKILDn+Rf6MRhhcyzGXi5eizn1i0i3F8Fe6UMMxXBnWkalq9+C5+VTmlIjAYM4iF2IYF2N+zToqAfYOp+3rfw==}
+ optionalDependencies:
+ cbor-extract: 2.1.1
dev: false
/chalk@4.1.2:
@@ -2367,13 +2548,6 @@ packages:
resolution: {integrity: sha512-YOrdiS4b/ItbPAtyEIpkhqryoul2Bu8vtX+SN2nmxsqPnqAfh48Nu9p6zdTp9iCgCoSb6Ib8B0y4UUznaVXgtA==}
dev: false
- /dashdash@1.14.1:
- resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==}
- engines: {node: '>=0.10'}
- dependencies:
- assert-plus: 1.0.0
- dev: false
-
/data-uri-to-buffer@6.0.1:
resolution: {integrity: sha512-MZd3VlchQkp8rdend6vrx7MmVDJzSNTBvghvKjirLkD+WTChA3KUf0jkE68Q4UyctNqI11zZO9/x2Yx+ub5Cvg==}
engines: {node: '>= 14'}
@@ -2602,13 +2776,6 @@ packages:
dev: false
optional: true
- /ecc-jsbn@0.1.2:
- resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==}
- dependencies:
- jsbn: 0.1.1
- safer-buffer: 2.1.2
- dev: false
-
/ecdsa-sig-formatter@1.0.11:
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
dependencies:
@@ -3150,10 +3317,6 @@ packages:
- supports-color
dev: false
- /extend@3.0.2:
- resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
- dev: false
-
/extract-zip@2.0.1:
resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==}
engines: {node: '>= 10.17.0'}
@@ -3187,6 +3350,7 @@ packages:
/fast-json-stable-stringify@2.1.0:
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+ dev: true
/fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
@@ -3286,19 +3450,6 @@ packages:
dependencies:
is-callable: 1.2.7
- /forever-agent@0.6.1:
- resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==}
- dev: false
-
- /form-data@2.3.3:
- resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==}
- engines: {node: '>= 0.12'}
- dependencies:
- asynckit: 0.4.0
- combined-stream: 1.0.8
- mime-types: 2.1.35
- dev: false
-
/form-data@4.0.0:
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
engines: {node: '>= 6'}
@@ -3443,12 +3594,6 @@ packages:
async: 3.2.4
dev: false
- /getpass@0.1.7:
- resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==}
- dependencies:
- assert-plus: 1.0.0
- dev: false
-
/github-from-package@0.0.0:
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
dev: false
@@ -3595,20 +3740,6 @@ packages:
engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
dev: false
- /har-schema@2.0.0:
- resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==}
- engines: {node: '>=4'}
- dev: false
-
- /har-validator@5.1.5:
- resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==}
- engines: {node: '>=6'}
- deprecated: this library is no longer supported
- dependencies:
- ajv: 6.12.6
- har-schema: 2.0.0
- dev: false
-
/has-bigints@1.0.2:
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
dev: true
@@ -3726,15 +3857,6 @@ packages:
- supports-color
dev: false
- /http-signature@1.2.0:
- resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==}
- engines: {node: '>=0.8', npm: '>=1.3.7'}
- dependencies:
- assert-plus: 1.0.0
- jsprim: 1.4.2
- sshpk: 1.17.0
- dev: false
-
/https-proxy-agent@5.0.1:
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
engines: {node: '>= 6'}
@@ -3755,20 +3877,6 @@ packages:
- supports-color
dev: false
- /i18next-node-fs-backend@2.1.3:
- resolution: {integrity: sha512-CreMFiVl3ChlMc5ys/e0QfuLFOZyFcL40Jj6jaKD6DxZ/GCUMxPI9BpU43QMWUgC7r+PClpxg2cGXAl0CjG04g==}
- deprecated: replaced by i18next-fs-backend
- dependencies:
- js-yaml: 3.13.1
- json5: 2.0.0
- dev: false
-
- /i18next@23.5.1:
- resolution: {integrity: sha512-JelYzcaCoFDaa+Ysbfz2JsGAKkrHiMG6S61+HLBUEIPaF40WMwW9hCPymlQGrP+wWawKxKPuSuD71WZscCsWHg==}
- dependencies:
- '@babel/runtime': 7.23.1
- dev: false
-
/iconv-lite@0.4.24:
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
engines: {node: '>=0.10.0'}
@@ -3866,6 +3974,11 @@ packages:
engines: {node: '>= 0.10'}
dev: false
+ /ipaddr.js@2.1.0:
+ resolution: {integrity: sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==}
+ engines: {node: '>= 10'}
+ dev: false
+
/is-arguments@1.1.1:
resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
engines: {node: '>= 0.4'}
@@ -4008,10 +4121,6 @@ packages:
dependencies:
which-typed-array: 1.1.11
- /is-typedarray@1.0.0:
- resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
- dev: false
-
/is-weakref@1.0.2:
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
dependencies:
@@ -4030,10 +4139,6 @@ packages:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
dev: true
- /isstream@0.1.2:
- resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==}
- dev: false
-
/jmespath@0.16.0:
resolution: {integrity: sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==}
engines: {node: '>= 0.6.0'}
@@ -4054,24 +4159,12 @@ packages:
dev: false
optional: true
- /js-yaml@3.13.1:
- resolution: {integrity: sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==}
- hasBin: true
- dependencies:
- argparse: 1.0.10
- esprima: 4.0.1
- dev: false
-
/js-yaml@4.1.0:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
hasBin: true
dependencies:
argparse: 2.0.1
- /jsbn@0.1.1:
- resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==}
- dev: false
-
/jsdom@22.1.0:
resolution: {integrity: sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==}
engines: {node: '>=16'}
@@ -4116,23 +4209,16 @@ packages:
/json-schema-traverse@0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+ dev: true
/json-schema-traverse@1.0.0:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
dev: false
- /json-schema@0.4.0:
- resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
- dev: false
-
/json-stable-stringify-without-jsonify@1.0.1:
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
dev: true
- /json-stringify-safe@5.0.1:
- resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
- dev: false
-
/json5@1.0.2:
resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
hasBin: true
@@ -4140,14 +4226,6 @@ packages:
minimist: 1.2.8
dev: true
- /json5@2.0.0:
- resolution: {integrity: sha512-0EdQvHuLm7yJ7lyG5dp7Q3X2ku++BG5ZHaJ5FTnaXpKqDrw4pMxel5Bt3oAYMthnrthFBdnZ1FcsXTPyrQlV0w==}
- engines: {node: '>=6'}
- hasBin: true
- dependencies:
- minimist: 1.2.8
- dev: false
-
/jsonfile@4.0.0:
resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
optionalDependencies:
@@ -4186,16 +4264,6 @@ packages:
semver: 7.5.4
dev: false
- /jsprim@1.4.2:
- resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==}
- engines: {node: '>=0.6.0'}
- dependencies:
- assert-plus: 1.0.0
- extsprintf: 1.3.0
- json-schema: 0.4.0
- verror: 1.10.0
- dev: false
-
/jwa@1.4.1:
resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==}
dependencies:
@@ -4763,6 +4831,13 @@ packages:
engines: {node: '>= 6.13.0'}
dev: false
+ /node-gyp-build-optional-packages@5.0.3:
+ resolution: {integrity: sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==}
+ hasBin: true
+ requiresBuild: true
+ dev: false
+ optional: true
+
/node-jose@2.2.0:
resolution: {integrity: sha512-XPCvJRr94SjLrSIm4pbYHKLEaOsDvJCpyFw/6V/KK/IXmyZ6SFBzAUDO9HQf4DB/nTEFcRGH87mNciOP23kFjw==}
dependencies:
@@ -4829,10 +4904,6 @@ packages:
resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==}
dev: false
- /oauth-sign@0.9.0:
- resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==}
- dev: false
-
/oauth@0.9.15:
resolution: {integrity: sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==}
dev: false
@@ -5194,22 +5265,6 @@ packages:
passport-strategy: 1.0.0
dev: false
- /passport-saml@3.2.4:
- resolution: {integrity: sha512-JSgkFXeaexLNQh1RrOvJAgjLnZzH/S3HbX/mWAk+i7aulnjqUe7WKnPl1NPnJWqP7Dqsv0I2Xm6KIFHkftk0HA==}
- engines: {node: '>= 12'}
- deprecated: For versions >= 4, please use scopped package @node-saml/passport-saml
- dependencies:
- '@xmldom/xmldom': 0.7.13
- debug: 4.3.4
- passport-strategy: 1.0.0
- xml-crypto: 2.1.5
- xml-encryption: 2.0.0
- xml2js: 0.4.23
- xmlbuilder: 15.1.1
- transitivePeerDependencies:
- - supports-color
- dev: false
-
/passport-slack-oauth2@1.2.0:
resolution: {integrity: sha512-SeQl8uPoi4ajhzgIvwQM7gW/6yPrKH0hPFjxcP/426SOZ0M9ZNDOfSa32q3NTw7KcwYOTjyWX/2xdJndQE7Rkg==}
dependencies:
@@ -5281,10 +5336,6 @@ packages:
resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
dev: false
- /performance-now@2.1.0:
- resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
- dev: false
-
/pg-cloudflare@1.1.1:
resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==}
requiresBuild: true
@@ -5579,6 +5630,17 @@ packages:
- utf-8-validate
dev: false
+ /pvtsutils@1.3.5:
+ resolution: {integrity: sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==}
+ dependencies:
+ tslib: 2.6.2
+ dev: false
+
+ /pvutils@1.1.3:
+ resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==}
+ engines: {node: '>=6.0.0'}
+ dev: false
+
/qr-image@3.2.0:
resolution: {integrity: sha512-rXKDS5Sx3YipVsqmlMJsJsk6jXylEpiHRC2+nJy66fxA5ExYyGa4PqwteW69SaVmAb2OQ18HbYriT7cGQMbduw==}
dev: false
@@ -5597,11 +5659,6 @@ packages:
side-channel: 1.0.4
dev: false
- /qs@6.5.3:
- resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==}
- engines: {node: '>=0.6'}
- dev: false
-
/querystring@0.2.0:
resolution: {integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==}
engines: {node: '>=0.4.x'}
@@ -5717,10 +5774,6 @@ packages:
resolve: 1.22.6
dev: false
- /regenerator-runtime@0.14.0:
- resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==}
- dev: false
-
/regexp-tree@0.1.27:
resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==}
hasBin: true
@@ -5744,57 +5797,6 @@ packages:
resolution: {integrity: sha512-x917M80K97K5IN1L8lUvFehsfhR8cYjGQ/yAMRI9E7JIKivtl5Emo5iD13DhMr+VojzMCiYk8V2byNPwT/oapg==}
dev: false
- /request-promise-core@1.1.4(request@2.88.2):
- resolution: {integrity: sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==}
- engines: {node: '>=0.10.0'}
- peerDependencies:
- request: ^2.34
- dependencies:
- lodash: 4.17.21
- request: 2.88.2
- dev: false
-
- /request-promise@4.2.6(request@2.88.2):
- resolution: {integrity: sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==}
- engines: {node: '>=0.10.0'}
- deprecated: request-promise has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142
- peerDependencies:
- request: ^2.34
- dependencies:
- bluebird: 3.7.2
- request: 2.88.2
- request-promise-core: 1.1.4(request@2.88.2)
- stealthy-require: 1.1.1
- tough-cookie: 2.5.0
- dev: false
-
- /request@2.88.2:
- resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==}
- engines: {node: '>= 6'}
- deprecated: request has been deprecated, see https://github.com/request/request/issues/3142
- dependencies:
- aws-sign2: 0.7.0
- aws4: 1.12.0
- caseless: 0.12.0
- combined-stream: 1.0.8
- extend: 3.0.2
- forever-agent: 0.6.1
- form-data: 2.3.3
- har-validator: 5.1.5
- http-signature: 1.2.0
- is-typedarray: 1.0.0
- isstream: 0.1.2
- json-stringify-safe: 5.0.1
- mime-types: 2.1.35
- oauth-sign: 0.9.0
- performance-now: 2.1.0
- qs: 6.5.3
- safe-buffer: 5.2.1
- tough-cookie: 2.5.0
- tunnel-agent: 0.6.0
- uuid: 3.4.0
- dev: false
-
/require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
@@ -5934,10 +5936,6 @@ packages:
resolution: {integrity: sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==}
dev: false
- /sax@1.3.0:
- resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==}
- dev: false
-
/saxes@6.0.0:
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
engines: {node: '>=v12.22.7'}
@@ -6168,36 +6166,11 @@ packages:
engines: {node: '>= 10.x'}
dev: false
- /sprintf-js@1.0.3:
- resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
- dev: false
-
- /sshpk@1.17.0:
- resolution: {integrity: sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==}
- engines: {node: '>=0.10.0'}
- hasBin: true
- dependencies:
- asn1: 0.2.6
- assert-plus: 1.0.0
- bcrypt-pbkdf: 1.0.2
- dashdash: 1.14.1
- ecc-jsbn: 0.1.2
- getpass: 0.1.7
- jsbn: 0.1.1
- safer-buffer: 2.1.2
- tweetnacl: 0.14.5
- dev: false
-
/statuses@2.0.1:
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
engines: {node: '>= 0.8'}
dev: false
- /stealthy-require@1.1.1:
- resolution: {integrity: sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==}
- engines: {node: '>=0.10.0'}
- dev: false
-
/streamsearch@1.1.0:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'}
@@ -6421,14 +6394,6 @@ packages:
nopt: 1.0.10
dev: true
- /tough-cookie@2.5.0:
- resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==}
- engines: {node: '>=0.8'}
- dependencies:
- psl: 1.9.0
- punycode: 2.3.0
- dev: false
-
/tough-cookie@4.1.3:
resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==}
engines: {node: '>=6'}
@@ -6495,10 +6460,6 @@ packages:
domino: 2.1.6
dev: false
- /tweetnacl@0.14.5:
- resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==}
- dev: false
-
/twemoji-parser@14.0.0:
resolution: {integrity: sha512-9DUOTGLOWs0pFWnh1p6NF+C3CkQ96PWmEFwhOVmT3WbecRC+68AIqpsnJXygfkFcp4aXbOp8Dwbhh/HQgvoRxA==}
dev: false
@@ -6714,12 +6675,6 @@ packages:
engines: {node: '>= 0.4.0'}
dev: false
- /uuid@3.4.0:
- resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==}
- deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
- hasBin: true
- dev: false
-
/uuid@8.0.0:
resolution: {integrity: sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==}
hasBin: true
@@ -6887,19 +6842,19 @@ packages:
optional: true
dev: false
- /xml-crypto@2.1.5:
- resolution: {integrity: sha512-xOSJmGFm+BTXmaPYk8pPV3duKo6hJuZ5niN4uMzoNcTlwYs0jAu/N3qY+ud9MhE4N7eMRuC1ayC7Yhmb7MmAWg==}
- engines: {node: '>=0.4.0'}
+ /xml-crypto@3.2.0:
+ resolution: {integrity: sha512-qVurBUOQrmvlgmZqIVBqmb06TD2a/PpEUfFPgD7BuBfjmoH4zgkqaWSIJrnymlCvM2GGt9x+XtJFA+ttoAufqg==}
+ engines: {node: '>=4.0.0'}
dependencies:
- '@xmldom/xmldom': 0.7.13
+ '@xmldom/xmldom': 0.8.10
xpath: 0.0.32
dev: false
- /xml-encryption@2.0.0:
- resolution: {integrity: sha512-4Av83DdvAgUQQMfi/w8G01aJshbEZP9ewjmZMpS9t3H+OCZBDvyK4GJPnHGfWiXlArnPbYvR58JB9qF2x9Ds+Q==}
+ /xml-encryption@3.0.2:
+ resolution: {integrity: sha512-VxYXPvsWB01/aqVLd6ZMPWZ+qaj0aIdF+cStrVJMcFj3iymwZeI0ABzB3VqMYv48DkSpRhnrXqTUkR34j+UDyg==}
engines: {node: '>=12'}
dependencies:
- '@xmldom/xmldom': 0.7.13
+ '@xmldom/xmldom': 0.8.10
escape-html: 1.0.3
xpath: 0.0.32
dev: false
@@ -6909,14 +6864,6 @@ packages:
engines: {node: '>=12'}
dev: false
- /xml2js@0.4.23:
- resolution: {integrity: sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==}
- engines: {node: '>=4.0.0'}
- dependencies:
- sax: 1.3.0
- xmlbuilder: 11.0.1
- dev: false
-
/xml2js@0.4.4:
resolution: {integrity: sha512-9ERdxLOo4EazMDHAS/vsuZiTXIMur6ydcRfzGrFVJ4qM78zD3ohUgPJC7NYpGwd5rnS0ufSydMJClh6jyH+V0w==}
dependencies:
@@ -6946,6 +6893,11 @@ packages:
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
dev: false
+ /xpath@0.0.27:
+ resolution: {integrity: sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ==}
+ engines: {node: '>=0.6.0'}
+ dev: false
+
/xpath@0.0.32:
resolution: {integrity: sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==}
engines: {node: '>=0.6.0'}
diff --git a/ux/package.json b/ux/package.json
index 4dfc900d5f..1a5422cb3d 100644
--- a/ux/package.json
+++ b/ux/package.json
@@ -17,6 +17,7 @@
"@lezer/common": "1.1.0",
"@mdi/font": "7.3.67",
"@quasar/extras": "1.16.7",
+ "@simplewebauthn/browser": "8.3.1",
"@tiptap/core": "2.1.11",
"@tiptap/extension-code-block": "2.1.11",
"@tiptap/extension-code-block-lowlight": "2.1.11",
diff --git a/ux/pnpm-lock.yaml b/ux/pnpm-lock.yaml
index 211f3a7bf0..cf78399c1f 100644
--- a/ux/pnpm-lock.yaml
+++ b/ux/pnpm-lock.yaml
@@ -17,6 +17,9 @@ dependencies:
'@quasar/extras':
specifier: 1.16.7
version: 1.16.7
+ '@simplewebauthn/browser':
+ specifier: 8.3.1
+ version: 8.3.1
'@tiptap/core':
specifier: 2.1.11
version: 2.1.11(@tiptap/pm@2.1.11)
@@ -709,6 +712,16 @@ packages:
picomatch: 2.3.1
dev: true
+ /@simplewebauthn/browser@8.3.1:
+ resolution: {integrity: sha512-bMW7oOkxX4ydRAkkPtJ1do2k9yOoIGc/hZYebcuEOVdJoC6wwVpu97mYY7Mz8B9hLlcaR5WFgBsLl5tSJVzm8A==}
+ dependencies:
+ '@simplewebauthn/typescript-types': 8.0.0
+ dev: false
+
+ /@simplewebauthn/typescript-types@8.0.0:
+ resolution: {integrity: sha512-d7Izb2H+LZJteXMkS8DmpAarD6mZdpIOu/av/yH4/u/3Pd6DKFLyBM3j8BMmUvUqpzvJvHARNrRfQYto58mtTQ==}
+ dev: false
+
/@socket.io/component-emitter@3.1.0:
resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==}
dev: false
diff --git a/ux/public/_assets/icons/fluent-add-key.svg b/ux/public/_assets/icons/fluent-add-key.svg
new file mode 100644
index 0000000000..9920976ade
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-add-key.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-fingerprint.svg b/ux/public/_assets/icons/fluent-fingerprint.svg
new file mode 100644
index 0000000000..b335cd746f
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-fingerprint.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/src/components/PasskeyCreateDialog.vue b/ux/src/components/PasskeyCreateDialog.vue
new file mode 100644
index 0000000000..4bed60121f
--- /dev/null
+++ b/ux/src/components/PasskeyCreateDialog.vue
@@ -0,0 +1,83 @@
+
+q-dialog(ref='dialogRef', @hide='onDialogHide', persistent)
+ q-card(style='min-width: 650px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-add-key.svg', left, size='sm')
+ span {{t(`profile.passkeysAdd`)}}
+ .q-py-sm
+ .text-body2.q-px-md.q-py-sm {{t(`profile.passkeysNameHint`)}}
+ q-item
+ blueprint-icon(icon='key')
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.name'
+ dense
+ hide-bottom-space
+ :label='t(`profile.passkeysName`)'
+ :aria-label='t(`profile.passkeysName`)'
+ autofocus
+ @keyup.enter='save'
+ )
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.save`)'
+ color='primary'
+ padding='xs md'
+ @click='save'
+ )
+
+
+
diff --git a/ux/src/components/SetupTfaDialog.vue b/ux/src/components/SetupTfaDialog.vue
index 5fe108303d..129e54fca0 100644
--- a/ux/src/components/SetupTfaDialog.vue
+++ b/ux/src/components/SetupTfaDialog.vue
@@ -1,97 +1,57 @@
-q-dialog(ref='dialogRef', @hide='onDialogHide')
- q-card(style='min-width: 650px;')
+q-dialog(ref='dialogRef', @hide='onDialogHide', persistent)
+ q-card.setup2fadialog(style='min-width: 450px;')
q-card-section.card-header
- q-icon(name='img:/_assets/icons/fluent-password-reset.svg', left, size='sm')
- span {{t(`admin.users.changePassword`)}}
- q-form.q-py-sm(ref='changeUserPwdForm', @submit='save')
- q-item
- blueprint-icon(icon='lock')
- q-item-section
- q-input(
- outlined
- v-model='state.currentPassword'
- dense
- :rules='currentPasswordValidation'
- hide-bottom-space
- :label='t(`auth.changePwd.currentPassword`)'
- :aria-label='t(`auth.changePwd.currentPassword`)'
- lazy-rules='ondemand'
- autofocus
- )
- q-item
- blueprint-icon(icon='password')
- q-item-section
- q-input(
- outlined
- v-model='state.newPassword'
- dense
- :rules='newPasswordValidation'
- hide-bottom-space
- :label='t(`auth.changePwd.newPassword`)'
- :aria-label='t(`auth.changePwd.newPassword`)'
- lazy-rules='ondemand'
- autofocus
- )
- template(#append)
- .flex.items-center
- q-badge(
- :color='passwordStrength.color'
- :label='passwordStrength.label'
- )
- q-separator.q-mx-sm(vertical)
- q-btn(
- flat
- dense
- padding='none xs'
- color='brown'
- @click='randomizePassword'
- )
- q-icon(name='las la-dice-d6')
- .q-pl-xs.text-caption: strong Generate
- q-item
- blueprint-icon(icon='good-pincode')
- q-item-section
- q-input(
- outlined
- v-model='state.verifyPassword'
- dense
- :rules='verifyPasswordValidation'
- hide-bottom-space
- :label='t(`auth.changePwd.newPasswordVerify`)'
- :aria-label='t(`auth.changePwd.newPasswordVerify`)'
- lazy-rules='ondemand'
- autofocus
- )
- q-card-actions.card-actions
- q-space
- q-btn.acrylic-btn(
- flat
- :label='t(`common.actions.cancel`)'
- color='grey'
- padding='xs md'
- @click='onDialogCancel'
- )
- q-btn(
- unelevated
- :label='t(`common.actions.update`)'
- color='primary'
- padding='xs md'
- @click='save'
- :loading='state.isLoading'
- )
+ q-icon(name='img:/_assets/icons/fluent-fingerprint.svg', left, size='sm')
+ span {{t(`profile.authSetTfa`)}}
+ template(v-if='!state.isInit')
+ q-linear-progress(query, color='positive')
+ q-card-section.text-center.text-grey {{t(`profile.authSetTfaLoading`)}}
+ template(v-else)
+ q-card-section.text-center
+ p {{t('auth.tfaSetupInstrFirst')}}
+ div(style='justify-content: center; display: flex;')
+ div(v-html='state.tfaQRImage', style='width: 200px;')
+ p.q-mt-sm {{t('auth.tfaSetupInstrSecond')}}
+ .flex.justify-center
+ v-otp-input(
+ v-model:value='state.securityCode'
+ :num-inputs='6'
+ :should-auto-focus='true'
+ input-classes='otp-input'
+ input-type='number'
+ separator=''
+ )
+ q-inner-loading(:showing='state.isLoading')
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`auth.tfa.verifyToken`)'
+ color='primary'
+ padding='xs md'
+ @click='save'
+ :loading='state.isLoading'
+ )
+
+
diff --git a/ux/src/helpers/localization.js b/ux/src/helpers/localization.js
new file mode 100644
index 0000000000..8f127d5142
--- /dev/null
+++ b/ux/src/helpers/localization.js
@@ -0,0 +1,13 @@
+/**
+ * Parse an error message for an error code and translate
+ *
+ * @param {String} val Value to parse
+ * @param {Function} t vue-i18n translation method
+ */
+export function localizeError (val, t) {
+ if (val?.startsWith('ERR_')) {
+ return t(`error.${val}`)
+ } else {
+ return val
+ }
+}
diff --git a/ux/src/pages/AdminSites.vue b/ux/src/pages/AdminSites.vue
index 6cb4fd478a..4b2ec80861 100644
--- a/ux/src/pages/AdminSites.vue
+++ b/ux/src/pages/AdminSites.vue
@@ -98,6 +98,7 @@ q-page.admin-locale
icon='las la-trash'
color='negative'
@click='deleteSite(site)'
+ :aria-label='t(`common.actions.delete`)'
)
diff --git a/ux/src/pages/ProfileAuth.vue b/ux/src/pages/ProfileAuth.vue
index 5ff02a6930..2b59584cfd 100644
--- a/ux/src/pages/ProfileAuth.vue
+++ b/ux/src/pages/ProfileAuth.vue
@@ -47,6 +47,46 @@ q-page.q-py-md(:style-fn='pageStyle')
@click='changePassword(auth.authId)'
)
+ .text-header.q-mt-md {{t('profile.passkeys')}}
+ .q-pa-md
+ .text-body2 {{ t('profile.passkeysIntro') }}
+ q-list.q-mt-lg(
+ v-if="state.passkeys?.length > 0"
+ bordered
+ separator
+ )
+ q-item(
+ v-for='pkey of state.passkeys'
+ :key='pkey.id'
+ )
+ q-item-section(avatar)
+ q-avatar(
+ color='secondary'
+ text-color='white'
+ rounded
+ )
+ q-icon(name='las la-key')
+ q-item-section
+ strong {{pkey.name}}
+ .text-caption {{ pkey.siteHostname }}
+ .text-caption.text-grey-7 {{ humanizeDate(pkey.createdAt) }}
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-trash'
+ :aria-label='t(`common.actions.delete`)'
+ color='negative'
+ @click='deactivatePasskey(pkey)'
+ )
+ .q-mt-md
+ q-btn(
+ icon='las la-plus'
+ unelevated
+ :label='t(`profile.passkeysAdd`)'
+ color='primary'
+ @click='setupPasskey'
+ )
+
q-inner-loading(:showing='state.loading > 0')
@@ -55,11 +95,16 @@ import gql from 'graphql-tag'
import { useI18n } from 'vue-i18n'
import { useMeta, useQuasar } from 'quasar'
import { onMounted, reactive } from 'vue'
+import { browserSupportsWebAuthn, startRegistration } from '@simplewebauthn/browser'
+import { localizeError } from 'src/helpers/localization'
+import { DateTime } from 'luxon'
+import { useSiteStore } from 'src/stores/site'
import { useUserStore } from 'src/stores/user'
import ChangePwdDialog from 'src/components/ChangePwdDialog.vue'
import SetupTfaDialog from 'src/components/SetupTfaDialog.vue'
+import PasskeyCreateDialog from 'src/components/PasskeyCreateDialog.vue'
// QUASAR
@@ -67,6 +112,7 @@ const $q = useQuasar()
// STORES
+const siteStore = useSiteStore()
const userStore = useUserStore()
// I18N
@@ -83,6 +129,7 @@ useMeta({
const state = reactive({
authMethods: [],
+ passkeys: [],
loading: 0
})
@@ -94,6 +141,10 @@ function pageStyle (offset, height) {
}
}
+function humanizeDate (val) {
+ return DateTime.fromISO(val).toLocaleString(DateTime.DATETIME_MED)
+}
+
async function fetchAuthMethods () {
state.loading++
try {
@@ -113,6 +164,12 @@ async function fetchAuthMethods () {
strategyIcon
config
}
+ passkeys {
+ id
+ name
+ createdAt
+ siteHostname
+ }
}
}
`,
@@ -122,6 +179,7 @@ async function fetchAuthMethods () {
fetchPolicy: 'network-only'
})
state.authMethods = respRaw.data?.userById?.auth ?? []
+ state.passkeys = respRaw.data?.userById?.passkeys ?? []
} catch (err) {
$q.notify({
type: 'negative',
@@ -189,12 +247,166 @@ function disableTfa (strategyId) {
}
function setupTfa (strategyId) {
- // $q.dialog({
- // component: SetupTfaDialog,
- // componentProps: {
- // strategyId
- // }
- // })
+ $q.dialog({
+ component: SetupTfaDialog,
+ componentProps: {
+ strategyId
+ }
+ }).onOk(() => {
+ fetchAuthMethods()
+ })
+}
+
+async function setupPasskey () {
+ try {
+ if (!browserSupportsWebAuthn()) {
+ throw new Error(t('profile.passkeysUnsupported'))
+ }
+ $q.loading.show()
+
+ // -> Generation registration options
+
+ const genResp = await APOLLO_CLIENT.mutate({
+ mutation: gql`
+ mutation setupPasskey (
+ $siteId: UUID!
+ ) {
+ setupPasskey(
+ siteId: $siteId
+ ) {
+ operation {
+ succeeded
+ message
+ }
+ registrationOptions
+ }
+ }
+ `,
+ variables: {
+ siteId: siteStore.id
+ }
+ })
+ if (genResp?.data?.setupPasskey?.operation?.succeeded) {
+ state.registrationOptions = genResp.data.setupPasskey.registrationOptions
+ } else {
+ throw new Error(localizeError(genResp?.data?.setupPasskey?.operation?.message, t))
+ }
+
+ // -> Start registration on the authenticator
+
+ let attResp
+ try {
+ attResp = await startRegistration(state.registrationOptions)
+ } catch (err) {
+ if (err.name === 'InvalidStateError') {
+ throw new Error(t('error.ERR_PK_ALREADY_REGISTERED'))
+ } else {
+ throw err
+ }
+ }
+
+ // -> Prompt for passkey name
+
+ $q.loading.hide()
+ const passkeyName = await new Promise((resolve, reject) => {
+ $q.dialog({
+ component: PasskeyCreateDialog
+ }).onOk(({ name }) => {
+ resolve(name)
+ }).onCancel(() => {
+ reject(new Error(t('error.ERR_PK_USER_CANCELLED')))
+ })
+ })
+ $q.loading.show()
+
+ // -> Verify the authenticator response
+
+ const resp = await APOLLO_CLIENT.mutate({
+ mutation: gql`
+ mutation finalizePasskey (
+ $registrationResponse: JSON!
+ $name: String!
+ ) {
+ finalizePasskey(
+ registrationResponse: $registrationResponse
+ name: $name
+ ) {
+ operation {
+ succeeded
+ message
+ }
+ }
+ }
+ `,
+ variables: {
+ registrationResponse: attResp,
+ name: passkeyName
+ }
+ })
+ if (resp?.data?.finalizePasskey?.operation?.succeeded) {
+ $q.notify({
+ type: 'positive',
+ message: t('profile.passkeysSetupSuccess')
+ })
+ } else {
+ throw new Error(resp?.data?.finalizePasskey?.operation?.message)
+ }
+ } catch (err) {
+ $q.notify({
+ type: 'negative',
+ message: t('profile.passkeysSetupFailed'),
+ caption: err.message ?? 'An unexpected error occured.'
+ })
+ }
+ await fetchAuthMethods()
+ $q.loading.hide()
+}
+
+async function deactivatePasskey (pkey) {
+ $q.dialog({
+ title: t('common.actions.confirm'),
+ message: t('profile.passkeysDeactivateConfirm'),
+ cancel: true
+ }).onOk(async () => {
+ $q.loading.show()
+ try {
+ const resp = await APOLLO_CLIENT.mutate({
+ mutation: gql`
+ mutation deactivatePasskey (
+ $id: UUID!
+ ) {
+ deactivatePasskey(
+ id: $id
+ ) {
+ operation {
+ succeeded
+ message
+ }
+ }
+ }
+ `,
+ variables: {
+ id: pkey.id
+ }
+ })
+ if (resp?.data?.deactivatePasskey?.operation?.succeeded) {
+ $q.notify({
+ type: 'positive',
+ message: t('profile.passkeysDeactivateSuccess')
+ })
+ } else {
+ throw new Error(resp?.data?.deactivatePasskey?.operation?.message)
+ }
+ } catch (err) {
+ $q.notify({
+ type: 'negative',
+ message: t('profile.passkeysDeactivateFailed'),
+ caption: err.message ?? 'An unexpected error occured.'
+ })
+ }
+ await fetchAuthMethods()
+ $q.loading.hide()
+ })
}
// MOUNTED