mirror of
https://github.com/logto-io/logto.git
synced 2025-01-13 21:30:30 -05:00
feat(core,console): enable backchannel logout
This commit is contained in:
parent
da5c71d916
commit
f28a083ed0
7 changed files with 105 additions and 17 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -54,6 +54,7 @@
|
||||||
"timestamptz",
|
"timestamptz",
|
||||||
"topbar",
|
"topbar",
|
||||||
"upsell",
|
"upsell",
|
||||||
"withtyped"
|
"withtyped",
|
||||||
|
"backchannel"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { type Application } from '@logto/schemas';
|
||||||
|
import { useFormContext } from 'react-hook-form';
|
||||||
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import FormCard from '@/components/FormCard';
|
||||||
|
import FormField from '@/ds-components/FormField';
|
||||||
|
import Switch from '@/ds-components/Switch';
|
||||||
|
import TextInput from '@/ds-components/TextInput';
|
||||||
|
|
||||||
|
function BackchannelLogout() {
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
formState: { errors },
|
||||||
|
} = useFormContext<Application>();
|
||||||
|
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormCard
|
||||||
|
title="application_details.backchannel_logout"
|
||||||
|
description="application_details.backchannel_logout_description"
|
||||||
|
learnMoreLink={{ href: 'https://openid.net/specs/openid-connect-backchannel-1_0-final.html' }}
|
||||||
|
>
|
||||||
|
<FormField title="application_details.backchannel_logout_uri">
|
||||||
|
<TextInput
|
||||||
|
error={errors.oidcClientMetadata?.backchannelLogoutUri?.message}
|
||||||
|
placeholder="https://your.website.com/backchannel_logout"
|
||||||
|
{...register('oidcClientMetadata.backchannelLogoutUri', {
|
||||||
|
validate: (value) =>
|
||||||
|
z.string().url().optional().safeParse(value).success ||
|
||||||
|
t('errors.invalid_uri_format'),
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</FormField>
|
||||||
|
<FormField title="application_details.backchannel_logout_uri_session_required">
|
||||||
|
<Switch
|
||||||
|
label={
|
||||||
|
<Trans i18nKey="admin_console.application_details.backchannel_logout_uri_session_required_description" />
|
||||||
|
}
|
||||||
|
{...register('oidcClientMetadata.backchannelLogoutSessionRequired')}
|
||||||
|
/>
|
||||||
|
</FormField>
|
||||||
|
</FormCard>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BackchannelLogout;
|
|
@ -36,6 +36,7 @@ import RefreshTokenSettings from './RefreshTokenSettings';
|
||||||
import Settings from './Settings';
|
import Settings from './Settings';
|
||||||
import * as styles from './index.module.scss';
|
import * as styles from './index.module.scss';
|
||||||
import { type ApplicationForm, applicationFormDataParser } from './utils';
|
import { type ApplicationForm, applicationFormDataParser } from './utils';
|
||||||
|
import BackchannelLogout from './BackchannelLogout';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
readonly data: ApplicationResponse;
|
readonly data: ApplicationResponse;
|
||||||
|
@ -204,6 +205,7 @@ function ApplicationDetailsContent({ data, oidcConfig, onApplicationUpdated }: P
|
||||||
{![ApplicationType.MachineToMachine, ApplicationType.Protected].includes(data.type) && (
|
{![ApplicationType.MachineToMachine, ApplicationType.Protected].includes(data.type) && (
|
||||||
<RefreshTokenSettings data={data} />
|
<RefreshTokenSettings data={data} />
|
||||||
)}
|
)}
|
||||||
|
<BackchannelLogout />
|
||||||
</DetailsForm>
|
</DetailsForm>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
{tab === ApplicationDetailsTabs.Settings && (
|
{tab === ApplicationDetailsTabs.Settings && (
|
||||||
|
|
|
@ -112,6 +112,7 @@ export default function initOidc(
|
||||||
introspection: { enabled: true },
|
introspection: { enabled: true },
|
||||||
devInteractions: { enabled: false },
|
devInteractions: { enabled: false },
|
||||||
clientCredentials: { enabled: true },
|
clientCredentials: { enabled: true },
|
||||||
|
backchannelLogout: { enabled: true },
|
||||||
rpInitiatedLogout: {
|
rpInitiatedLogout: {
|
||||||
logoutSource: (ctx, form) => {
|
logoutSource: (ctx, form) => {
|
||||||
// eslint-disable-next-line no-template-curly-in-string
|
// eslint-disable-next-line no-template-curly-in-string
|
||||||
|
|
|
@ -22,8 +22,8 @@ const application_details = {
|
||||||
application_name_placeholder: 'My App',
|
application_name_placeholder: 'My App',
|
||||||
description: 'Description',
|
description: 'Description',
|
||||||
description_placeholder: 'Enter your application description',
|
description_placeholder: 'Enter your application description',
|
||||||
config_endpoint: 'OpenID Provider configuration endpoint',
|
config_endpoint: 'OpenID provider configuration endpoint',
|
||||||
authorization_endpoint: 'Authorization Endpoint',
|
authorization_endpoint: 'Authorization endpoint',
|
||||||
authorization_endpoint_tip:
|
authorization_endpoint_tip:
|
||||||
"The endpoint to perform authentication and authorization. It's used for OpenID Connect <a>Authentication</a>.",
|
"The endpoint to perform authentication and authorization. It's used for OpenID Connect <a>Authentication</a>.",
|
||||||
show_endpoint_details: 'Show endpoint details',
|
show_endpoint_details: 'Show endpoint details',
|
||||||
|
@ -39,8 +39,8 @@ const application_details = {
|
||||||
redirect_uri_placeholder_native: 'io.logto://callback',
|
redirect_uri_placeholder_native: 'io.logto://callback',
|
||||||
redirect_uri_tip:
|
redirect_uri_tip:
|
||||||
'The URI redirects after a user sign-in (whether successful or not). See OpenID Connect <a>AuthRequest</a> for more info.',
|
'The URI redirects after a user sign-in (whether successful or not). See OpenID Connect <a>AuthRequest</a> for more info.',
|
||||||
post_sign_out_redirect_uri: 'Post Sign-out Redirect URI',
|
post_sign_out_redirect_uri: 'Post sign-out redirect URI',
|
||||||
post_sign_out_redirect_uris: 'Post Sign-out Redirect URIs',
|
post_sign_out_redirect_uris: 'Post sign-out redirect URIs',
|
||||||
post_sign_out_redirect_uri_placeholder: 'https://your.website.com/home',
|
post_sign_out_redirect_uri_placeholder: 'https://your.website.com/home',
|
||||||
post_sign_out_redirect_uri_tip:
|
post_sign_out_redirect_uri_tip:
|
||||||
'The URI redirects after a user sign-out (optional). It may have no practical effect in some app types.',
|
'The URI redirects after a user sign-out (optional). It may have no practical effect in some app types.',
|
||||||
|
@ -48,20 +48,27 @@ const application_details = {
|
||||||
cors_allowed_origins_placeholder: 'https://your.website.com',
|
cors_allowed_origins_placeholder: 'https://your.website.com',
|
||||||
cors_allowed_origins_tip:
|
cors_allowed_origins_tip:
|
||||||
'By default, all the origins of Redirect URIs will be allowed. Usually no action is required for this field. See the <a>MDN doc</a> for detailed info.',
|
'By default, all the origins of Redirect URIs will be allowed. Usually no action is required for this field. See the <a>MDN doc</a> for detailed info.',
|
||||||
token_endpoint: 'Token Endpoint',
|
token_endpoint: 'Token endpoint',
|
||||||
user_info_endpoint: 'Userinfo Endpoint',
|
user_info_endpoint: 'Userinfo endpoint',
|
||||||
enable_admin_access: 'Enable admin access',
|
enable_admin_access: 'Enable admin access',
|
||||||
enable_admin_access_label:
|
enable_admin_access_label:
|
||||||
'Enable or disable the access to Management API. Once enabled, you can use access tokens to call Management API on behalf on this application.',
|
'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: 'Always issue refresh token',
|
||||||
always_issue_refresh_token_label:
|
always_issue_refresh_token_label:
|
||||||
'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.',
|
'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: 'Refresh token time to live (TTL) in days',
|
||||||
refresh_token_ttl_tip:
|
refresh_token_ttl_tip:
|
||||||
'The duration for which a Refresh Token can be used to request new access tokens before it expires and becomes invalid. Token requests will extend the TTL of the Refresh Token to this value.',
|
'The duration for which a refresh token can be used to request new access tokens before it expires and becomes invalid. Token requests will extend the TTL of the refresh token to this value.',
|
||||||
rotate_refresh_token: 'Rotate Refresh Token',
|
rotate_refresh_token: 'Rotate refresh token',
|
||||||
rotate_refresh_token_label:
|
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 or certain conditions are met. <a>Learn more</a>',
|
'When enabled, Logto will issue a new refresh token for token requests when 70% of the original time to live (TTL) has passed or certain conditions are met. <a>Learn more</a>',
|
||||||
|
backchannel_logout: 'Backchannel Logout',
|
||||||
|
backchannel_logout_description:
|
||||||
|
'Configure the OpenID Connect backchannel logout endpoint and if session is required for this application.',
|
||||||
|
backchannel_logout_uri: 'Backchannel logout URI',
|
||||||
|
backchannel_logout_uri_session_required: 'Is session required?',
|
||||||
|
backchannel_logout_uri_session_required_description:
|
||||||
|
'When enabled, the RP requires that a `sid` (session ID) claim be included in the logout token to identify the RP session with the OP when the `backchannel_logout_uri` is used.',
|
||||||
delete_description:
|
delete_description:
|
||||||
'This action cannot be undone. It will permanently delete the application. Please enter the application name <span>{{name}}</span> to confirm.',
|
'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',
|
enter_your_application_name: 'Enter your application name',
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { validateRedirectUrl } from '@logto/core-kit';
|
import { validateRedirectUrl } from '@logto/core-kit';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { type ToZodObject } from '../../utils/zod.js';
|
||||||
|
|
||||||
export const oidcModelInstancePayloadGuard = z
|
export const oidcModelInstancePayloadGuard = z
|
||||||
.object({
|
.object({
|
||||||
userCode: z.string().optional(),
|
userCode: z.string().optional(),
|
||||||
|
@ -15,6 +17,34 @@ export const oidcModelInstancePayloadGuard = z
|
||||||
|
|
||||||
export type OidcModelInstancePayload = z.infer<typeof oidcModelInstancePayloadGuard>;
|
export type OidcModelInstancePayload = z.infer<typeof oidcModelInstancePayloadGuard>;
|
||||||
|
|
||||||
|
export type OidcClientMetadata = {
|
||||||
|
/**
|
||||||
|
* The redirect URIs that the client is allowed to use.
|
||||||
|
*
|
||||||
|
* @see {@link https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata | OpenID Connect Dynamic Client Registration 1.0}
|
||||||
|
*/
|
||||||
|
redirectUris: string[];
|
||||||
|
/**
|
||||||
|
* The post-logout redirect URIs that the client is allowed to use.
|
||||||
|
*
|
||||||
|
* @see {@link https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ClientMetadata | OpenID Connect RP-Initiated Logout 1.0}
|
||||||
|
*/
|
||||||
|
postLogoutRedirectUris: string[];
|
||||||
|
/**
|
||||||
|
* The URI for backchannel logout.
|
||||||
|
*
|
||||||
|
* @see {@link https://openid.net/specs/openid-connect-backchannel-1_0-final.html#BCRegistration | OpenID Connect Back-Channel Logout 1.0}
|
||||||
|
*/
|
||||||
|
backchannelLogoutUri?: string;
|
||||||
|
/**
|
||||||
|
* Whether the RP requires that a `sid` (session ID) Claim be included in the Logout Token.
|
||||||
|
*
|
||||||
|
* @see {@link https://openid.net/specs/openid-connect-backchannel-1_0-final.html#BCRegistration | OpenID Connect Back-Channel Logout 1.0}
|
||||||
|
*/
|
||||||
|
backchannelLogoutSessionRequired?: boolean;
|
||||||
|
logoUri?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export const oidcClientMetadataGuard = z.object({
|
export const oidcClientMetadataGuard = z.object({
|
||||||
redirectUris: z
|
redirectUris: z
|
||||||
.string()
|
.string()
|
||||||
|
@ -22,10 +52,10 @@ export const oidcClientMetadataGuard = z.object({
|
||||||
.or(z.string().refine((url) => validateRedirectUrl(url, 'mobile')))
|
.or(z.string().refine((url) => validateRedirectUrl(url, 'mobile')))
|
||||||
.array(),
|
.array(),
|
||||||
postLogoutRedirectUris: z.string().url().array(),
|
postLogoutRedirectUris: z.string().url().array(),
|
||||||
|
backchannelLogoutUri: z.string().url().optional(),
|
||||||
|
backchannelLogoutSessionRequired: z.boolean().optional(),
|
||||||
logoUri: z.string().optional(),
|
logoUri: z.string().optional(),
|
||||||
});
|
}) satisfies ToZodObject<OidcClientMetadata>;
|
||||||
|
|
||||||
export type OidcClientMetadata = z.infer<typeof oidcClientMetadataGuard>;
|
|
||||||
|
|
||||||
export enum CustomClientMetadataKey {
|
export enum CustomClientMetadataKey {
|
||||||
CorsAllowedOrigins = 'corsAllowedOrigins',
|
CorsAllowedOrigins = 'corsAllowedOrigins',
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { type z } from 'zod';
|
import { type z } from 'zod';
|
||||||
|
|
||||||
export type ToZodObject<T> = z.ZodObject<{
|
export type ToZodObject<T> = z.ZodObject<{
|
||||||
[K in keyof T]: z.ZodType<T[K]>;
|
[K in keyof T]-?: z.ZodType<T[K]>;
|
||||||
}>;
|
}>;
|
||||||
|
|
Loading…
Add table
Reference in a new issue