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:
parent
d3e7cff0bd
commit
fcda26ac2f
6 changed files with 39 additions and 21 deletions
27
packages/core/src/oidc/grants/index.ts
Normal file
27
packages/core/src/oidc/grants/index.ts
Normal 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
|
||||||
|
);
|
||||||
|
};
|
|
@ -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))
|
||||||
|
|
|
@ -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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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'],
|
||||||
}}
|
}}
|
||||||
|
|
Loading…
Reference in a new issue