0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

refactor(core): fix resource parameter

This commit is contained in:
Gao Sun 2023-11-10 14:59:48 +08:00
parent d3e7cff0bd
commit fcda26ac2f
No known key found for this signature in database
GPG key ID: 13EBE123E4773688
6 changed files with 39 additions and 21 deletions

View file

@ -0,0 +1,27 @@
import { GrantType } from '@logto/schemas';
import type Provider from 'oidc-provider';
import instance from 'oidc-provider/lib/helpers/weak_cache.js';
import { type EnvSet } from '#src/env-set/index.js';
import type Queries from '#src/tenants/Queries.js';
import * as refreshToken from './refresh-token.js';
export const registerGrants = (oidc: Provider, envSet: EnvSet, queries: Queries) => {
const {
features: { resourceIndicators },
} = instance(oidc).configuration();
// If resource indicators are enabled, append `resource` to the parameters and allow it to
// be duplicated
const parameterConfig: [parameters: string[], duplicates: string[]] = resourceIndicators.enabled
? [[...refreshToken.parameters, 'resource'], ['resource']]
: [[...refreshToken.parameters], []];
// Override the default `refresh_token` grant
oidc.registerGrantType(
GrantType.RefreshToken,
refreshToken.buildHandler(envSet, queries.organizations),
...parameterConfig
);
};

View file

