mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
feat(core): add hasPassword field to custom JWT user context (#6096)
This commit is contained in:
parent
f2c7799ee6
commit
b52609a1ed
6 changed files with 44 additions and 10 deletions
7
.changeset/sharp-cooks-explain.md
Normal file
7
.changeset/sharp-cooks-explain.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
"@logto/console": minor
|
||||||
|
"@logto/schemas": minor
|
||||||
|
"@logto/core": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
add `hasPassword` to custom JWT user context
|
|
@ -182,6 +182,7 @@ export const defaultClientCredentialsPayload: ClientCredentialsPayload = {
|
||||||
|
|
||||||
const defaultUserContext: Partial<JwtCustomizerUserContext> = {
|
const defaultUserContext: Partial<JwtCustomizerUserContext> = {
|
||||||
id: '123',
|
id: '123',
|
||||||
|
hasPassword: false,
|
||||||
username: 'foo',
|
username: 'foo',
|
||||||
primaryEmail: 'foo@logto.io',
|
primaryEmail: 'foo@logto.io',
|
||||||
primaryPhone: '+1234567890',
|
primaryPhone: '+1234567890',
|
||||||
|
|
|
@ -87,6 +87,7 @@ export class JwtCustomizerLibrary {
|
||||||
await this.queries.organizations.relations.users.getOrganizationsByUserId(userId);
|
await this.queries.organizations.relations.users.getOrganizationsByUserId(userId);
|
||||||
const userContext = {
|
const userContext = {
|
||||||
...pick(user, ...userInfoSelectFields),
|
...pick(user, ...userInfoSelectFields),
|
||||||
|
hasPassword: Boolean(user.passwordEncrypted),
|
||||||
ssoIdentities: fullSsoIdentities.map(pickState('issuer', 'identityId', 'detail')),
|
ssoIdentities: fullSsoIdentities.map(pickState('issuer', 'identityId', 'detail')),
|
||||||
mfaVerificationFactors: deduplicate(user.mfaVerifications.map(({ type }) => type)),
|
mfaVerificationFactors: deduplicate(user.mfaVerifications.map(({ type }) => type)),
|
||||||
roles: roles.map((role) => {
|
roles: roles.map((role) => {
|
||||||
|
|
|
@ -55,8 +55,8 @@ export const accessTokenJwtCustomizerPayload = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const accessTokenSampleScript = `const getCustomJwtClaims = async ({ token, context, environmentVariables }) => {
|
export const accessTokenSampleScript = `const getCustomJwtClaims = async ({ token, context, environmentVariables }) => {
|
||||||
return { user_id: context?.user?.id ?? 'unknown' };
|
return { user_id: context?.user?.id ?? 'unknown', hasPassword: context?.user?.hasPassword };
|
||||||
}`;
|
};`;
|
||||||
|
|
||||||
export const clientCredentialsSampleScript = `const getCustomJwtClaims = async ({ token, context, environmentVariables }) => {
|
export const clientCredentialsSampleScript = `const getCustomJwtClaims = async ({ token, context, environmentVariables }) => {
|
||||||
return { ...environmentVariables };
|
return { ...environmentVariables };
|
||||||
|
|
|
@ -125,6 +125,8 @@ describe('get access token', () => {
|
||||||
testApiScopeNames.join(' ')
|
testApiScopeNames.join(' ')
|
||||||
);
|
);
|
||||||
expect(getAccessTokenPayload(accessToken)).toHaveProperty('user_id', guestUserId);
|
expect(getAccessTokenPayload(accessToken)).toHaveProperty('user_id', guestUserId);
|
||||||
|
// The guest user has password.
|
||||||
|
expect(getAccessTokenPayload(accessToken)).toHaveProperty('hasPassword', true);
|
||||||
|
|
||||||
await deleteJwtCustomizer('access-token');
|
await deleteJwtCustomizer('access-token');
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
import { jsonObjectGuard } from '@logto/connector-kit';
|
import { jsonObjectGuard } from '@logto/connector-kit';
|
||||||
import { z } from 'zod';
|
import { type ZodType, z } from 'zod';
|
||||||
|
|
||||||
import { Organizations, Roles, UserSsoIdentities } from '../../db-entries/index.js';
|
import {
|
||||||
import { mfaFactorsGuard } from '../../foundations/index.js';
|
Organizations,
|
||||||
import { scopeResponseGuard } from '../scope.js';
|
type Organization,
|
||||||
import { userInfoGuard } from '../user.js';
|
type Role,
|
||||||
|
Roles,
|
||||||
|
UserSsoIdentities,
|
||||||
|
type UserSsoIdentity,
|
||||||
|
} from '../../db-entries/index.js';
|
||||||
|
import { mfaFactorsGuard, type MfaFactors } from '../../foundations/index.js';
|
||||||
|
import { scopeResponseGuard, type ScopeResponse } from '../scope.js';
|
||||||
|
import { userInfoGuard, type UserInfo } from '../user.js';
|
||||||
|
|
||||||
import { accessTokenPayloadGuard, clientCredentialsPayloadGuard } from './oidc-provider.js';
|
import { accessTokenPayloadGuard, clientCredentialsPayloadGuard } from './oidc-provider.js';
|
||||||
|
|
||||||
|
@ -19,7 +26,25 @@ export enum LogtoJwtTokenKeyType {
|
||||||
ClientCredentials = 'client-credentials',
|
ClientCredentials = 'client-credentials',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type JwtCustomizerUserContext = UserInfo & {
|
||||||
|
hasPassword: boolean;
|
||||||
|
ssoIdentities: Array<Pick<UserSsoIdentity, 'issuer' | 'identityId' | 'detail'>>;
|
||||||
|
mfaVerificationFactors: MfaFactors;
|
||||||
|
roles: Array<
|
||||||
|
Pick<Role, 'id' | 'name' | 'description'> & {
|
||||||
|
scopes: Array<Pick<ScopeResponse, 'id' | 'name' | 'description' | 'resourceId' | 'resource'>>;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
organizations: Array<Pick<Organization, 'id' | 'name' | 'description'>>;
|
||||||
|
organizationRoles: Array<{
|
||||||
|
organizationId: string;
|
||||||
|
roleId: string;
|
||||||
|
roleName: string;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
|
||||||
export const jwtCustomizerUserContextGuard = userInfoGuard.extend({
|
export const jwtCustomizerUserContextGuard = userInfoGuard.extend({
|
||||||
|
hasPassword: z.boolean(),
|
||||||
ssoIdentities: UserSsoIdentities.guard
|
ssoIdentities: UserSsoIdentities.guard
|
||||||
.pick({ issuer: true, identityId: true, detail: true })
|
.pick({ issuer: true, identityId: true, detail: true })
|
||||||
.array(),
|
.array(),
|
||||||
|
@ -40,9 +65,7 @@ export const jwtCustomizerUserContextGuard = userInfoGuard.extend({
|
||||||
roleName: z.string(),
|
roleName: z.string(),
|
||||||
})
|
})
|
||||||
.array(),
|
.array(),
|
||||||
});
|
}) satisfies ZodType<JwtCustomizerUserContext>;
|
||||||
|
|
||||||
export type JwtCustomizerUserContext = z.infer<typeof jwtCustomizerUserContextGuard>;
|
|
||||||
|
|
||||||
export const accessTokenJwtCustomizerGuard = jwtCustomizerGuard
|
export const accessTokenJwtCustomizerGuard = jwtCustomizerGuard
|
||||||
.extend({
|
.extend({
|
||||||
|
|
Loading…
Reference in a new issue