738d59596a
The test deployment keeps one base URL per environment, as the sibling ActivityPods apps do, so a pull-request preview lives on a single host: the app at the root, the backend under /api on the same host, its /.well-known routes at the origin root, the proxy passing paths through unchanged. SemApps already works that way — it mounts every route it adds under the base URL's path and keeps /.well-known at the root — but Festipod's own pieces were written for a backend at the root of its own origin: its routes (/app, /as/actor/app, /catalog) were mounted at fixed paths, the public-surface allow-list matched fixed paths (so under /api every request would have been refused), and the Solid-OIDC client_id joined the raw request path to the base URL, which would have repeated the base path (/api/api/...). base-path.js derives the base path from SEMAPPS_HOME_URL; the routes are mounted under it; the allow-list matches paths relative to it, the two /.well-known endpoints against the full path; the client_id is the requested URI rebuilt from the base URL as written, equal byte for byte to the actor URI and to what the front end derives. At the root every gate decision is identical to before (compared over ~8M method/path cases). The rights matrix probes /.well-known at the backend's origin root. Verified: bun test (424 pass); the full gate at the root; and the built images behind a local proxy routing /api and /.well-known unstripped with the Host header kept — login flow, rights matrix 98/98, revocation.
53 lines
2.6 KiB
JavaScript
53 lines
2.6 KiB
JavaScript
const urlJoin = require('url-join');
|
|
const CONFIG = require('../config/config');
|
|
const { requestUri } = require('./base-path');
|
|
|
|
// 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, base path included, survives
|
|
// in `originalUrl`.
|
|
const requestPath = String((req && (req.originalUrl || req.url)) || '').split('?')[0];
|
|
return {
|
|
'@context': ['https://www.w3.org/ns/solid/oidc-context.jsonld'],
|
|
client_id: requestPath ? requestUri(CONFIG.HOME_URL, requestPath) : urlJoin(CONFIG.HOME_URL, 'as/actor/app'),
|
|
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'
|
|
});
|
|
}
|
|
}
|
|
}
|
|
};
|