0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

feat(core): support comma separated resource param (#5773)

This commit is contained in:
Charles Zhao 2024-04-23 15:11:39 +08:00 committed by GitHub
parent 9cf03c8edb
commit b575f57ac3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 92 additions and 0 deletions

View file

@ -0,0 +1,9 @@
---
"@logto/core": patch
---
Support comma separated resource parameter
Some third-party libraries or plugins do not support array of resources, and can only specify `resource` through `additionalParameters` config, e.g. `flutter-appauth`. However, only one resource can be specified at a time in this way. This PR enables comma separated resource parameter support in Logto core service, so that multiple resources can be specified via a single string.
For example: Auth URL like `/oidc/auth?resource=https://example.com/api1,https://example.com/api2` will be interpreted and parsed to Logto core service as `/ordc/auth?resource=https://example.com/api1&resource=https://example.com/api2`.

View file

@ -0,0 +1,45 @@
import createMockContext from '#src/test-utils/jest-koa-mocks/create-mock-context.js';
const { jest } = import.meta;
const { default: koaResourceParam } = await import('./koa-resource-param.js');
// eslint-disable-next-line @typescript-eslint/no-empty-function
const noop = async () => {};
const endpoint = 'https://logto.io/oidc/auth';
describe('koaResourceParam() middleware', () => {
it('should check and process comma separated resource params in the URL', async () => {
const ctx = createMockContext({ url: endpoint + '?resource=foo,bar' });
const run = koaResourceParam();
await run(ctx, noop);
expect(ctx.request.query).toEqual({
resource: ['foo', 'bar'],
});
});
it('should also work with both comma separated and single resource params', async () => {
const ctx = createMockContext({ url: endpoint + '?resource=foo,bar&resource=baz' });
const run = koaResourceParam();
await run(ctx, noop);
expect(ctx.request.query).toEqual({
resource: ['foo', 'bar', 'baz'],
});
});
it('should not affect the URL if no comma separated resource params are found', async () => {
const ctx = createMockContext({ url: endpoint + '?resource=foo&resource=bar' });
const run = koaResourceParam();
await run(ctx, noop);
expect(ctx.request.query).toEqual({
resource: ['foo', 'bar'],
});
});
});

View file

@ -0,0 +1,31 @@
import type { Nullable } from '@silverhand/essentials';
import type { MiddlewareType } from 'koa';
/**
* Create a middleware function that checks if the request URL contains comma separated `resource` query parameter.
* If yes, split the values and reconstruct the URL with multiple `resource` query parameters.
* E.g. `?resource=foo,bar` => `?resource=foo&resource=bar`
*/
export default function koaResourceParam<StateT, ContextT, ResponseBodyT>(): MiddlewareType<
StateT,
ContextT,
Nullable<ResponseBodyT>
> {
return async (ctx, next) => {
const { query } = ctx.request;
const { resource } = query;
if (!resource) {
return next();
}
const resources = Array.isArray(resource) ? resource : [resource];
const resourceParams = resources.flatMap((resource) => resource.split(','));
ctx.request.query = {
...query,
resource: resourceParams,
};
return next();
};
}

View file

@ -28,6 +28,7 @@ import { type CloudConnectionLibrary } from '#src/libraries/cloud-connection.js'
import { type LogtoConfigLibrary } from '#src/libraries/logto-config.js';
import koaAuditLog from '#src/middleware/koa-audit-log.js';
import koaBodyEtag from '#src/middleware/koa-body-etag.js';
import koaResourceParam from '#src/middleware/koa-resource-param.js';
import postgresAdapter from '#src/oidc/adapter.js';
import {
buildLoginPromptUrl,
@ -377,6 +378,12 @@ export default function initOidc(
throw error;
}
});
/**
* Check if the request URL contains comma separated `resource` query parameter. If yes, split the values and
* reconstruct the URL with multiple `resource` query parameters.
* E.g. `?resource=foo,bar` => `?resource=foo&resource=bar`
*/
oidc.use(koaResourceParam());
/**
* `oidc-provider` [strictly checks](https://github.com/panva/node-oidc-provider/blob/6a0bcbcd35ed3e6179e81f0ab97a45f5e4e58f48/lib/shared/selective_body.js#L11)
* the `content-type` header for further processing.