diff --git a/packages/backend/package.json b/packages/backend/package.json index 6de4e634f..34d4b206a 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -117,6 +117,7 @@ "nodemailer": "6.9.3", "nsfwjs": "2.4.2", "oauth": "0.10.0", + "oidc-provider": "^8.1.1", "os-utils": "0.0.14", "otpauth": "9.1.2", "parse5": "7.1.2", diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts index da86b2c1d..5d80eb4e0 100644 --- a/packages/backend/src/server/ServerModule.ts +++ b/packages/backend/src/server/ServerModule.ts @@ -36,6 +36,7 @@ import { UserListChannelService } from './api/stream/channels/user-list.js'; import { OpenApiServerService } from './api/openapi/OpenApiServerService.js'; import { ClientLoggerService } from './web/ClientLoggerService.js'; import { RoleTimelineChannelService } from './api/stream/channels/role-timeline.js'; +import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js'; @Module({ imports: [ @@ -78,6 +79,7 @@ import { RoleTimelineChannelService } from './api/stream/channels/role-timeline. ServerStatsChannelService, UserListChannelService, OpenApiServerService, + OAuth2ProviderService, ], exports: [ ServerService, diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index c3d45e4ad..2ef074cb5 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -24,6 +24,7 @@ import { WellKnownServerService } from './WellKnownServerService.js'; import { FileServerService } from './FileServerService.js'; import { ClientServerService } from './web/ClientServerService.js'; import { OpenApiServerService } from './api/openapi/OpenApiServerService.js'; +import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js'; const _dirname = fileURLToPath(new URL('.', import.meta.url)); @@ -56,12 +57,13 @@ export class ServerService implements OnApplicationShutdown { private clientServerService: ClientServerService, private globalEventService: GlobalEventService, private loggerService: LoggerService, + private oauth2ProviderService: OAuth2ProviderService, ) { this.logger = this.loggerService.getLogger('server', 'gray', false); } @bindThis - public async launch() { + public async launch(): Promise { const fastify = Fastify({ trustProxy: true, logger: !['production', 'test'].includes(process.env.NODE_ENV ?? ''), @@ -90,6 +92,8 @@ export class ServerService implements OnApplicationShutdown { fastify.register(this.activityPubServerService.createServer); fastify.register(this.nodeinfoServerService.createServer); fastify.register(this.wellKnownServerService.createServer); + fastify.register(this.oauth2ProviderService.createServerWildcard); + fastify.register(this.oauth2ProviderService.createServer); fastify.get<{ Params: { path: string }; Querystring: { static?: any; badge?: any; }; }>('/emoji/:path(.*)', async (request, reply) => { const path = request.params.path; diff --git a/packages/backend/src/server/oauth/OAuth2ProviderService.ts b/packages/backend/src/server/oauth/OAuth2ProviderService.ts new file mode 100644 index 000000000..347b14753 --- /dev/null +++ b/packages/backend/src/server/oauth/OAuth2ProviderService.ts @@ -0,0 +1,216 @@ +import dns from 'node:dns/promises'; +import { Inject, Injectable } from '@nestjs/common'; +import Provider, { type Adapter, type Account, AdapterPayload } from 'oidc-provider'; +import fastifyMiddie from '@fastify/middie'; +import { JSDOM } from 'jsdom'; +import parseLinkHeader from 'parse-link-header'; +import ipaddr from 'ipaddr.js'; +import { bindThis } from '@/decorators.js'; +import { DI } from '@/di-symbols.js'; +import type { Config } from '@/config.js'; +import { kinds } from '@/misc/api-permissions.js'; +import { HttpRequestService } from '@/core/HttpRequestService.js'; +import type { FastifyInstance } from 'fastify'; + + +// TODO: For now let's focus on letting oidc-provider use the existing miauth infra. +// Supporting IndieAuth is a separate project. +// Allow client_id created by apps/create or not? It's already marked as old method. + +// https://indieauth.spec.indieweb.org/#client-identifier +function validateClientId(raw: string): URL { + // Clients are identified by a [URL]. + const url = ((): URL => { + try { + return new URL(raw); + } catch { throw new Error('client_id must be a valid URL'); } + })(); + + // Client identifier URLs MUST have either an https or http scheme + if (!['http:', 'https:'].includes(url.protocol)) { + throw new Error('client_id must be either https or http URL'); + } + + // MUST contain a path component (new URL() implicitly adds one) + + // MUST NOT contain single-dot or double-dot path segments, + // url. + const segments = url.pathname.split('/'); + if (segments.includes('.') || segments.includes('..')) { + throw new Error('client_id must not contain dot path segments'); + } + + // MUST NOT contain a fragment component + if (url.hash) { + throw new Error('client_id must not contain a fragment component'); + } + + // MUST NOT contain a username or password component + if (url.username || url.password) { + throw new Error('client_id must not contain a username or a password'); + } + + // MUST NOT contain a port + if (url.port) { + throw new Error('client_id must not contain a port'); + } + + // host names MUST be domain names or a loopback interface and MUST NOT be + // IPv4 or IPv6 addresses except for IPv4 127.0.0.1 or IPv6 [::1]. + // (But in https://indieauth.spec.indieweb.org/#redirect-url we need to only + // fetch non-loopback URLs, so exclude them here.) + if (!url.hostname.match(/\.\w+$/)) { + throw new Error('client_id must have a domain name as a host name'); + } + + return url; +} + +async function fetchFromClientId(httpRequestService: HttpRequestService, id: string): Promise { + try { + const res = await httpRequestService.send(id); + let redirectUri = parseLinkHeader(res.headers.get('link'))?.redirect_uri?.url; + if (redirectUri) { + return new URL(redirectUri, res.url).toString(); + } + + const { window } = new JSDOM(await res.text()); + redirectUri = window.document.querySelector('link[rel=redirect_uri][href]')?.href; + if (redirectUri) { + return new URL(redirectUri, res.url).toString(); + } + } catch { + throw new Error('Failed to fetch client information'); + } +} + +class MisskeyAdapter implements Adapter { + constructor(private httpRequestService: HttpRequestService) { } + + upsert(id: string, payload: AdapterPayload, expiresIn: number): Promise { + console.log('oauth upsert', id, payload, expiresIn); + throw new Error('Method not implemented.'); + } + async find(id: string): Promise { + // Find client information from the remote. + + console.log('oauth find', id); + const url = validateClientId(id); + + if (process.env.NODE_ENV !== 'test') { + const lookup = await dns.lookup(url.hostname); + if (ipaddr.parse(lookup.address).range() === 'loopback') { + throw new Error('client_id unexpectedly resolves to loopback IP.'); + } + } + + const redirectUri = await fetchFromClientId(this.httpRequestService, id); + if (!redirectUri) { + // IndieAuth also implicitly allows any path under the same scheme+host, + // but oidc-provider does not have such option. + throw new Error('The URL of client_id must provide `redirect_uri` as HTTP Link header or HTML element.'); + } + + return { + client_id: id, + token_endpoint_auth_method: 'none', + redirect_uris: [redirectUri], + }; + } + async findByUserCode(userCode: string): Promise { + console.log('oauth findByUserCode', userCode); + throw new Error('Method not implemented.'); + } + async findByUid(uid: string): Promise { + console.log('oauth findByUid', uid); + throw new Error('Method not implemented.'); + } + async consume(id: string): Promise { + console.log('oauth consume', id); + throw new Error('Method not implemented.'); + } + async destroy(id: string): Promise { + console.log('oauth destroy', id); + throw new Error('Method not implemented.'); + } + async revokeByGrantId(grantId: string): Promise { + console.log('oauth revokeByGrandId', grantId); + throw new Error('Method not implemented.'); + } +} + +@Injectable() +export class OAuth2ProviderService { + #provider: Provider; + + constructor( + @Inject(DI.config) + private config: Config, + httpRequestService: HttpRequestService, + ) { + this.#provider = new Provider(config.url, { + clientAuthMethods: ['none'], + pkce: { + // This is the default, but be explicit here as we announce it below + methods: ['S256'], + }, + routes: { + // defaults to '/auth' but '/authorize' is more consistent with many + // other services eg. Mastodon/Twitter/Facebook/GitLab/GitHub/etc. + authorization: '/authorize', + }, + scopes: kinds, + async findAccount(ctx, id): Promise { + console.log(id); + return undefined; + }, + adapter(): MisskeyAdapter { + return new MisskeyAdapter(httpRequestService); + }, + async renderError(ctx, out, error): Promise { + console.log(error); + }, + }); + } + + // Return 404 for any unknown paths under /oauth so that clients can know + // certain endpoints are unsupported. + // Registering separately because otherwise fastify.use() will match the + // wildcard too. + @bindThis + public async createServerWildcard(fastify: FastifyInstance): Promise { + fastify.all('/oauth/*', async (_request, reply) => { + reply.code(404); + reply.send({ + error: { + message: 'Unknown OAuth endpoint.', + code: 'UNKNOWN_OAUTH_ENDPOINT', + id: 'aa49e620-26cb-4e28-aad6-8cbcb58db147', + kind: 'client', + }, + }); + }); + } + + @bindThis + public async createServer(fastify: FastifyInstance): Promise { + fastify.get('/.well-known/oauth-authorization-server', async (_request, reply) => { + reply.send({ + issuer: this.config.url, + authorization_endpoint: new URL('/oauth/authorize', this.config.url), + token_endpoint: new URL('/oauth/token', this.config.url), + code_challenge_methods_supported: ['S256'], + }); + }); + + // oidc-provider provides many more endpoints for OpenID support and there's + // no way to turn it off. + // For now only allow the basic OAuth endpoints, to start small and evaluate + // this feature for some time, given that this is security related. + fastify.get('/oauth/authorize', async () => { }); + fastify.post('/oauth/token', async () => { }); + + await fastify.register(fastifyMiddie); + fastify.use('/oauth', this.#provider.callback()); + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be9204ad7..af9c08e95 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -266,6 +266,9 @@ importers: oauth: specifier: 0.10.0 version: 0.10.0 + oidc-provider: + specifier: ^8.1.1 + version: 8.1.1 os-utils: specifier: 0.0.14 version: 0.0.14 @@ -5251,6 +5254,23 @@ packages: resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} dev: true + /@koa/cors@4.0.0: + resolution: {integrity: sha512-Y4RrbvGTlAaa04DBoPBWJqDR5gPj32OOz827ULXfgB1F7piD1MB/zwn8JR2LAnvdILhxUbXbkXGWuNVsFuVFCQ==} + engines: {node: '>= 14.0.0'} + dependencies: + vary: 1.1.2 + dev: false + + /@koa/router@12.0.0: + resolution: {integrity: sha512-cnnxeKHXlt7XARJptflGURdJaO+ITpNkOHmQu7NHmCoRinPbyvFzce/EG/E8Zy81yQ1W9MoSdtklc3nyaDReUw==} + engines: {node: '>= 12'} + dependencies: + http-errors: 2.0.0 + koa-compose: 4.1.0 + methods: 1.1.2 + path-to-regexp: 6.2.1 + dev: false + /@kurkle/color@0.3.2: resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==} dev: false @@ -9617,7 +9637,6 @@ packages: /bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} - dev: true /c8@7.13.0: resolution: {integrity: sha512-/NL4hQTv1gBL6J6ei80zu3IiTrmePDKXKXOTLpHvcIWZTVYQlDhVWjjWvkhICylE8EwwnMVzDZugCvdx0/DIIA==} @@ -9675,6 +9694,14 @@ packages: union-value: 1.0.1 unset-value: 1.0.0 + /cache-content-type@1.0.1: + resolution: {integrity: sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==} + engines: {node: '>= 6.0.0'} + dependencies: + mime-types: 2.1.35 + ylru: 1.3.2 + dev: false + /cacheable-lookup@5.0.4: resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} engines: {node: '>=10.6.0'} @@ -10164,7 +10191,6 @@ packages: /co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - dev: true /coa@1.0.4: resolution: {integrity: sha512-KAGck/eNAmCL0dcT3BiuYwLbExK6lduR8DxM3C1TyDzaXhZHyZ8ooX5I5+na2e3dPFuibfxrGdorr0/Lr7RYCQ==} @@ -10398,7 +10424,6 @@ packages: /content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} - dev: true /convert-source-map@1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} @@ -10420,6 +10445,14 @@ packages: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} + /cookies@0.8.0: + resolution: {integrity: sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==} + engines: {node: '>= 0.8'} + dependencies: + depd: 2.0.0 + keygrip: 1.1.0 + dev: false + /copy-descriptor@0.1.1: resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==} engines: {node: '>=0.10.0'} @@ -10806,6 +10839,10 @@ packages: type-detect: 4.0.8 dev: true + /deep-equal@1.0.1: + resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==} + dev: false + /deep-equal@2.2.0: resolution: {integrity: sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==} dependencies: @@ -10952,7 +10989,6 @@ packages: /destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - dev: true /detect-file@1.0.0: resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==} @@ -11132,7 +11168,6 @@ packages: /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - dev: true /ejs@3.1.8: resolution: {integrity: sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==} @@ -11167,7 +11202,6 @@ packages: /encodeurl@1.0.2: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} - dev: true /encoding@0.1.13: resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} @@ -11662,6 +11696,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /eta@2.0.1: + resolution: {integrity: sha512-46E2qDPDm7QA+usjffUWz9KfXsxVZclPOuKsXs4ZWZdI/X1wpDF7AO424pt7fdYohCzWsIkXAhNGXSlwo5naAg==} + engines: {node: '>=6.0.0'} + dev: false + /etag@1.8.1: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} @@ -12422,7 +12461,6 @@ packages: /fresh@0.5.2: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} - dev: true /from@0.1.7: resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} @@ -13235,9 +13273,28 @@ packages: domutils: 3.0.1 entities: 4.5.0 + /http-assert@1.5.0: + resolution: {integrity: sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==} + engines: {node: '>= 0.8'} + dependencies: + deep-equal: 1.0.1 + http-errors: 1.8.1 + dev: false + /http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + /http-errors@1.8.1: + resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} + engines: {node: '>= 0.6'} + dependencies: + depd: 1.1.2 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 1.5.0 + toidentifier: 1.0.1 + dev: false + /http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} @@ -13749,7 +13806,6 @@ packages: engines: {node: '>= 0.4'} dependencies: has-tostringtag: 1.0.0 - dev: true /is-glob@3.1.0: resolution: {integrity: sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==} @@ -14583,6 +14639,10 @@ packages: '@sideway/pinpoint': 2.0.0 dev: true + /jose@4.14.0: + resolution: {integrity: sha512-LSA/XenLPwqk6e2L+PSUNuuY9G4NGsvjRWz6sJcUBmzTLEPJqQh46FHSUxnAQ64AWOkRO6bSXpy3yXuEKZkbIA==} + dev: false + /jpeg-js@0.3.7: resolution: {integrity: sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ==} dev: false @@ -14734,6 +14794,12 @@ packages: hasBin: true dev: true + /jsesc@3.0.2: + resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} + engines: {node: '>=6'} + hasBin: true + dev: false + /json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -14873,6 +14939,13 @@ packages: safe-buffer: 5.2.1 dev: false + /keygrip@1.1.0: + resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} + engines: {node: '>= 0.6'} + dependencies: + tsscmp: 1.0.6 + dev: false + /keyv@4.5.2: resolution: {integrity: sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==} dependencies: @@ -14903,6 +14976,49 @@ packages: engines: {node: '>=6'} dev: true + /koa-compose@4.1.0: + resolution: {integrity: sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==} + dev: false + + /koa-convert@2.0.0: + resolution: {integrity: sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==} + engines: {node: '>= 10'} + dependencies: + co: 4.6.0 + koa-compose: 4.1.0 + dev: false + + /koa@2.14.2: + resolution: {integrity: sha512-VFI2bpJaodz6P7x2uyLiX6RLYpZmOJqNmoCst/Yyd7hQlszyPwG/I9CQJ63nOtKSxpt5M7NH67V6nJL2BwCl7g==} + engines: {node: ^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4} + dependencies: + accepts: 1.3.8 + cache-content-type: 1.0.1 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookies: 0.8.0 + debug: 4.3.4(supports-color@8.1.1) + delegates: 1.0.0 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + fresh: 0.5.2 + http-assert: 1.5.0 + http-errors: 1.8.1 + is-generator-function: 1.0.10 + koa-compose: 4.1.0 + koa-convert: 2.0.0 + on-finished: 2.4.1 + only: 0.0.2 + parseurl: 1.3.3 + statuses: 1.5.0 + type-is: 1.6.18 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + dev: false + /ky-universal@0.11.0(ky@0.33.3): resolution: {integrity: sha512-65KyweaWvk+uKKkCrfAf+xqN2/epw1IJDtlyCPxYffFCMR8u1sp2U65NtWpnozYfZxQ6IUzIlvUcw+hQ82U2Xw==} engines: {node: '>=14.16'} @@ -15377,7 +15493,6 @@ packages: /media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} - dev: true /meilisearch@0.33.0: resolution: {integrity: sha512-bYPb9WyITnJfzf92e7QFK8Rc50DmshFWxypXCs3ILlpNh8pT15A7KSu9Xgnnk/K3G/4vb3wkxxtFS4sxNkWB8w==} @@ -15425,7 +15540,6 @@ packages: /methods@1.1.2: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} - dev: true /mfm-js@0.23.3: resolution: {integrity: sha512-o8scYmbey6rMUmWAlT3k3ntt6khaCLdxlmHhAWV5wTTMj2OK1atQvZfRUq0SIVm1Jig08qlZg/ps71xUqrScNA==} @@ -15804,6 +15918,12 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + /nanoid@4.0.2: + resolution: {integrity: sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==} + engines: {node: ^14 || ^16 || >=18} + hasBin: true + dev: false + /nanomatch@1.2.13: resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==} engines: {node: '>=0.10.0'} @@ -16176,6 +16296,11 @@ packages: define-property: 0.2.5 kind-of: 3.2.2 + /object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + dev: false + /object-inspect@1.12.2: resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} dev: true @@ -16260,6 +16385,31 @@ packages: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} dev: true + /oidc-provider@8.1.1: + resolution: {integrity: sha512-fu7nUNFpfzyAmvFmgVsFxRTKB3GoxKLKDcfLDeUgP03PLctsnKOzKY/ot4Pm2/ahPOGBNcpVQi670bPhD8X/NA==} + dependencies: + '@koa/cors': 4.0.0 + '@koa/router': 12.0.0 + debug: 4.3.4(supports-color@8.1.1) + eta: 2.0.1 + got: 12.6.0 + jose: 4.14.0 + jsesc: 3.0.2 + koa: 2.14.2 + nanoid: 4.0.2 + object-hash: 3.0.0 + oidc-token-hash: 5.0.2 + quick-lru: 6.1.1 + raw-body: 2.5.2 + transitivePeerDependencies: + - supports-color + dev: false + + /oidc-token-hash@5.0.2: + resolution: {integrity: sha512-U91Ba78GtVBxcExLI7U+hC2AwJQqXQEW/D3fjmJC4hhSVIgdl954KO4Gu95WqAlgDKJdLATxkmuxraWLT0fVRQ==} + engines: {node: ^10.13.0 || >=12.0.0} + dev: false + /omggif@1.0.10: resolution: {integrity: sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==} dev: false @@ -16273,7 +16423,6 @@ packages: engines: {node: '>= 0.8'} dependencies: ee-first: 1.1.1 - dev: true /on-headers@1.0.2: resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} @@ -16298,6 +16447,10 @@ packages: mimic-fn: 4.0.0 dev: true + /only@0.0.2: + resolution: {integrity: sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==} + dev: false + /open@7.4.2: resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} engines: {node: '>=8'} @@ -16583,7 +16736,6 @@ packages: /parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} - dev: true /pascalcase@0.1.1: resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==} @@ -16666,7 +16818,6 @@ packages: /path-to-regexp@6.2.1: resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==} - dev: true /path-type@1.1.0: resolution: {integrity: sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==} @@ -17617,6 +17768,11 @@ packages: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} + /quick-lru@6.1.1: + resolution: {integrity: sha512-S27GBT+F0NTRiehtbrgaSE1idUAJ5bX8dPAQTdylEyNlrdcH5X4Lz7Edz3DYzecbsCluD5zO8ZNEe04z3D3u6Q==} + engines: {node: '>=12'} + dev: false + /ramda@0.28.0: resolution: {integrity: sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==} dev: true @@ -17647,6 +17803,16 @@ packages: unpipe: 1.0.0 dev: true + /raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + dev: false + /rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true @@ -19055,6 +19221,11 @@ packages: define-property: 0.2.5 object-copy: 0.1.0 + /statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + dev: false + /statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -19802,6 +19973,11 @@ packages: /tslib@2.5.3: resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==} + /tsscmp@1.0.6: + resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} + engines: {node: '>=0.6.x'} + dev: false + /tsutils@3.21.0(typescript@5.1.3): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} @@ -19883,7 +20059,6 @@ packages: dependencies: media-typer: 0.3.0 mime-types: 2.1.35 - dev: true /type@1.2.0: resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==} @@ -20163,7 +20338,6 @@ packages: /unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - dev: true /unplugin@0.10.2: resolution: {integrity: sha512-6rk7GUa4ICYjae5PrAllvcDeuT8pA9+j5J5EkxbMFaV+SalHhxZ7X2dohMzu6C3XzsMT+6jwR/+pwPNR3uK9MA==} @@ -21106,6 +21280,11 @@ packages: fd-slicer: 1.1.0 dev: true + /ylru@1.3.2: + resolution: {integrity: sha512-RXRJzMiK6U2ye0BlGGZnmpwJDPgakn6aNQ0A7gHRbD4I0uvK4TW6UqkK1V0pp9jskjJBAXd3dRrbzWkqJ+6cxA==} + engines: {node: '>= 4.0.0'} + dev: false + /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'}