Close the login loop end-to-end: client_id is the app actor uri, actor path serves the OIDC client document via a boot-safe service, startup heals the access-need-group link, stack aligned on pod provider v2.3.12
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
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'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user