Zum Hauptinhalt springen

Dynamic client registration

Dynamic Client Registration (DCR) lets your software register an OAuth client with Casdoor in one HTTP request instead of creating an application manually in the admin UI. That helps when you ship tools to end users: MCP clients, CLIs, or desktop apps can obtain client credentials at first run or install. Casdoor implements RFC 7591.

Registration endpoint

The endpoint is advertised in OIDC discovery. Request /.well-known/openid-configuration:

curl https://your-casdoor.com/.well-known/openid-configuration

Use the registration_endpoint value (e.g. /api/oauth/register) for registration:

{
"issuer": "https://your-casdoor.com",
"authorization_endpoint": "https://your-casdoor.com/login/oauth/authorize",
"token_endpoint": "https://your-casdoor.com/api/login/oauth/access_token",
"registration_endpoint": "https://your-casdoor.com/api/oauth/register",
...
}

Registering a client

POST to /api/oauth/register with JSON metadata:

curl -X POST https://your-casdoor.com/api/oauth/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "Claude Desktop",
"redirect_uris": ["http://localhost:3000/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"token_endpoint_auth_method": "none",
"application_type": "native"
}'

Response includes the new client credentials:

{
"client_id": "a1b2c3d4e5f6",
"client_secret": "secret_xyz789...",
"client_id_issued_at": 1737799294,
"client_secret_expires_at": 0,
"redirect_uris": ["http://localhost:3000/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"token_endpoint_auth_method": "none",
"application_type": "native"
}

Store the client_id and client_secret securely—you'll use them for all subsequent OAuth flows.

Request Parameters

Your registration request needs at least one redirect URI. Everything else is optional, with Casdoor applying sensible defaults:

  • redirect_uris (required): Array of allowed callback URLs where Casdoor redirects after authentication
  • client_name: Display name for your application (auto-generated if omitted)
  • grant_types: OAuth grant types your app will use—defaults to ["authorization_code"]
  • token_endpoint_auth_method: How your app authenticates at the token endpoint (none, client_secret_post, or client_secret_basic)
  • application_type: Either web for server-side apps or native for desktop/mobile apps
  • logo_uri: URL to your application's logo
  • client_uri: URL to your application's homepage
  • scope: Space-separated list of OAuth scopes your app requests

Applications created through DCR get a 7-day token expiration and are tagged with dcr for easy identification in the admin interface.

Controlling DCR Per Organization

Organizations control whether DCR is available through the dcrPolicy setting on the organization configuration page. Two values are supported:

  • disabled (default) — registration requests are rejected with an error. An unset/empty dcrPolicy is also treated as disabled, so DCR is off unless you explicitly opt in. Casdoor's built-in organization ships with DCR disabled.
  • open — anyone can register an application in this organization without authentication.

This gives you flexibility: turn DCR on for developer-friendly organizations while keeping it locked down for production environments that require manual oversight. Because the default is closed, an unauthenticated registration endpoint is never exposed until you deliberately enable it.

Security Model

The registration endpoint itself requires no authentication—this is by design for public clients like mobile apps and desktop tools that can't securely store credentials before registration. To keep that unauthenticated surface from being exposed by accident, DCR is disabled by default and must be turned on per organization (see Controlling DCR Per Organization).

Applications created through DCR belong to the organization's admin account and appear in your application list with a dcr tag. This tag is not just a label: DCR-registered applications run under a restricted app-dcr role and can only reach the OAuth/OIDC endpoints they need for the login flow (/api/login/oauth/*, /api/get-oauth-token, /api/userinfo, /api/get-application). They cannot use the client credentials to call other management APIs, which limits the blast radius of a self-registered client.

So that end users can actually sign in to a self-registered app, DCR-registered applications have password sign-in enabled and inherit a set of fields from the organization's default application:

  • Providers and sign-in methods — so there is at least one working sign-in method out of the box.
  • Branding — the logo (only when the request omits logo_uri), theme, footer HTML, and form CSS, so the login page matches the rest of the organization.
  • Sign-in items — the same sign-in form layout as the default application.
  • EnableSigninSession and EnableWebAuthn — the default application's session and WebAuthn settings.

Client secrets never expire by default, but you can revoke any application through the admin interface at any time. For production deployments, consider whether your organization actually needs unauthenticated registration. Many scenarios work fine with manual app creation, and leaving DCR disabled removes a potential abuse vector.

Complete Example: MCP Client

Here's how an MCP client might implement DCR from scratch:

// Discover the registration endpoint
const discovery = await fetch('https://your-casdoor.com/.well-known/openid-configuration')
.then(r => r.json());

// Register the application
const registration = await fetch(discovery.registration_endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
client_name: 'MCP Client',
redirect_uris: ['http://127.0.0.1:6437/callback'],
grant_types: ['authorization_code', 'refresh_token'],
token_endpoint_auth_method: 'none',
application_type: 'native'
})
}).then(r => r.json());

// Store credentials for OAuth flows
const { client_id, client_secret } = registration;

With these credentials, the client proceeds through the standard OAuth authorization code flow. The user authenticates in their browser, Casdoor redirects back to your callback URL with an authorization code, and you exchange it for access tokens.

Handling Registration Failures

When something goes wrong, Casdoor returns RFC 7591 compliant errors with an error code and human-readable error_description. The most common issues: missing redirect URIs (invalid_redirect_uri), malformed parameters (invalid_client_metadata), or DCR being disabled for the organization (access_denied). Check the description field for specifics on what needs to be fixed.