0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-06 20:40:08 -05:00

feat(application): set idToken and refreshToken ttl based on client metadata (#176)

* feat(application): set idtoken and refresh token ttl based on client metadata

add idToken and refreshToken ttl metadata

* fix(application): cr fix

cr fix add default constant & set custom client metadata not null
This commit is contained in:
simeng-li 2022-01-13 14:15:13 +08:00 committed by GitHub
parent 59cd617b2b
commit 77be675bfb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 55 additions and 13 deletions

View file

@ -0,0 +1,6 @@
import { CustomClientMetadata } from '@logto/schemas';
import { AllClientMetadata } from 'oidc-provider';
declare module 'oidc-provider' {
export interface AllClientMetadata extends CustomClientMetadata {}
}

View file

@ -23,6 +23,7 @@ export default function postgresAdapter(modelName: string): ReturnType<AdapterFa
name: client_name,
type,
oidcClientMetadata,
customClientMetadata,
}: ApplicationUpdate): AllClientMetadata => ({
client_id,
client_name,
@ -30,6 +31,7 @@ export default function postgresAdapter(modelName: string): ReturnType<AdapterFa
grant_types: ['authorization_code', 'refresh_token'],
token_endpoint_auth_method: 'none',
...snakecaseKeys(oidcClientMetadata),
...customClientMetadata, // OIDC Provider won't camelcase custom parameter keys
});
return {

View file

@ -11,3 +11,6 @@ export const publicKey = crypto.createPublicKey(privateKey);
export const issuer = getEnv('OIDC_ISSUER', `http://localhost:${port}/oidc`);
export const adminResource = getEnv('ADMIN_RESOURCE', 'https://api.logto.io');
export const defaultIdTokenTtl = 60 * 60;
export const defaultRefreshTokenTtl = 14 * 24 * 60 * 60;

View file

@ -1,3 +1,4 @@
import { customClientMetadataGuard, CustomClientMetadataType } from '@logto/schemas';
import { fromKeyLike } from 'jose/jwk/from_key_like';
import Koa from 'koa';
import mount from 'koa-mount';
@ -9,7 +10,7 @@ import { findAllScopesWithResourceId } from '@/queries/scopes';
import { findUserById } from '@/queries/user';
import { routes } from '@/routes/consts';
import { issuer, privateKey } from './consts';
import { issuer, privateKey, defaultIdTokenTtl, defaultRefreshTokenTtl } from './consts';
export default async function initOidc(app: Koa): Promise<Provider> {
const keys = [await fromKeyLike(privateKey)];
@ -71,6 +72,15 @@ export default async function initOidc(app: Koa): Promise<Provider> {
}
},
},
extraClientMetadata: {
properties: Object.keys(CustomClientMetadataType),
validator: (_ctx, key, value) => {
const result = customClientMetadataGuard.pick({ [key]: true }).safeParse({ key: value });
if (!result.success) {
throw new errors.InvalidClientMetadata(key);
}
},
},
clientBasedCORS: (_, origin) => {
console.log('origin', origin);
return origin.startsWith('http://localhost:3000');
@ -89,6 +99,19 @@ export default async function initOidc(app: Koa): Promise<Provider> {
},
};
},
ttl: {
/**
* [OIDC Provider Default Settings](https://github.com/panva/node-oidc-provider/blob/main/docs/README.md#ttl)
*/
IdToken: (ctx, token, client) => {
const { idTokenTtl } = client.metadata();
return idTokenTtl ?? defaultIdTokenTtl;
},
RefreshToken: (ctx, token, client) => {
const { refreshTokenTtl } = client.metadata();
return refreshTokenTtl ?? defaultRefreshTokenTtl;
},
},
});
app.use(mount('/oidc', oidc.app));
return oidc;

View file

@ -5,6 +5,8 @@ import { z } from 'zod';
import {
OidcClientMetadata,
oidcClientMetadataGuard,
CustomClientMetadata,
customClientMetadataGuard,
GeneratedSchema,
Guard,
} from '../foundations';
@ -16,8 +18,7 @@ export type ApplicationUpdate = {
description?: string | null;
type: ApplicationType;
oidcClientMetadata: OidcClientMetadata;
idTokenTtl?: number;
refreshTokenTtl?: number;
customClientMetadata?: CustomClientMetadata;
createdAt?: number;
};
@ -27,8 +28,7 @@ export type Application = {
description: string | null;
type: ApplicationType;
oidcClientMetadata: OidcClientMetadata;
idTokenTtl: number;
refreshTokenTtl: number;
customClientMetadata: CustomClientMetadata;
createdAt: number;
};
@ -38,8 +38,7 @@ const guard: Guard<ApplicationUpdate> = z.object({
description: z.string().optional(),
type: z.nativeEnum(ApplicationType),
oidcClientMetadata: oidcClientMetadataGuard,
idTokenTtl: z.number().optional(),
refreshTokenTtl: z.number().optional(),
customClientMetadata: customClientMetadataGuard.optional(),
createdAt: z.number().optional(),
});
@ -52,8 +51,7 @@ export const Applications: GeneratedSchema<ApplicationUpdate> = Object.freeze({
description: 'description',
type: 'type',
oidcClientMetadata: 'oidc_client_metadata',
idTokenTtl: 'id_token_ttl',
refreshTokenTtl: 'refresh_token_ttl',
customClientMetadata: 'custom_client_metadata',
createdAt: 'created_at',
},
fieldKeys: [
@ -62,8 +60,7 @@ export const Applications: GeneratedSchema<ApplicationUpdate> = Object.freeze({
'description',
'type',
'oidcClientMetadata',
'idTokenTtl',
'refreshTokenTtl',
'customClientMetadata',
'createdAt',
],
guard,

View file

@ -22,6 +22,18 @@ export const oidcClientMetadataGuard = z.object({
export type OidcClientMetadata = z.infer<typeof oidcClientMetadataGuard>;
export enum CustomClientMetadataType {
idTokenTtl = 'idTokenTtl',
refreshTokenTtl = 'refreshTokenTtl',
}
export const customClientMetadataGuard = z.object({
[CustomClientMetadataType.idTokenTtl]: z.number().optional(),
[CustomClientMetadataType.refreshTokenTtl]: z.number().optional(),
});
export type CustomClientMetadata = z.infer<typeof customClientMetadataGuard>;
export const userLogPayloadGuard = z.object({
ip: z.string().optional(),
userAgent: z.string().optional(),

View file

@ -6,8 +6,7 @@ create table applications (
description text,
type application_type not null,
oidc_client_metadata jsonb /* @use OidcClientMetadata */ not null,
id_token_ttl bigint not null default(86400),
refresh_token_ttl bigint not null default(2592000),
custom_client_metadata jsonb /* @use CustomClientMetadata */ not null default '{}'::jsonb,
created_at timestamptz not null default(now()),
primary key (id)
);