diff --git a/config/identity/handler/provider-factory/identity.json b/config/identity/handler/provider-factory/identity.json index f28dd740a..ede7553f8 100644 --- a/config/identity/handler/provider-factory/identity.json +++ b/config/identity/handler/provider-factory/identity.json @@ -2,7 +2,10 @@ "@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^1.0.0/components/context.jsonld", "@graph": [ { - "comment": "Sets all the relevant oidc parameters.", + "comment": [ + "Sets all the relevant oidc parameters.", + "webid claim is in openid scope until an official scope has been decided: https://github.com/solid/authentication-panel/issues/86" + ], "@id": "urn:solid-server:default:IdentityProviderFactory", "@type": "IdentityProviderFactory", "args_adapterFactory": { "@id": "urn:solid-server:default:IdpAdapterFactory" }, @@ -13,7 +16,7 @@ "args_responseWriter": { "@id": "urn:solid-server:default:ResponseWriter" }, "config": { "claims": { - "webid": [ "webid", "client_id" ] + "openid": [ "webid", "client_id" ] }, "cookies": { "long": { "signed": true, "maxAge": 86400000 }, @@ -33,6 +36,7 @@ "formats": { "AccessToken": "jwt" }, + "scopes": [ "openid", "profile", "offline_access" ], "ttl": { "AccessToken": 3600, "AuthorizationCode": 600, diff --git a/src/identity/configuration/IdentityProviderFactory.ts b/src/identity/configuration/IdentityProviderFactory.ts index 472d97702..10ca55b6a 100644 --- a/src/identity/configuration/IdentityProviderFactory.ts +++ b/src/identity/configuration/IdentityProviderFactory.ts @@ -120,10 +120,6 @@ export class IdentityProviderFactory implements ProviderFactory { return factory.createStorageAdapter(name); }; - // This needs to be a function, can't have a static string - // Sets the "aud" value to "solid" for all tokens - config.audiences = (): string => 'solid'; - // Cast necessary due to typing conflict between jose 2.x and 3.x config.jwks = await this.generateJwks() as any; config.cookies = { @@ -175,22 +171,36 @@ export class IdentityProviderFactory implements ProviderFactory { return newCookieSecret; } + /** + * Checks if the given token is an access token. + * The AccessToken interface is not exported so we have to access it like this. + */ + private isAccessToken(token: any): token is KoaContextWithOIDC['oidc']['accessToken'] { + return token.kind === 'AccessToken'; + } + /** * Adds the necessary claims the to id token and access token based on the Solid OIDC spec. */ private configureClaims(config: Configuration): void { - // Returns the id_token and adds the webid claim + // Access token audience is 'solid', ID token audience is the client_id + config.audiences = (ctx, sub, token, use): string => + use === 'access_token' ? 'solid' : token.clientId!; + + // Returns the id_token + // See https://solid.github.io/authentication-panel/solid-oidc/#tokens-id config.findAccount = async(ctx: KoaContextWithOIDC, sub: string): Promise => ({ accountId: sub, - claims: async(): Promise<{ sub: string; [key: string]: any }> => ({ sub, webid: sub }), + claims: async(): Promise<{ sub: string; [key: string]: any }> => + ({ sub, webid: sub }), }); // Add extra claims in case an AccessToken is being issued. // Specifically this sets the required webid and client_id claims for the access token + // See https://solid.github.io/authentication-panel/solid-oidc/#tokens-access config.extraAccessTokenClaims = (ctx, token): CanBePromise => - // AccessToken interface is not exported - (token as any).accountId ? - { webid: (token as any).accountId, client_id: 'http://localhost:3001/' } : + this.isAccessToken(token) ? + { webid: token.accountId, client_id: token.clientId } : {}; } diff --git a/test/unit/identity/configuration/IdentityProviderFactory.test.ts b/test/unit/identity/configuration/IdentityProviderFactory.test.ts index 3181c7536..91c5ee733 100644 --- a/test/unit/identity/configuration/IdentityProviderFactory.test.ts +++ b/test/unit/identity/configuration/IdentityProviderFactory.test.ts @@ -94,18 +94,19 @@ describe('An IdentityProviderFactory', (): void => { expect(config.routes).toEqual(routes); expect((config.interactions?.url as any)()).toEqual('/idp/'); - expect((config.audiences as any)()).toBe('solid'); + expect((config.audiences as any)(null, null, {}, 'access_token')).toBe('solid'); + expect((config.audiences as any)(null, null, { clientId: 'clientId' }, 'client_credentials')).toBe('clientId'); - const findResult = await config.findAccount?.({} as any, webId); + const findResult = await config.findAccount?.({ oidc: { client: { clientId: 'clientId' }}} as any, webId); expect(findResult?.accountId).toBe(webId); await expect((findResult?.claims as any)()).resolves.toEqual({ sub: webId, webid: webId }); expect((config.extraAccessTokenClaims as any)({}, {})).toEqual({}); - expect((config.extraAccessTokenClaims as any)({}, { accountId: webId })).toEqual({ - webid: webId, - // This will need to change once #718 is fixed - client_id: 'http://localhost:3001/', - }); + expect((config.extraAccessTokenClaims as any)({}, { kind: 'AccessToken', accountId: webId, clientId: 'clientId' })) + .toEqual({ + webid: webId, + client_id: 'clientId', + }); // Test the renderError function const response = { } as HttpResponse;