0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-04-07 23:01:25 -05:00

feat: various application improvements

- show OpenID Provider configuration endpoint in Console
- configure "Rotate Refresh Token" in Console
- configure "Refresh Token TTL" in Console
- refactor code for OIDC default values
This commit is contained in:
Gao Sun 2023-06-13 00:59:23 +08:00
parent 4ed72121b8
commit e3e3f2c729
No known key found for this signature in database
GPG key ID: 13EBE123E4773688
25 changed files with 267 additions and 24 deletions

View file

@ -0,0 +1 @@
export const openIdProviderConfigPath = 'oidc/.well-known/openid-configuration';

View file

@ -1,5 +1,6 @@
import type { Application, SnakeCaseOidcConfig } from '@logto/schemas';
import { ApplicationType } from '@logto/schemas';
import { type Application, type SnakeCaseOidcConfig, ApplicationType } from '@logto/schemas';
import { appendPath } from '@silverhand/essentials';
import { useContext } from 'react';
import { useFormContext } from 'react-hook-form';
import { Trans, useTranslation } from 'react-i18next';
@ -7,7 +8,10 @@ import CopyToClipboard from '@/components/CopyToClipboard';
import FormCard from '@/components/FormCard';
import FormField from '@/components/FormField';
import Switch from '@/components/Switch';
import TextInput from '@/components/TextInput';
import TextLink from '@/components/TextLink';
import { openIdProviderConfigPath } from '@/consts/oidc';
import { AppEndpointsContext } from '@/contexts/AppEndpointsProvider';
import * as styles from '../index.module.scss';
@ -17,7 +21,11 @@ type Props = {
};
function AdvancedSettings({ applicationType, oidcConfig }: Props) {
const { register } = useFormContext<Application & { isAdmin?: boolean }>();
const { userEndpoint } = useContext(AppEndpointsContext);
const {
register,
formState: { errors },
} = useFormContext<Application & { isAdmin?: boolean }>();
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
return (
@ -26,6 +34,15 @@ function AdvancedSettings({ applicationType, oidcConfig }: Props) {
description="application_details.advanced_settings_description"
learnMoreLink="https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint"
>
{userEndpoint && (
<FormField title="application_details.config_endpoint">
<CopyToClipboard
className={styles.textField}
value={appendPath(userEndpoint, openIdProviderConfigPath).href}
variant="border"
/>
</FormField>
)}
<FormField
title="application_details.authorization_endpoint"
tip={(closeTipHandler) => (
@ -72,6 +89,31 @@ function AdvancedSettings({ applicationType, oidcConfig }: Props) {
/>
</FormField>
)}
{[ApplicationType.Traditional, ApplicationType.Native].includes(applicationType) && (
<>
<FormField title="application_details.rotate_refresh_token">
<Switch
label={t('application_details.rotate_refresh_token_label')}
{...register('customClientMetadata.rotateRefreshToken')}
/>
</FormField>
<FormField
title="application_details.refresh_token_ttl"
tip={t('application_details.refresh_token_ttl_tip')}
>
<TextInput
{...register('customClientMetadata.refreshTokenTtlInDays', {
min: 1,
max: 90,
valueAsNumber: true,
})}
placeholder="14"
// Confirm if we need a customized message here
error={errors.customClientMetadata?.refreshTokenTtlInDays?.message}
/>
</FormField>
</>
)}
{applicationType === ApplicationType.MachineToMachine && (
<FormField title="application_details.enable_admin_access">
<Switch

View file

@ -1,6 +1,11 @@
import { withAppInsights } from '@logto/app-insights/react';
import type { Application, ApplicationResponse, SnakeCaseOidcConfig } from '@logto/schemas';
import { ApplicationType } from '@logto/schemas';
import {
type Application,
type ApplicationResponse,
type SnakeCaseOidcConfig,
ApplicationType,
customClientMetadataDefault,
} from '@logto/schemas';
import { useEffect, useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
@ -23,6 +28,7 @@ import PageMeta from '@/components/PageMeta';
import TabNav, { TabNavItem } from '@/components/TabNav';
import Tag from '@/components/Tag';
import UnsavedChangesAlertModal from '@/components/UnsavedChangesAlertModal';
import { openIdProviderConfigPath } from '@/consts/oidc';
import type { RequestError } from '@/hooks/use-api';
import useApi from '@/hooks/use-api';
import useDocumentationUrl from '@/hooks/use-documentation-url';
@ -53,7 +59,7 @@ function ApplicationDetails() {
data: oidcConfig,
error: fetchOidcConfigError,
mutate: mutateOidcConfig,
} = useSWR<SnakeCaseOidcConfig, RequestError>('oidc/.well-known/openid-configuration');
} = useSWR<SnakeCaseOidcConfig, RequestError>(openIdProviderConfigPath);
const isLoading = (!data && !error) || (!oidcConfig && !fetchOidcConfigError);
const requestError = error ?? fetchOidcConfigError;
const [isReadmeOpen, setIsReadmeOpen] = useState(false);
@ -62,7 +68,9 @@ function ApplicationDetails() {
const [isDeleted, setIsDeleted] = useState(false);
const api = useApi();
const navigate = useNavigate();
const formMethods = useForm<Application & { isAdmin: boolean }>();
const formMethods = useForm<Application & { isAdmin: boolean }>({
defaultValues: { customClientMetadata: customClientMetadataDefault },
});
const { getDocumentationUrl } = useDocumentationUrl();
const {

View file

@ -28,8 +28,6 @@ const loadOidcValues = async (issuer: string, configs: LogtoOidcConfigType) => {
jwkSigningAlg,
localJWKSet,
issuer,
defaultIdTokenTtl: 60 * 60,
defaultRefreshTokenTtl: 14 * 24 * 60 * 60,
});
};

View file

@ -0,0 +1,46 @@
import type Provider from 'oidc-provider';
import { type KoaContextWithOIDC } from 'oidc-provider';
/**
* Keep the default pre-checks from oidc-provider.
*
* @see {@link https://github.com/panva/node-oidc-provider/blob/d6edf2a0b9efc777081e6f9cc358a0677ccd9897/docs/README.md#ttl | ttl's default value}
*/
const refreshTokenTtl = (
...[ctx, token, client]: Parameters<Provider.TTLFunction<InstanceType<Provider['RefreshToken']>>>
) => {
if (
ctx.oidc.entities.RotatedRefreshToken &&
client.applicationType === 'web' &&
client.clientAuthMethod === 'none' &&
!token.isSenderConstrained()
) {
// Non-Sender Constrained SPA RefreshTokens do not have infinite expiration through rotation
return ctx.oidc.entities.RotatedRefreshToken.remainingTTL;
}
};
/** @see {@link https://github.com/panva/node-oidc-provider/blob/d6edf2a0b9efc777081e6f9cc358a0677ccd9897/docs/README.md#rotaterefreshtoken | rotateRefreshToken's default value} */
const rotateRefreshToken = (ctx: KoaContextWithOIDC) => {
const { RefreshToken: refreshToken, Client: client } = ctx.oidc.entities;
if (!refreshToken || !client) {
return false;
}
// Cap the maximum amount of time a refresh token can be
// rotated for up to 1 year, afterwards its TTL is final
if (refreshToken.totalLifetime() >= 365.25 * 24 * 60 * 60) {
return false;
}
// Rotate non sender-constrained public client refresh tokens
if (client.clientAuthMethod === 'none' && !refreshToken.isSenderConstrained()) {
return true;
}
// Rotate if the token is nearing expiration (it's beyond 70% of its lifetime)
return refreshToken.ttlPercentagePassed() >= 70;
};
const defaults = { refreshTokenTtl, rotateRefreshToken };
export default defaults;

View file

@ -5,8 +5,10 @@ import { readFileSync } from 'node:fs';
import { userClaims } from '@logto/core-kit';
import type { I18nKey } from '@logto/phrases';
import {
customClientMetadataDefault,
CustomClientMetadataKey,
demoAppApplicationId,
inSeconds,
logtoCookieKey,
type LogtoUiCookie,
} from '@logto/schemas';
@ -27,6 +29,7 @@ import type Libraries from '#src/tenants/Libraries.js';
import type Queries from '#src/tenants/Queries.js';
import { consoleLog } from '#src/utils/console.js';
import defaults from './defaults.js';
import { getUserClaimData, getUserClaims } from './scope.js';
import { OIDCExtraParametersKey, InteractionMode } from './type.js';
@ -39,14 +42,7 @@ export default function initOidc(
queries: Queries,
libraries: Libraries
): Provider {
const {
issuer,
cookieKeys,
privateJwks,
jwkSigningAlg,
defaultIdTokenTtl,
defaultRefreshTokenTtl,
} = envSet.oidc;
const { issuer, cookieKeys, privateJwks, jwkSigningAlg } = envSet.oidc;
const {
resources: { findResourceByIndicator, findDefaultResource },
users: { findUserById },
@ -252,12 +248,25 @@ export default function initOidc(
IdToken: (_ctx, _token, client) => {
const { idTokenTtl } = client.metadata();
return idTokenTtl ?? defaultIdTokenTtl;
return idTokenTtl ?? customClientMetadataDefault.idTokenTtl;
},
RefreshToken: (_ctx, _token, client) => {
const { refreshTokenTtl } = client.metadata();
RefreshToken: (ctx, token, client) => {
const defaultTtl = defaults.refreshTokenTtl(ctx, token, client);
return refreshTokenTtl ?? defaultRefreshTokenTtl;
if (defaultTtl !== undefined) {
return defaultTtl;
}
/** Customized logic for Refresh Token TTL */
const { refreshTokenTtlInDays, refreshTokenTtl } = client.metadata();
if (refreshTokenTtlInDays !== undefined) {
return refreshTokenTtlInDays * inSeconds.oneDay;
}
return (
refreshTokenTtl ?? customClientMetadataDefault.refreshTokenTtlInDays * inSeconds.oneDay
);
},
AccessToken: (ctx, token) => {
if (token.resourceServer) {
@ -270,6 +279,18 @@ export default function initOidc(
Session: 1_209_600 /* 14 days in seconds */,
Grant: 1_209_600 /* 14 days in seconds */,
},
rotateRefreshToken: (ctx) => {
const { Client: client } = ctx.oidc.entities;
// Directly return false only when `rotateRefreshToken` has been explicitly set to `false`.
if (
!(client?.metadata()?.rotateRefreshToken ?? customClientMetadataDefault.rotateRefreshToken)
) {
return false;
}
return defaults.rotateRefreshToken(ctx);
},
pkce: {
required: (ctx, client) => {
return client.clientAuthMethod !== 'client_secret_basic';

View file

@ -12,6 +12,7 @@ const application_details = {
application_name_placeholder: 'Meine App',
description: 'Beschreibung',
description_placeholder: 'Gib eine Beschreibung ein',
config_endpoint: 'OpenID Provider configuration endpoint', // UNTRANSLATED
authorization_endpoint: 'Autorisierungs-Endpoint',
authorization_endpoint_tip:
'Der Endpoint, der für die Authentifizierung und <a>Autorisierung</a> via OpenID Connect verwendet wird.',
@ -44,6 +45,12 @@ const application_details = {
always_issue_refresh_token: 'Immer den Refresh Token ausgeben',
always_issue_refresh_token_label:
'Durch Aktivieren dieser Konfiguration kann Logto immer Refresh Tokens ausgeben, unabhängig davon, ob in der Authentifizierungsanforderung "prompt=consent" angegeben ist. Diese Praxis wird jedoch nur dann empfohlen, wenn es notwendig ist, da sie nicht mit OpenID Connect kompatibel ist und möglicherweise Probleme verursacht.',
refresh_token_ttl: 'Refresh Token Time to Live (TTL) in days', // UNTRANSLATED
refresh_token_ttl_tip:
'The duration during which a refresh token can be used to request new access tokens before it expires and becomes invalid.', // UNTRANSLATED
rotate_refresh_token: 'Rotate Refresh Token', // UNTRANSLATED
rotate_refresh_token_label:
'When enabled, Logto will issue a new Refresh Token for token requests when 70% of the original Time to Live (TTL) has passed.', // UNTRANSLATED
delete_description:
'Diese Aktion kann nicht rückgängig gemacht werden. Die Anwendung wird permanent gelöscht. Bitte gib den Anwendungsnamen <span>{{name}}</span> zur Bestätigung ein.',
enter_your_application_name: 'Gib einen Anwendungsnamen ein',

View file

@ -12,6 +12,7 @@ const application_details = {
application_name_placeholder: 'My App',
description: 'Description',
description_placeholder: 'Enter your application description',
config_endpoint: 'OpenID Provider configuration endpoint',
authorization_endpoint: 'Authorization Endpoint',
authorization_endpoint_tip:
"The endpoint to perform authentication and authorization. It's used for OpenID Connect <a>Authentication</a>.",
@ -43,7 +44,13 @@ const application_details = {
'Enable or disable the access to Management API. Once enabled, you can use access tokens to call Management API on behalf on this application.',
always_issue_refresh_token: 'Always issue Refresh Token',
always_issue_refresh_token_label:
'Enabling this configuration will allow Logto to always issue Refresh Tokens, regardless of whether `prompt=consent` is presented in the authentication request. However, this practice is discouraged unless necessary, as it is not compatible with OpenID Connect and may potentially cause issues.',
'When enabled, Logto will always issue Refresh Tokens, regardless of whether `prompt=consent` is presented in the authentication request. However, this practice is discouraged unless necessary, as it is not compatible with OpenID Connect and may potentially cause issues.',
refresh_token_ttl: 'Refresh Token Time to Live (TTL) in days',
refresh_token_ttl_tip:
'The duration during which a refresh token can be used to request new access tokens before it expires and becomes invalid.',
rotate_refresh_token: 'Rotate Refresh Token',
rotate_refresh_token_label:
'When enabled, Logto will issue a new Refresh Token for token requests when 70% of the original Time to Live (TTL) has passed.',
delete_description:
'This action cannot be undone. It will permanently delete the application. Please enter the application name <span>{{name}}</span> to confirm.',
enter_your_application_name: 'Enter your application name',

View file

@ -12,6 +12,7 @@ const detalles_aplicacion = {
application_name_placeholder: 'Mi App',
description: 'Descripción',
description_placeholder: 'Ingresa la descripción de tu aplicación',
config_endpoint: 'OpenID Provider configuration endpoint', // UNTRANSLATED
authorization_endpoint: 'Endpoint de Autorización',
authorization_endpoint_tip:
'El endpoint para la autenticación y autorización. Se utiliza para OpenID Connect <a>Autenticación</a>.',
@ -44,6 +45,12 @@ const detalles_aplicacion = {
always_issue_refresh_token: 'Siempre emitir Token de Refresco',
always_issue_refresh_token_label:
'Al habilitar esta configuración, Logto siempre emitirá Tokens de Refresco, independientemente de si se presenta o no “prompt=consent” en la solicitud de autenticación. Sin embargo, esta práctica no está recomendada a menos que sea necesario, ya que no es compatible con OpenID Connect y puede causar problemas potenciales.',
refresh_token_ttl: 'Refresh Token Time to Live (TTL) in days', // UNTRANSLATED
refresh_token_ttl_tip:
'The duration during which a refresh token can be used to request new access tokens before it expires and becomes invalid.', // UNTRANSLATED
rotate_refresh_token: 'Rotate Refresh Token', // UNTRANSLATED
rotate_refresh_token_label:
'When enabled, Logto will issue a new Refresh Token for token requests when 70% of the original Time to Live (TTL) has passed.', // UNTRANSLATED
delete_description:
'Esta acción no se puede deshacer. Eliminará permanentemente la aplicación. Ingresa el nombre de la aplicación <span>{{name}}</span> para confirmar.',
enter_your_application_name: 'Ingresa el nombre de tu aplicación',

View file

@ -12,6 +12,7 @@ const application_details = {
application_name_placeholder: 'Mon App',
description: 'Description',
description_placeholder: 'Entrez la description de votre application',
config_endpoint: 'OpenID Provider configuration endpoint', // UNTRANSLATED
authorization_endpoint: "Point de terminaison d'autorisation",
authorization_endpoint_tip:
"Le point de terminaison pour effectuer l'authentification et l'autorisation. Il est utilisé pour <a>l'authentification</a> OpenID Connect.",
@ -44,6 +45,12 @@ const application_details = {
always_issue_refresh_token: 'Toujours émettre le Refresh Token.',
always_issue_refresh_token_label:
"En activant cette configuration, Logto pourra toujours émettre des Refresh Tokens, indépendamment de la présentation ou non de la demande d'authentification `prompt=consent`. Cependant, cette pratique est découragée sauf si nécessaire, car elle n'est pas compatible avec OpenID Connect et peut potentiellement causer des problèmes.",
refresh_token_ttl: 'Refresh Token Time to Live (TTL) in days', // UNTRANSLATED
refresh_token_ttl_tip:
'The duration during which a refresh token can be used to request new access tokens before it expires and becomes invalid.', // UNTRANSLATED
rotate_refresh_token: 'Rotate Refresh Token', // UNTRANSLATED
rotate_refresh_token_label:
'When enabled, Logto will issue a new Refresh Token for token requests when 70% of the original Time to Live (TTL) has passed.', // UNTRANSLATED
delete_description:
"Cette action ne peut être annulée. Elle supprimera définitivement l'application. Veuillez entrer le nom de l'application <span>{{nom}}</span> pour confirmer.",
enter_your_application_name: 'Entrez le nom de votre application',

View file

@ -12,6 +12,7 @@ const application_details = {
application_name_placeholder: 'La mia app',
description: 'Descrizione',
description_placeholder: 'Inserisci la descrizione della tua applicazione',
config_endpoint: 'OpenID Provider configuration endpoint', // UNTRANSLATED
authorization_endpoint: 'Authorization Endpoint',
authorization_endpoint_tip:
"L'endpoint per effettuare l'autenticazione e l'autorizzazione. Viene utilizzato per la connessione OpenID <a>autenticazione</a>.",
@ -44,6 +45,12 @@ const application_details = {
always_issue_refresh_token: 'Rilascia sempre il token di aggiornamento',
always_issue_refresh_token_label:
'Abilitando questa configurazione, Logto potrà rilasciare sempre token di aggiornamento, anche se `prompt=consent` non viene presentata nella richiesta di autenticazione. Tuttavia, questa pratica è scoraggiata a meno che non sia necessaria, in quanto non è compatibile con OpenID Connect e potrebbe potenzialmente causare problemi.',
refresh_token_ttl: 'Refresh Token Time to Live (TTL) in days', // UNTRANSLATED
refresh_token_ttl_tip:
'The duration during which a refresh token can be used to request new access tokens before it expires and becomes invalid.', // UNTRANSLATED
rotate_refresh_token: 'Rotate Refresh Token', // UNTRANSLATED
rotate_refresh_token_label:
'When enabled, Logto will issue a new Refresh Token for token requests when 70% of the original Time to Live (TTL) has passed.', // UNTRANSLATED
delete_description:
"Questa azione non può essere annullata. Eliminerà definitivamente l'applicazione. Inserisci il nome dell'applicazione <span>{{name}}</span> per confermare.",
enter_your_application_name: 'Inserisci il nome della tua applicazione',

View file

@ -12,6 +12,7 @@ const application_details = {
application_name_placeholder: '私のアプリ',
description: '説明',
description_placeholder: 'アプリケーションの説明を入力してください',
config_endpoint: 'OpenID Provider configuration endpoint', // UNTRANSLATED
authorization_endpoint: '認可エンドポイント',
authorization_endpoint_tip:
'認証と認可を実行するエンドポイントです。 OpenID Connectの<a>認証</a>に使用されます。',
@ -44,6 +45,12 @@ const application_details = {
always_issue_refresh_token: '常にRefresh Tokenを発行する',
always_issue_refresh_token_label:
'この設定を有効にすると、Logtoは、認証要求に「prompt = consent」が提示されたかどうかにかかわらず、常にRefresh Tokenを発行することができます。ただし、OpenID Connectと互換性がないため、必要でない限りこのプラクティスは推奨されず、問題が発生する可能性があります。',
refresh_token_ttl: 'Refresh Token Time to Live (TTL) in days', // UNTRANSLATED
refresh_token_ttl_tip:
'The duration during which a refresh token can be used to request new access tokens before it expires and becomes invalid.', // UNTRANSLATED
rotate_refresh_token: 'Rotate Refresh Token', // UNTRANSLATED
rotate_refresh_token_label:
'When enabled, Logto will issue a new Refresh Token for token requests when 70% of the original Time to Live (TTL) has passed.', // UNTRANSLATED
delete_description:
'この操作は元に戻すことはできません。アプリケーション名「<span>{{name}}</span>」を入力して確認してください。',
enter_your_application_name: 'アプリケーション名を入力してください',

View file

@ -12,7 +12,8 @@ const application_details = {
application_name_placeholder: '나의 앱',
description: '설명',
description_placeholder: '어플리케이션 설명을 적어주세요.',
authorization_endpoint: '인증 End-Point',
config_endpoint: 'OpenID Provider configuration endpoint', // UNTRANSLATED
authorization_endpoint: '인증 Endpoint',
authorization_endpoint_tip:
'인증 및 권한 부여를 진행할 End-Point예요. OpenID Connect <a>인증</a>에서 사용되던 값이에요.',
application_id: '앱 ID',
@ -44,6 +45,12 @@ const application_details = {
always_issue_refresh_token: '항상 Refresh Token을 발급하세요',
always_issue_refresh_token_label:
'다음 구성을 활성화하면 Logto가 인증 요청에 `prompt=consent`가 제시되었는지 여부와 상관없이 항상 Refresh Token을 발급할 수 있게 됩니다. 그러나 OpenID Connect와 호환되지 않으며 문제가 발생할 수 있으므로 필요하지 않은 경우에는 이러한 방법을 권장하지 않습니다. ',
refresh_token_ttl: 'Refresh Token Time to Live (TTL) in days', // UNTRANSLATED
refresh_token_ttl_tip:
'The duration during which a refresh token can be used to request new access tokens before it expires and becomes invalid.', // UNTRANSLATED
rotate_refresh_token: 'Rotate Refresh Token', // UNTRANSLATED
rotate_refresh_token_label:
'When enabled, Logto will issue a new Refresh Token for token requests when 70% of the original Time to Live (TTL) has passed.', // UNTRANSLATED
delete_description:
'이 행동은 취소될 수 없어요. 어플리케이션을 영원히 삭제할 거에요. 삭제를 진행하기 위해 <span>{{name}}</span> 를 입력해주세요.',
enter_your_application_name: '어플리케이션 이름을 입력해 주세요.',

View file

@ -12,6 +12,7 @@ const application_details = {
application_name_placeholder: 'Moja aplikacja',
description: 'Opis',
description_placeholder: 'Wpisz opis swojej aplikacji',
config_endpoint: 'OpenID Provider configuration endpoint', // UNTRANSLATED
authorization_endpoint: 'Endpoint autoryzacji',
authorization_endpoint_tip:
'Endpoint wykorzystywany do autentykacji i autoryzacji. Używany jest dla OpenID Connect <a>Autentykacji</a>.',
@ -44,6 +45,12 @@ const application_details = {
always_issue_refresh_token: 'Zawsze wydawaj Refresh Token',
always_issue_refresh_token_label:
'Rozwiazanie tej konfiguracji pozwoli Logto zawsze wydawac cwiecze tokeny, bez wzgledu na to, czy w zadaniu autoryzacji zostal przedstawiony `prompt=consent`. Jednak ta praktyka jest odstraszana, chyba ze konieczne, jak nie jest w pelni kompatybilna z OpenID Connect i moze potencjalnie powodowac problemy.',
refresh_token_ttl: 'Refresh Token Time to Live (TTL) in days', // UNTRANSLATED
refresh_token_ttl_tip:
'The duration during which a refresh token can be used to request new access tokens before it expires and becomes invalid.', // UNTRANSLATED
rotate_refresh_token: 'Rotate Refresh Token', // UNTRANSLATED
rotate_refresh_token_label:
'When enabled, Logto will issue a new Refresh Token for token requests when 70% of the original Time to Live (TTL) has passed.', // UNTRANSLATED
delete_description:
'Ta operacja nie może zostać cofnięta. Skutkuje ona trwałym usunięciem aplikacji. Aby potwierdzić, wpisz nazwę aplikacji <span>{{name}}</span>.',
enter_your_application_name: 'Wpisz nazwę swojej aplikacji',

View file

@ -12,6 +12,7 @@ const application_details = {
application_name_placeholder: 'Meu aplicativo',
description: 'Descrição',
description_placeholder: 'Digite a descrição do seu aplicativo',
config_endpoint: 'OpenID Provider configuration endpoint', // UNTRANSLATED
authorization_endpoint: 'Endpoint de autorização',
authorization_endpoint_tip:
'O endpoint para executar autenticação e autorização. É usado para <a>autenticação</a> OpenID Connect.',
@ -44,6 +45,12 @@ const application_details = {
always_issue_refresh_token: 'Emitir sempre o token de refresh',
always_issue_refresh_token_label:
'Ativar esta configuração permitirá que a Logto emita sempre tokens de Refresh, independentemente de "prompt=consent" ser apresentado na solicitação de autenticação. No entanto, essa prática é desencorajada, a menos que seja necessária, pois não é compatível com o OpenID Connect e pode potencialmente causar problemas.',
refresh_token_ttl: 'Refresh Token Time to Live (TTL) in days', // UNTRANSLATED
refresh_token_ttl_tip:
'The duration during which a refresh token can be used to request new access tokens before it expires and becomes invalid.', // UNTRANSLATED
rotate_refresh_token: 'Rotate Refresh Token', // UNTRANSLATED
rotate_refresh_token_label:
'When enabled, Logto will issue a new Refresh Token for token requests when 70% of the original Time to Live (TTL) has passed.', // UNTRANSLATED
delete_description:
'Essa ação não pode ser desfeita. Isso excluirá permanentemente o aplicativo. Insira o nome do aplicativo <span>{{name}}</span> para confirmar.',
enter_your_application_name: 'Digite o nome do seu aplicativo',

View file

@ -12,6 +12,7 @@ const application_details = {
application_name_placeholder: 'Ex: Site da Empresa',
description: 'Descrição',
description_placeholder: 'Insira a descrição da sua aplicação',
config_endpoint: 'OpenID Provider configuration endpoint', // UNTRANSLATED
authorization_endpoint: 'Endpoint de autorização',
authorization_endpoint_tip:
'O endpoint para realizar a autenticação e autorização. É usado para <a>autenticação</a> OpenID Connect.',
@ -44,6 +45,12 @@ const application_details = {
always_issue_refresh_token: 'Sempre emitir Refresh Token',
always_issue_refresh_token_label:
'Ao ativar essa configuração, a Logto sempre emitirá tokens de atualização, independentemente de `prompt=consent`ser apresentado na solicitação de autenticação. No entanto, essa prática é desencorajada, a menos que seja necessária, pois não é compatível com OpenID Connect e pode causar problemas.',
refresh_token_ttl: 'Refresh Token Time to Live (TTL) in days', // UNTRANSLATED
refresh_token_ttl_tip:
'The duration during which a refresh token can be used to request new access tokens before it expires and becomes invalid.', // UNTRANSLATED
rotate_refresh_token: 'Rotate Refresh Token', // UNTRANSLATED
rotate_refresh_token_label:
'When enabled, Logto will issue a new Refresh Token for token requests when 70% of the original Time to Live (TTL) has passed.', // UNTRANSLATED
delete_description:
'Esta ação não pode ser revertida. Esta ação irá eliminar permanentemente a aplicação. Insira o nome da aplicação <span>{{name}}</span> para confirmar.',
enter_your_application_name: 'Insira o nome da aplicação',

View file

@ -12,6 +12,7 @@ const application_details = {
application_name_placeholder: 'Мое приложение',
description: 'Описание',
description_placeholder: 'Введите описание своего приложения',
config_endpoint: 'OpenID Provider configuration endpoint', // UNTRANSLATED
authorization_endpoint: 'Конечная точка авторизации',
authorization_endpoint_tip:
'Конечная точка для аутентификации и авторизации. Он используется для аутентификации <a> OpenID Connect </a>.',
@ -44,6 +45,12 @@ const application_details = {
always_issue_refresh_token: 'Всегда выдавать Refresh Token',
always_issue_refresh_token_label:
'Включение этой настройки позволит Logto всегда выдавать Refresh Tokens, независимо от того, была ли в запросе на аутентификацию предложена команда `prompt=consent`. Однако данная практика не рекомендуется, если это необходимо, поскольку она несовместима с OpenID Connect и может вызвать проблемы.',
refresh_token_ttl: 'Refresh Token Time to Live (TTL) in days', // UNTRANSLATED
refresh_token_ttl_tip:
'The duration during which a refresh token can be used to request new access tokens before it expires and becomes invalid.', // UNTRANSLATED
rotate_refresh_token: 'Rotate Refresh Token', // UNTRANSLATED
rotate_refresh_token_label:
'When enabled, Logto will issue a new Refresh Token for token requests when 70% of the original Time to Live (TTL) has passed.', // UNTRANSLATED
delete_description:
'Это действие нельзя отменить. Оно навсегда удалит приложение. Введите название приложения <span> {{name}} </span>, чтобы подтвердить.',
enter_your_application_name: 'Введите название своего приложения',

View file

@ -12,6 +12,7 @@ const application_details = {
application_name_placeholder: 'Uygulamam',
description: 'Açıklama',
description_placeholder: 'Uygulama açıklamasını giriniz',
config_endpoint: 'OpenID Provider configuration endpoint', // UNTRANSLATED
authorization_endpoint: 'Yetkilendirme bitiş noktası',
authorization_endpoint_tip:
'Kimlik doğrulama ve yetkilendirme gerçekleştirmek için bitiş noktası. OpenID Connect <a>Authentication</a> için kullanılır.',
@ -44,6 +45,12 @@ const application_details = {
always_issue_refresh_token: 'Her zaman Refresh Token ver',
always_issue_refresh_token_label:
"Bu yapılandırmayı etkinleştirmek, Logto'nun OpenID Connect ile uyumlu olmayan ve olası sorunlara neden olabilecek her zaman Refresh Token çıkarmasına izin verir `prompt=consent` kimlik doğrulama isteğinin sunulup sunulmadığına bakılmaksızın. Ancak, bu uygulama yalnızca zorunlu olduğunda caydırılmayan bir uygulamadır.",
refresh_token_ttl: 'Refresh Token Time to Live (TTL) in days', // UNTRANSLATED
refresh_token_ttl_tip:
'The duration during which a refresh token can be used to request new access tokens before it expires and becomes invalid.', // UNTRANSLATED
rotate_refresh_token: 'Rotate Refresh Token', // UNTRANSLATED
rotate_refresh_token_label:
'When enabled, Logto will issue a new Refresh Token for token requests when 70% of the original Time to Live (TTL) has passed.', // UNTRANSLATED
delete_description:
'Bu eylem geri alınamaz. Uygulama kalıcı olarak silinecektir. Lütfen onaylamak için uygulama adı <span>{{name}}</span> girin.',
enter_your_application_name: 'Uygulama adı giriniz',

View file

@ -11,6 +11,7 @@ const application_details = {
application_name_placeholder: '我的应用',
description: '描述',
description_placeholder: '请输入应用描述',
config_endpoint: 'OpenID Provider configuration endpoint', // UNTRANSLATED
authorization_endpoint: '授权端点',
authorization_endpoint_tip: '进行鉴权与授权的端点。用于 OpenID Connect 中的 <a>鉴权</a> 流程。',
application_id: '应用 ID',
@ -42,6 +43,12 @@ const application_details = {
always_issue_refresh_token: '总是颁发 Refresh Token',
always_issue_refresh_token_label:
'启用此配置将允许 Logto 始终颁发 Refresh Token无论身份验证请求中是否呈现 `prompt=consent`。 然而,除非必要,否则不推荐这样做,因为它与 OpenID Connect 不兼容,可能会导致问题。',
refresh_token_ttl: 'Refresh Token Time to Live (TTL) in days', // UNTRANSLATED
refresh_token_ttl_tip:
'The duration during which a refresh token can be used to request new access tokens before it expires and becomes invalid.', // UNTRANSLATED
rotate_refresh_token: 'Rotate Refresh Token', // UNTRANSLATED
rotate_refresh_token_label:
'When enabled, Logto will issue a new Refresh Token for token requests when 70% of the original Time to Live (TTL) has passed.', // UNTRANSLATED
delete_description: '本操作会永久性地删除该应用,且不可撤销。输入 <span>{{name}}</span> 确认。',
enter_your_application_name: '输入你的应用名称',
application_deleted: '应用 {{name}} 成功删除。',

View file

@ -11,6 +11,7 @@ const application_details = {
application_name_placeholder: '我的應用程式',
description: '描述',
description_placeholder: '請輸入應用程式描述',
config_endpoint: 'OpenID Provider configuration endpoint', // UNTRANSLATED
authorization_endpoint: '授權端點',
authorization_endpoint_tip: '進行驗證和授權的端點。用於 OpenID Connect 中的<a> 驗證 </a> 流程。',
application_id: '應用程式 ID',
@ -42,6 +43,12 @@ const application_details = {
always_issue_refresh_token: '始終發放 Refresh Token',
always_issue_refresh_token_label:
'啟用此配置將允許 Logto 始終發行 Refresh Token無論是否在驗證請求中呈現 `prompt=consent`。但是,除非必要,否則不建議這樣做,因為它不兼容 OpenID Connect可能會引起問題。',
refresh_token_ttl: 'Refresh Token Time to Live (TTL) in days', // UNTRANSLATED
refresh_token_ttl_tip:
'The duration during which a refresh token can be used to request new access tokens before it expires and becomes invalid.', // UNTRANSLATED
rotate_refresh_token: 'Rotate Refresh Token', // UNTRANSLATED
rotate_refresh_token_label:
'When enabled, Logto will issue a new Refresh Token for token requests when 70% of the original Time to Live (TTL) has passed.', // UNTRANSLATED
delete_description: '本操作會永久性地刪除該應用,且不可撤銷。輸入 <span>{{name}}</span> 確認。',
enter_your_application_name: '輸入你的應用程式名稱',
application_deleted: '應用 {{name}} 成功刪除。',

View file

@ -11,6 +11,7 @@ const application_details = {
application_name_placeholder: '我的應用程式',
description: '說明',
description_placeholder: '請輸入應用程式說明',
config_endpoint: 'OpenID Provider configuration endpoint', // UNTRANSLATED
authorization_endpoint: '授權端點',
authorization_endpoint_tip: '進行驗證與授權的端點。用於 OpenID Connect 中的 <a>驗證</a> 流程。',
application_id: '應用程式 ID',
@ -42,6 +43,12 @@ const application_details = {
always_issue_refresh_token: '始終發放 Refresh Token',
always_issue_refresh_token_label:
'啟用此配置將使 Logto 無論在驗證請求中是否提供 prompt=consent都能始終發放 Refresh Token。然而除非必要否則不鼓勵這種做法因為它與 OpenID Connect 不相容並可能引起問題。',
refresh_token_ttl: 'Refresh Token Time to Live (TTL) in days', // UNTRANSLATED
refresh_token_ttl_tip:
'The duration during which a refresh token can be used to request new access tokens before it expires and becomes invalid.', // UNTRANSLATED
rotate_refresh_token: 'Rotate Refresh Token', // UNTRANSLATED
rotate_refresh_token_label:
'When enabled, Logto will issue a new Refresh Token for token requests when 70% of the original Time to Live (TTL) has passed.', // UNTRANSLATED
delete_description:
'本操作會永久性地刪除該應用程式,且不可撤銷。輸入 <span>{{name}}</span> 確認。',
enter_your_application_name: '輸入你的應用程式姓名',

View file

@ -0,0 +1 @@
export const inSeconds = Object.freeze({ oneMinute: 60, oneHour: 60 * 60, oneDay: 24 * 60 * 60 });

View file

@ -1,3 +1,4 @@
export * from './cookie.js';
export * from './system.js';
export * from './oidc.js';
export * from './date.js';

View file

@ -1 +1,11 @@
import { type CustomClientMetadata } from '../foundations/jsonb-types.js';
import { inSeconds } from './date.js';
export const tenantIdKey = 'tenant_id';
export const customClientMetadataDefault = Object.freeze({
idTokenTtl: inSeconds.oneHour,
refreshTokenTtlInDays: 14,
rotateRefreshToken: true,
} as const satisfies Partial<CustomClientMetadata>);

View file

@ -67,7 +67,9 @@ export type OidcClientMetadata = z.infer<typeof oidcClientMetadataGuard>;
export enum CustomClientMetadataKey {
CorsAllowedOrigins = 'corsAllowedOrigins',
IdTokenTtl = 'idTokenTtl',
/** @deprecated Use {@link RefreshTokenTtlInDays} instead. */
RefreshTokenTtl = 'refreshTokenTtl',
RefreshTokenTtlInDays = 'refreshTokenTtlInDays',
TenantId = 'tenantId',
/**
* Enabling this configuration will allow Logto to always issue Refresh Tokens, regardless of whether `prompt=consent` is presented in the authentication request.
@ -77,15 +79,23 @@ export enum CustomClientMetadataKey {
* This config is for the third-party integrations that do not strictly follow OpenID Connect standards due to some reasons (e.g. they only know OAuth, but requires a Refresh Token to be returned anyway).
*/
AlwaysIssueRefreshToken = 'alwaysIssueRefreshToken',
/**
* When enabled (default), Logto will issue a new Refresh Token for token requests when 70% of the original Time to Live (TTL) has passed.
*
* It can be turned off for only traditional web apps for enhanced security.
*/
RotateRefreshToken = 'rotateRefreshToken',
}
export const customClientMetadataGuard = z.object({
[CustomClientMetadataKey.CorsAllowedOrigins]: z.string().url().array().optional(),
[CustomClientMetadataKey.IdTokenTtl]: z.number().optional(),
[CustomClientMetadataKey.RefreshTokenTtl]: z.number().optional(),
[CustomClientMetadataKey.RefreshTokenTtlInDays]: z.number().optional(),
[CustomClientMetadataKey.TenantId]: z.string().optional(),
[CustomClientMetadataKey.AlwaysIssueRefreshToken]: z.boolean().optional(),
});
[CustomClientMetadataKey.RotateRefreshToken]: z.boolean().optional(),
} satisfies Record<CustomClientMetadataKey, z.ZodType>);
/**
* @see {@link CustomClientMetadataKey} for key descriptions.