Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core,schemas): log extraTokenClaims exception #5539

Merged
merged 1 commit into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion packages/console/src/consts/logs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AuditLogKey, LogKey, WebhookLogKey } from '@logto/schemas';
import type { AuditLogKey, WebhookLogKey, JwtCustomizerLogKey, LogKey } from '@logto/schemas';
import { type Optional } from '@silverhand/essentials';

export const auditLogEventTitle: Record<string, Optional<string>> &
Expand Down Expand Up @@ -86,7 +86,14 @@ const webhookLogEventTitle: Record<string, Optional<string>> &
'TriggerHook.PostSignIn': undefined,
});

const jwtCustomizerLogEventTitle: Record<string, Optional<string>> &
Record<JwtCustomizerLogKey, Optional<string>> = Object.freeze({
'JwtCustomizer.AccessToken': undefined,
'JwtCustomizer.ClientCredentials': undefined,
});

export const logEventTitle: Record<string, Optional<string>> & Record<LogKey, Optional<string>> = {
...auditLogEventTitle,
...webhookLogEventTitle,
...jwtCustomizerLogEventTitle,
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { type WebhookDetailsOutletContext } from '../types';

import * as styles from './index.module.scss';

const hooLogEventOptions = Object.values(HookEvent).map((event) => ({
const hookLogEventOptions = Object.values(HookEvent).map((event) => ({
title: <DynamicT forKey={hookEventLabel[event]} />,
value: hookEventLogKey[event],
}));
Expand Down Expand Up @@ -64,7 +64,7 @@ function WebhookLogs() {
<div className={styles.eventSelector}>
<EventSelector
value={event}
options={hooLogEventOptions}
options={hookLogEventOptions}
onChange={(event) => {
updateSearchParameters({ event, page: undefined });
}}
Expand Down
41 changes: 35 additions & 6 deletions packages/core/src/oidc/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
LogtoJwtTokenPath,
ExtraParamsKey,
type Json,
jwtCustomizer as jwtCustomizerLog,
LogResult,
} from '@logto/schemas';
import { generateStandardId } from '@logto/shared';
import { conditional, trySafe, tryThat } from '@silverhand/essentials';
import i18next from 'i18next';
import koaBody from 'koa-body';
Expand All @@ -29,7 +32,7 @@
import { addOidcEventListeners } from '#src/event-listeners/index.js';
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 koaAuditLog, { LogEntry } from '#src/middleware/koa-audit-log.js';
import koaBodyEtag from '#src/middleware/koa-body-etag.js';
import postgresAdapter from '#src/oidc/adapter.js';
import {
Expand All @@ -54,7 +57,7 @@
// Temporarily removed 'EdDSA' since it's not supported by browser yet
const supportedSigningAlgs = Object.freeze(['RS256', 'PS256', 'ES256', 'ES384', 'ES512'] as const);

export default function initOidc(

Check warning on line 60 in packages/core/src/oidc/init.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/core/src/oidc/init.ts#L60

[max-params] Function 'initOidc' has too many parameters (5). Maximum allowed is 4.
envSet: EnvSet,
queries: Queries,
libraries: Libraries,
Expand All @@ -65,6 +68,7 @@
resources: { findDefaultResource },
users: { findUserById },
organizations,
logs: { insertLog },
} = queries;
const logoutSource = readFileSync('static/html/logout.html', 'utf8');
const logoutSuccessSource = readFileSync('static/html/post-logout/index.html', 'utf8');
Expand Down Expand Up @@ -206,7 +210,7 @@
},
},
extraParams: Object.values(ExtraParamsKey),

// eslint-disable-next-line complexity
extraTokenClaims: async (ctx, token) => {
const { isDevFeaturesEnabled, isCloud } = EnvSet.values;

Expand All @@ -215,9 +219,14 @@
return;
}

try {
const isTokenClientCredentials = token instanceof ctx.oidc.provider.ClientCredentials;
const isTokenClientCredentials = token instanceof ctx.oidc.provider.ClientCredentials;

try {
/**
* It is by design to use `trySafe` here to catch the error but not log it since we do not
* want to insert an error log every time the OIDC provider issues a token when the JWT
* customizer is not configured.
*/
const { script, envVars } =
(await trySafe(
logtoConfigs.getJwtCustomizer(
Expand Down Expand Up @@ -265,13 +274,33 @@
: {
...commonPayload,
tokenType: LogtoJwtTokenPath.AccessToken,
// TODO (LOG-8555): the newly added `UserProfile` type includes undefined fields and can not be directly assigned to `Json` type. And the `undefined` fields should be removed by zod guard.

Check warning on line 277 in packages/core/src/oidc/init.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/core/src/oidc/init.ts#L277

[no-warning-comments] Unexpected 'todo' comment: 'TODO (LOG-8555): the newly added...'.
// eslint-disable-next-line no-restricted-syntax
context: { user: logtoUserInfo as Record<string, Json> },
},
});
} catch {
// TODO: Log the error
} catch (error: unknown) {
const entry = new LogEntry(
`${jwtCustomizerLog.prefix}.${
isTokenClientCredentials
? jwtCustomizerLog.Type.ClientCredentials
: jwtCustomizerLog.Type.AccessToken
}`
);
entry.append({
result: LogResult.Error,
error: { message: String(error) },
});
const { payload } = entry;
await insertLog({
id: generateStandardId(),
key: payload.key,
payload: {
...payload,
tenantId: envSet.tenantId,
token,
},
});
}
},
extraClientMetadata: {
Expand Down
5 changes: 4 additions & 1 deletion packages/schemas/src/types/log/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import type * as hook from './hook.js';
import type * as interaction from './interaction.js';
import type * as jwtCustomizer from './jwt-customizer.js';
import type * as token from './token.js';

export * as interaction from './interaction.js';
export * as token from './token.js';
export * as hook from './hook.js';
export * as jwtCustomizer from './jwt-customizer.js';

/** Fallback for empty or unrecognized log keys. */
export const LogKeyUnknown = 'Unknown';

export type AuditLogKey = typeof LogKeyUnknown | interaction.LogKey | token.LogKey;
export type WebhookLogKey = hook.LogKey;
export type JwtCustomizerLogKey = jwtCustomizer.LogKey;

/**
* The union type of all available log keys.
Expand All @@ -19,4 +22,4 @@ export type WebhookLogKey = hook.LogKey;
* @see {@link interaction.LogKey} for interaction log keys.
* @see {@link token.LogKey} for token log keys.
**/
export type LogKey = AuditLogKey | WebhookLogKey;
export type LogKey = AuditLogKey | WebhookLogKey | JwtCustomizerLogKey;
11 changes: 11 additions & 0 deletions packages/schemas/src/types/log/jwt-customizer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export type Prefix = 'JwtCustomizer';

export const prefix: Prefix = 'JwtCustomizer';

/** The type of a custom JWT scenario. */
export enum Type {
AccessToken = 'AccessToken',
ClientCredentials = 'ClientCredentials',
}

export type LogKey = `${Prefix}.${Type.AccessToken | Type.ClientCredentials}`;
Loading