@ -20,7 +20,7 @@
*/ */
import { UserScope, buildOrganizationUrn } from '@logto/core-kit'; import { UserScope, buildOrganizationUrn } from '@logto/core-kit';
import { type Optional, cond, isKeyInObject } from '@silverhand/essentials'; import { type Optional, isKeyInObject, cond } from '@silverhand/essentials';
import type Provider from 'oidc-provider'; import type Provider from 'oidc-provider';
import { errors } from 'oidc-provider'; import { errors } from 'oidc-provider';
import difference from 'oidc-provider/lib/helpers/_/difference.js'; import difference from 'oidc-provider/lib/helpers/_/difference.js';
@ -53,7 +53,7 @@ const gty = 'refresh_token';
/** /**
* The valid parameters for the `organization_token` grant type. Note the `resource` parameter is * The valid parameters for the `organization_token` grant type. Note the `resource` parameter is
* handled by oidc-provider so we don't need to include it here. * not included here since it should be handled per configuration when registering the grant type.
*/ */
export const parameters = Object.freeze(['refresh_token', 'organization_id', 'scope'] as const); export const parameters = Object.freeze(['refresh_token', 'organization_id', 'scope'] as const);
@ -130,7 +130,9 @@ export const buildHandler: (
} }
/* === RFC 0001 === */ /* === RFC 0001 === */
if (params.organization_id) { // The params object may have the key with `undefined` value, so we have to use `Boolean` to check.
const organizationId = cond(Boolean(params.organization_id) && String(params.organization_id));
if (organizationId) {
// Validate if the refresh token has the required scope from RFC 0001. // Validate if the refresh token has the required scope from RFC 0001.
if (!refreshToken.scopes.has(UserScope.Organizations)) { if (!refreshToken.scopes.has(UserScope.Organizations)) {
throw new InsufficientScope('refresh token missing required scope', UserScope.Organizations); throw new InsufficientScope('refresh token missing required scope', UserScope.Organizations);
@ -218,7 +220,6 @@ export const buildHandler: (
/* === RFC 0001 === */ /* === RFC 0001 === */
// Check membership // Check membership
const organizationId = cond(Boolean(params.organization_id) && String(params.organization_id));
if ( if (
organizationId && organizationId &&
!(await queries.relations.users.exists(organizationId, account.accountId)) !(await queries.relations.users.exists(organizationId, account.accountId))

View file

@ -7,6 +7,6 @@ describe('oidc provider init', () => {
it('init should not throw', async () => { it('init should not throw', async () => {
const { queries, libraries } = new MockTenant(); const { queries, libraries } = new MockTenant();
expect(() => initOidc('mock_id', mockEnvSet, queries, libraries)).not.toThrow(); expect(() => initOidc(mockEnvSet, queries, libraries)).not.toThrow();
}); });
}); });

View file

@ -8,7 +8,6 @@ import {
customClientMetadataDefault, customClientMetadataDefault,
CustomClientMetadataKey, CustomClientMetadataKey,
demoAppApplicationId, demoAppApplicationId,
GrantType,
inSeconds, inSeconds,
logtoCookieKey, logtoCookieKey,
type LogtoUiCookie, type LogtoUiCookie,
@ -31,7 +30,7 @@ import type Libraries from '#src/tenants/Libraries.js';
import type Queries from '#src/tenants/Queries.js'; import type Queries from '#src/tenants/Queries.js';
import defaults from './defaults.js'; import defaults from './defaults.js';
import * as refreshToken from './grants/refresh-token.js'; import { registerGrants } from './grants/index.js';
import { findResource, findResourceScopes, getSharedResourceServerData } from './resource.js'; import { findResource, findResourceScopes, getSharedResourceServerData } from './resource.js';
import { getUserClaimData, getUserClaims } from './scope.js'; import { getUserClaimData, getUserClaims } from './scope.js';
import { OIDCExtraParametersKey, InteractionMode } from './type.js'; import { OIDCExtraParametersKey, InteractionMode } from './type.js';
@ -39,12 +38,7 @@ import { OIDCExtraParametersKey, InteractionMode } from './type.js';
// Temporarily removed 'EdDSA' since it's not supported by browser yet // Temporarily removed 'EdDSA' since it's not supported by browser yet
const supportedSigningAlgs = Object.freeze(['RS256', 'PS256', 'ES256', 'ES384', 'ES512'] as const); const supportedSigningAlgs = Object.freeze(['RS256', 'PS256', 'ES256', 'ES384', 'ES512'] as const);
export default function initOidc( export default function initOidc(envSet: EnvSet, queries: Queries, libraries: Libraries): Provider {
tenantId: string,
envSet: EnvSet,
queries: Queries,
libraries: Libraries
): Provider {
const { const {
resources: { findDefaultResource }, resources: { findDefaultResource },
users: { findUserById }, users: { findUserById },
@ -287,11 +281,7 @@ export default function initOidc(
}); });
addOidcEventListeners(oidc, queries); addOidcEventListeners(oidc, queries);
registerGrants(oidc, envSet, queries);
// Override the default `refresh_token` grant
oidc.registerGrantType(GrantType.RefreshToken, refreshToken.buildHandler(envSet, organizations), [
...refreshToken.parameters,
]);
// Provide audit log context for event listeners // Provide audit log context for event listeners
oidc.use(koaAuditLog(queries)); oidc.use(koaAuditLog(queries));

View file

@ -83,7 +83,7 @@ export default class Tenant implements TenantContext {
app.use(koaSecurityHeaders(mountedApps, id)); app.use(koaSecurityHeaders(mountedApps, id));
// Mount OIDC // Mount OIDC
const provider = initOidc(id, envSet, queries, libraries); const provider = initOidc(envSet, queries, libraries);
app.use(mount('/oidc', provider.app)); app.use(mount('/oidc', provider.app));
const tenantContext: TenantContext = { const tenantContext: TenantContext = {

View file

@ -101,8 +101,8 @@ const App = () => {
config={{ config={{
endpoint: window.location.origin, endpoint: window.location.origin,
appId: demoAppApplicationId, appId: demoAppApplicationId,
prompt: Prompt.Consent, prompt: Prompt.Login,
// Use enum values once JS SDK is updated // TODO: Use enum values once JS SDK is updated
scopes: ['urn:logto:scope:organizations', 'urn:logto:scope:organization_roles'], scopes: ['urn:logto:scope:organizations', 'urn:logto:scope:organization_roles'],
resources: ['urn:logto:resource:organizations'], resources: ['urn:logto:resource:organizations'],
}} }}