0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-13 21:30:30 -05:00

fix(core): add grant record for token exchange (#6502)

This commit is contained in:
wangsijie 2024-08-22 15:29:54 +08:00 committed by GitHub
parent fec844c3ce
commit 4be675a53a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 22 additions and 10 deletions

View file

@ -1,5 +1,4 @@
import { type SubjectToken } from '@logto/schemas';
import { createMockUtils } from '@logto/shared/esm';
import { type KoaContextWithOIDC, errors } from 'oidc-provider';
import Sinon from 'sinon';
@ -10,11 +9,6 @@ import { MockTenant } from '#src/test-utils/tenant.js';
import { TokenExchangeTokenType } from './types.js';
const { jest } = import.meta;
const { mockEsm } = createMockUtils(jest);
const { handleActorToken } = mockEsm('./actor-token.js', () => ({
handleActorToken: jest.fn().mockResolvedValue({ accountId: undefined }),
}));
const { buildHandler } = await import('./index.js');

View file

@ -58,7 +58,7 @@ export const buildHandler: (
queries: Queries
) => Parameters<Provider['registerGrantType']>['1'] = (envSet, queries) => async (ctx, next) => {
const { client, params, requestParamScopes, provider } = ctx.oidc;
const { Account, AccessToken } = provider;
const { Account, AccessToken, Grant } = provider;
assertThat(params, new InvalidGrant('parameters must be available'));
assertThat(client, new InvalidClient('client must be available'));
@ -90,6 +90,11 @@ export const buildHandler: (
ctx.oidc.entity('Account', account);
const grant = new Grant({
accountId: account.accountId,
clientId: client.clientId,
});
const { organizationId } = await checkOrganizationAccess(ctx, queries, account);
const accessToken = new AccessToken({
@ -97,9 +102,7 @@ export const buildHandler: (
clientId: client.clientId,
gty: GrantType.TokenExchange,
client,
// The token exchange grant type does not have a grant ID or grant object,
// so we use an empty string here.
grantId: '',
grantId: await grant.save(),
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
scope: undefined!,
extra: {
@ -147,6 +150,7 @@ export const buildHandler: (
scope: availableScopes.join(' '),
};
accessToken.scope = issuedScopes;
grant.addResourceScope(audience, accessToken.scope);
/* === End RFC 0001 === */
} else if (resource) {
const resourceServerInfo = await resourceIndicators.getResourceServerInfo(
@ -163,6 +167,7 @@ export const buildHandler: (
// @ts-expect-error -- code from oidc-provider
.filter(Set.prototype.has.bind(accessToken.resourceServer.scopes))
.join(' ');
grant.addResourceScope(resource, accessToken.scope);
} else {
accessToken.claims = ctx.oidc.claims;
// Filter scopes from `oidcScopes`,
@ -173,6 +178,7 @@ export const buildHandler: (
// wrap it with `new Set` to make it work
.filter((name) => new Set(oidcScopes).has(name))
.join(' ');
grant.addOIDCScope(accessToken.scope);
}
// Handle the actor token
@ -189,6 +195,8 @@ export const buildHandler: (
};
}
await grant.save();
ctx.oidc.entity('Grant', grant);
ctx.oidc.entity('AccessToken', accessToken);
const accessTokenString = await accessToken.save();

View file

@ -106,6 +106,16 @@ describe('Token Exchange (Personal Access Token)', () => {
.json();
expect(introspectionResponse).toHaveProperty('active', true);
expect(introspectionResponse).toHaveProperty('sub', testUserId);
// Should be able to get user info
const userInfoResponse = await oidcApi
.get('me', {
headers: {
Authorization: `Bearer ${access_token}`,
},
})
.json();
expect(userInfoResponse).toHaveProperty('sub', testUserId);
});
it('should be able to use for multiple times', async () => {