Skip to content

Commit 89354e8

Browse files
authored
docs: document dynamic app (CIMD) (#1480)
* docs: document dynamic app (CIMD) * docs: clarify permission enforcement for dynamic apps
1 parent dd4be50 commit 89354e8

8 files changed

Lines changed: 158 additions & 2 deletions

File tree

docs/authorization/app-level-access-control.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ For [third-party applications](/integrate-logto/third-party-applications), app-l
9595

9696
If both are configured, the user must pass app-level access control before they can authorize the third-party application.
9797

98+
:::note
99+
App-level access control applies to registered applications only. [Dynamic app](/integrate-logto/third-party-applications/dynamic-apps) clients have no application record, so they cannot be restricted this way. Their access is limited by the permissions you grant to the dynamic app and by user consent.
100+
:::
101+
98102
## Relationship with Protected App \{#relationship-with-protected-app}
99103

100104
For [Protected App](/integrate-logto/protected-app), app-level access control restricts who can pass the Logto authentication layer and reach the protected origin server.

docs/concepts/core-service/configuration.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,16 @@ In default values, `protocol` will be either `http` or `https` according to your
3939

4040
### OIDC provider SSRF protection {#oidc-provider-ssrf-protection}
4141

42-
Logto protects the OIDC provider's outbound requests against server-side request forgery (SSRF) by default. Requests to special-use addresses, including loopback and private network addresses, are blocked. This protection covers relying-party endpoints such as back-channel logout URIs, `jwks_uri`, and `sector_identifier_uri`.
42+
Logto protects the OIDC provider's outbound requests against server-side request forgery (SSRF) by default. Requests to special-use addresses, including loopback and private network addresses, are blocked. This protection covers relying-party endpoints such as back-channel logout URIs, `jwks_uri`, and `sector_identifier_uri`, as well as the client ID metadata documents fetched for [dynamic app](/integrate-logto/third-party-applications/dynamic-apps).
4343

4444
If your self-hosted deployment intentionally needs to reach a trusted relying-party endpoint on a private network, set `OIDC_PROVIDER_SSRF_PROTECTION_DISABLED=true` and restart every Logto instance.
4545

4646
:::caution
4747

4848
This setting disables SSRF protection for all OIDC provider outbound requests, not just one endpoint. Only disable it when all configured relying-party endpoints are trusted and your network controls prevent access to sensitive internal services.
4949

50+
[Dynamic app](/integrate-logto/third-party-applications/dynamic-apps) cannot be enabled while this protection is off, since it fetches metadata documents from URLs provided by the clients themselves.
51+
5052
:::
5153

5254
### Enabling HTTPS {#enabling-https}

docs/end-user-flows/consent-screen.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ Similarly, in Logto, when Logto functions as the IdP, the consent screen is show
2424

2525
<img src="/img/assets/consent-screen-logto-as-idp.png" alt="Consent screen Logto as IdP" />
2626

27+
### Unregistered (dynamic) clients \{#unregistered-dynamic-clients}
28+
29+
If [dynamic app](/integrate-logto/third-party-applications/dynamic-apps) is enabled, clients can connect to your tenant without pre-registration. For these clients, the consent screen shows an additional notice, along with the host of the client ID URL. The client name and logo come from the client's own metadata document, so they can imitate any brand, while the host cannot be forged.
30+
2731
## How to configure the consent screen? \{#how-to-configure-the-consent-screen}
2832

2933
The Logto consent flow allows you to authorize logins from third-party applications. You can customize the branding and permission requests for each OIDC third-party application.

docs/integrate-logto/application-data-structure.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ If you want to integrate your application with Logto, see [Integrate Logto](/int
2020

2121
_Application ID_ is a unique auto-generated key to identify your application in Logto, and is referenced as [client id](https://www.oauth.com/oauth2-servers/client-registration/client-id-secret/) in OAuth 2.0.
2222

23+
:::note
24+
[Dynamic app](/integrate-logto/third-party-applications/dynamic-apps) clients are not registered in Logto. Their client ID is a public HTTPS URL owned by the client, which serves the client metadata document.
25+
:::
26+
2327
### Application types \{#application-types}
2428

2529
An _Application_ can be one of the following application types:

docs/integrate-logto/third-party-applications/README.mdx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ sidebar_position: 4
44
---
55

66
import AuthorizationIcon from '@site/src/assets/authorization.svg';
7+
import CloudConnectionIcon from '@site/src/assets/cloud-connection.svg';
78
import CustomizationIcon from '@site/src/assets/customization.svg';
89
import TabItem from '@theme/TabItem';
910
import Tabs from '@theme/Tabs';
@@ -22,6 +23,13 @@ Logto is an identity service built on the [OpenID Connect (OIDC)](https://auth.w
2223

2324
Thus due to OIDC builds upon [OAuth 2.0](https://auth.wiki/oauth-2.0) adding an authentication layer, you can also integrate third-party app using OAuth protocol.
2425

26+
## Two ways to onboard a third-party app \{#two-ways-to-onboard-a-third-party-app}
27+
28+
- **Register the app in Logto** (this guide): you create the application and configure its permissions and redirect URIs. Use it when you know your partners.
29+
- **[Dynamic app](/integrate-logto/third-party-applications/dynamic-apps)**: clients connect without pre-registration, using a public HTTPS URL that serves their own metadata document as the client ID. Use it when any client may connect, such as MCP clients and AI agents.
30+
31+
Both can be used at the same time.
32+
2533
## Create a third-party application in Logto \{#create-a-third-party-application-in-logto}
2634

2735
1. Go to <CloudLink to="/applications">Console > Applications</CloudLink>.
@@ -134,6 +142,16 @@ These requested permissions will be granted to the third-party applications only
134142
icon: <CustomizationIcon width={20} height={20} />,
135143
},
136144
},
145+
{
146+
type: 'link',
147+
label: 'Dynamic app (CIMD)',
148+
href: '/integrate-logto/third-party-applications/dynamic-apps',
149+
description:
150+
'Allow OAuth clients to connect without pre-registration, using a client ID metadata document URL as their client ID.',
151+
customProps: {
152+
icon: <CloudConnectionIcon width={20} height={20} />,
153+
},
154+
},
137155
]}
138156
/>
139157

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
---
2+
description: Let OAuth clients connect to your tenant without pre-registration by using a client ID metadata document URL as their client ID.
3+
sidebar_label: Dynamic app (CIMD)
4+
sidebar_position: 3
5+
---
6+
7+
# Dynamic app (CIMD)
8+
9+
Dynamic app allows OAuth clients to connect to your tenant without pre-registration. Instead of a client ID issued by Logto, the client uses a public HTTPS URL as its `client_id`. The URL serves a JSON document describing the client, called the [client ID metadata document (CIMD)](https://auth.wiki/client-id-metadata-document). Logto fetches the document and treats the client as a [third-party application](/integrate-logto/third-party-applications).
10+
11+
Dynamic app implements the IETF draft [OAuth Client ID Metadata Document](https://www.ietf.org/archive/id/draft-ietf-oauth-client-id-metadata-document-02.html).
12+
13+
## When to use dynamic app \{#when-to-use-dynamic-app}
14+
15+
Pre-registration works when you know your partners. It does not work when any client may connect, which is common in the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization/client-registration#client-id-metadata-documents) ecosystem: a user asks their AI agent to connect to your service, and the agent has never talked to your tenant before.
16+
17+
With dynamic app, the client publishes its own metadata at a URL it owns, and that URL is its identity. Nothing needs to be created in your tenant beforehand.
18+
19+
| | Registered third-party app | Dynamic app |
20+
| ------------- | -------------------------- | ---------------------------------------- |
21+
| Client ID | Issued by Logto | An HTTPS URL owned by the client |
22+
| Registration | Required | Not required |
23+
| Client secret | Supported | Not supported |
24+
| Permissions | Per application | Shared by all dynamic clients |
25+
| Grant types | Depends on app type | `authorization_code` and `refresh_token` |
26+
27+
Dynamic clients are public clients, so they always use [PKCE](https://auth.wiki/pkce). You can use both models at the same time. A partner you trust can still have a registered app with its own permissions.
28+
29+
## Enable dynamic app \{#enable-dynamic-app}
30+
31+
1. Go to <CloudLink to="/applications">Console > Applications</CloudLink> and open the **Third-party apps** tab.
32+
2. Click **Create application** and select the **Dynamic app** card. It enables a tenant-level feature instead of creating an application.
33+
3. Confirm in the dialog. Once enabled, any OAuth client with a valid public HTTPS client ID URL can start an authorization request for your tenant.
34+
4. Open the dynamic app from the application list and go to the **Permissions** tab to grant permissions.
35+
36+
The dynamic app has no editable name, redirect URIs, or credentials. Each client provides its own in its metadata document.
37+
38+
:::note
39+
Dynamic app requires the [OIDC provider SSRF protection](/concepts/core-service/configuration#oidc-provider-ssrf-protection), since Logto fetches metadata documents from the internet. Self-hosted instances that disable it cannot enable dynamic app.
40+
:::
41+
42+
### Grant permissions \{#grant-permissions}
43+
44+
The **Permissions** tab defines the maximum permissions shared by all dynamic clients. It works like the [permission management](/integrate-logto/third-party-applications/permission-management) of a registered third-party app, with **User** and **Organization** sections.
45+
46+
Requesting a user permission that is not granted results in an error, while API resource and organization permissions that are not granted are ignored. Users also consent only to permissions they have through their [roles](/authorization/role-based-access-control).
47+
48+
Since every dynamic client shares this set, keep it minimal.
49+
50+
## Publish a client ID metadata document \{#publish-a-client-id-metadata-document}
51+
52+
If you are building a client that connects to Logto, host a metadata document and use its URL as your `client_id`. The URL must use the `https` scheme and contain no fragment, user info, or dot path segments. Logto sends a `GET` request to the URL and expects a JSON object.
53+
54+
For example, Claude Code uses `https://claude.ai/oauth/claude-code-client-metadata`, which serves:
55+
56+
```json
57+
{
58+
"client_id": "https://claude.ai/oauth/claude-code-client-metadata",
59+
"client_name": "Claude Code",
60+
"client_uri": "https://claude.ai",
61+
"redirect_uris": ["http://localhost/callback", "http://127.0.0.1/callback"],
62+
"token_endpoint_auth_method": "none"
63+
}
64+
```
65+
66+
The field names are the same as in [OAuth 2.0 Dynamic Client Registration](https://datatracker.ietf.org/doc/html/rfc7591#section-2). Note that:
67+
68+
- `client_id` must be identical to the URL that serves the document.
69+
- Dynamic clients are public clients. The document must not contain `client_secret`, and `token_endpoint_auth_method` must not be a shared-secret method. Use [PKCE](https://auth.wiki/pkce) instead.
70+
- Metadata URIs such as `client_uri`, `logo_uri`, `tos_uri`, and `policy_uri` must be absolute `https` URLs. This does not apply to `redirect_uris`, so native clients can still use loopback addresses like the example above.
71+
- `redirect_uris` are matched as exact strings, except that loopback addresses can be matched with any port. [Wildcard patterns](/integrate-logto/application-data-structure#wildcard-patterns) are also supported.
72+
- `scope`, `grant_types`, and `response_types` are decided by Logto. If the document declares them, the values are ignored. Dynamic clients can only use the authorization code flow and refresh tokens.
73+
74+
Logto caches the document for up to 24 hours, following the `Cache-Control` and `Expires` headers of your response. Set them according to how often you expect to update the document.
75+
76+
## Consent screen \{#consent-screen}
77+
78+
Dynamic clients are third-party applications, so the [consent screen](/end-user-flows/consent-screen) is always shown.
79+
80+
The consent screen also shows a notice that the client is unregistered. The client name and logo come from the metadata document, so they can imitate any brand. The host of the client ID URL is displayed as well, since it is the only part the client cannot forge.
81+
82+
## Manage authorizations \{#manage-authorizations}
83+
84+
Authorizations granted to dynamic clients are regular third-party [grants](/sessions/grants-management). Users can review and revoke them in account settings, and admins can manage them through the Management API. The client ID URL is used to identify the client.
85+
86+
Disabling the dynamic app stops new authorization requests, while existing grants are kept. Revoking a grant requires the client to get user authorization again, but previously issued access tokens may remain valid until they expire.
87+
88+
## Limitations \{#limitations}
89+
90+
- Only the authorization code flow with PKCE and refresh tokens are supported. Client credentials, device flow, and token exchange are not available.
91+
- Permissions and branding cannot be configured per client.
92+
- [App-level access control](/authorization/app-level-access-control) does not apply to dynamic clients, since they have no application record.
93+
94+
## Related resources \{#related-resources}
95+
96+
<Url href="/integrate-logto/third-party-applications">Third-party app (OAuth / OIDC)</Url>
97+
<Url href="/use-cases/ai/mcp-server-enable-third-party-ai-agent-access">
98+
Enable third-party AI agent access to your MCP server
99+
</Url>

docs/sessions/grants-management.mdx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ App grants represent user authorization state for specific client applications.
88

99
Use grants management when users or admins need to review authorized apps and revoke app access without revoking the whole Logto session.
1010

11-
When grants are revoked, previously issued opaque access tokens and refresh tokens for those grants are invalidated.
11+
When grants are revoked, previously issued opaque access tokens and refresh tokens for those grants are invalidated. JWT access tokens are stateless, so previously issued ones remain valid until they expire.
1212

1313
## Choose the right path \{#choose-the-right-path}
1414

@@ -44,6 +44,8 @@ Grant listing endpoints support optional query parameter `appType`:
4444
- `appType=thirdParty`: List third-party app grants only.
4545
- Omit `appType`: Return all active grants.
4646

47+
[Dynamic app](/integrate-logto/third-party-applications/dynamic-apps) clients are third-party applications, so their grants are returned with `appType=thirdParty`. The client ID URL is used as the application ID.
48+
4749
## Manage third-party app authorizations in Console \{#manage-third-party-app-authorizations-in-console}
4850

4951
In Logto Console, use the user details page to view and revoke authorized third-party apps.

docs/use-cases/ai/fragments/_configure-third-party-ai-agent.mdx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ A **third-party app** is an application created by external developers (not the
1010
To learn more, see [Third-party applications](/integrate-logto/third-party-applications).
1111
:::
1212

13+
There are three ways to onboard AI agents:
14+
15+
- [Manually create the app in Console](#manually-create-a-third-party-app-in-logto), for testing or a few known agents.
16+
- [Build a registration service on the Management API](#allow-developers-to-create-third-party-apps-in-logto), if you want to control who gets credentials.
17+
- [Enable dynamic app](#let-any-ai-agent-connect-without-pre-registration), if any agent may connect and you don't want a registration step.
18+
1319
### Allow developers to create third-party apps in Logto \{#allow-developers-to-create-third-party-apps-in-logto}
1420

1521
If you are building a marketplace or want to allow developers to create third-party apps in Logto, you can leverage [Logto Management API](/integrate-logto/interact-with-management-api) to create third-party apps programmatically. This allows developers to register their applications and obtain the necessary credentials for authentication.
@@ -29,6 +35,23 @@ sequenceDiagram
2935

3036
Alternatively, you can manually create third-party apps in Logto Console to get familiar with the process.
3137

38+
### Let any AI agent connect without pre-registration \{#let-any-ai-agent-connect-without-pre-registration}
39+
40+
In an open MCP ecosystem, you usually don't know the agents in advance. [Dynamic app](/integrate-logto/third-party-applications/dynamic-apps) removes the registration step: the agent uses a public HTTPS URL serving its own client metadata document as its `client_id`, and Logto resolves it when the authorization request arrives.
41+
42+
```mermaid
43+
sequenceDiagram
44+
participant Agent as AI agent (MCP client)
45+
participant Logto
46+
participant Metadata as Agent's metadata document URL
47+
Agent->>Logto: Authorization request with the client ID URL
48+
Logto->>Metadata: Fetch the client ID metadata document
49+
Metadata->>Logto: Client name, logo, redirect URIs, etc.
50+
Logto->>Agent: Consent screen, then authorization code
51+
```
52+
53+
You still control what agents can request through the permissions granted to the dynamic app, and every authorization goes through the user consent screen.
54+
3255
### Manually create a third-party app in Logto \{#manually-create-a-third-party-app-in-logto}
3356

3457
You can manually create a third-party app in Logto Console for testing purposes or ad-hoc integrations. This is useful when you want to quickly test the integration without implementing a full client-registration flow.

0 commit comments

Comments
 (0)