const urlJoin = require('url-join'); const CONFIG = require('../config/config'); // Serves the app actor at its canonical uri (`/as/actor/app`) with a Solid-OIDC dual // personality, discriminated by the request's `JsonLdContext` header — same contract as // the `/app` route (see app.service.js for the full story), but implemented as a // standalone service with no dependency on the `app` service. // // WHY A SEPARATE SERVICE: the Solid-OIDC client_id IS the actor uri (the pod provider's // auth-agent only returns the `Link: rel="interop:registeredAgent"` header when the // requesting app's identity — the token's azp, i.e. this client_id — matches the // registration's `registeredAgent`, which the pod stores under the actor uri). The // adapter therefore fetches THIS path as the client metadata document. Routing the path // to `app.get` deadlocked boot: `@activitypods/app`'s `awaitCreateComplete` polls the // actor over HTTP while the `app` service is still starting, and an unregistered action // 503s the poll it waits on. This service has no `started()` work and no dependencies, // so it is routable long before the `app` service exists. module.exports = { name: 'client-id-doc', actions: { get: { async handler(ctx) { const req = ctx.params && ctx.params.$req; const headers = (req && req.headers) || {}; const jsonLdContextKey = Object.keys(headers).find(key => key.toLowerCase() === 'jsonldcontext'); const isOidcClientIdDocument = jsonLdContextKey !== undefined && String(headers[jsonLdContextKey]).includes('solid/oidc-context.jsonld'); if (isOidcClientIdDocument) { // The client_id must equal the fetched URI exactly. `req.url` is rewritten to // the alias remainder by moleculer-web; the raw path survives in `originalUrl`. const requestPath = String((req && (req.originalUrl || req.url)) || '/as/actor/app').split('?')[0]; return { '@context': ['https://www.w3.org/ns/solid/oidc-context.jsonld'], client_id: urlJoin(CONFIG.HOME_URL, requestPath), redirect_uris: [urlJoin(CONFIG.FRONT_URL, 'login')], client_name: CONFIG.APP_NAME, client_uri: CONFIG.FRONT_URL }; } return await ctx.call('ldp.resource.get', { resourceUri: urlJoin(CONFIG.HOME_URL, 'as/actor/app'), accept: 'application/ld+json', webId: 'system' }); } } } };