Skip to main content

Create OAuth Apps and Clients

Use this flow for provider setup before any consent or token exchange happens. OAuth app/client creation is available through backend deployment-scoped APIs and both SDKs.

Step 1: Create OAuth app in Console

In Console OAuth settings, create the app with:
  1. Stable app slug (avoid renaming later).
  2. User-facing app name and description.
  3. Verified domain/FQDN.
  4. Supported scopes and scope definitions.

Step 2: Add scopes with clear contracts

Good scope design is specific and additive:
  1. agents:run for agent execution only.
  2. mcp:invoke for MCP tool invocation.
  3. workspace:read for workspace data reads.
Avoid broad scopes like * or ambiguous names.

Step 3: Create OAuth clients

Per environment (dev/staging/prod), create separate OAuth clients:
  1. Register exact redirect URIs.
  2. Use least privilege scopes.
  3. Store client secret only in server-side secret managers.

Create via Node SDK

import { WachtClient } from "@wacht/backend";

const client = new WachtClient({
  apiKey: process.env.WACHT_BACKEND_API_KEY!,
  baseUrl: "https://api.wacht.dev",
});

const app = await client.oauth.createOAuthApp("dep_123", {
  slug: "mcp-auth",
  name: "MCP Auth App",
  description: "OAuth provider for MCP access",
  fqdn: "auth.example.com",
  supported_scopes: ["mcp:invoke", "workspace:read"],
});

const oauthClient = await client.oauth.createOAuthClient("dep_123", "mcp-auth", {
  client_auth_method: "client_secret_basic",
  grant_types: ["authorization_code", "refresh_token"],
  redirect_uris: ["https://app.example.com/oauth/callback"],
});

Create via Rust SDK

use wacht::models::{CreateOAuthAppRequest, CreateOAuthClientRequest};

let app = client.oauth()
    .create_oauth_app(
        "dep_123",
        CreateOAuthAppRequest {
            slug: "mcp-auth".to_string(),
            name: "MCP Auth App".to_string(),
            description: Some("OAuth provider for MCP access".to_string()),
            fqdn: Some("auth.example.com".to_string()),
            supported_scopes: Some(vec!["mcp:invoke".to_string(), "workspace:read".to_string()]),
            scope_definitions: None,
            allow_dynamic_client_registration: Some(false),
            logo_file: None,
            logo_filename: None,
        },
    )
    .send()
    .await?;

let oauth_client = client.oauth()
    .create_oauth_client(
        "dep_123",
        "mcp-auth",
        CreateOAuthClientRequest {
            client_auth_method: "client_secret_basic".to_string(),
            grant_types: vec!["authorization_code".to_string(), "refresh_token".to_string()],
            redirect_uris: vec!["https://app.example.com/oauth/callback".to_string()],
            token_endpoint_auth_signing_alg: None,
            jwks_uri: None,
            jwks: None,
            public_key_pem: None,
        },
    )
    .send()
    .await?;

SDK operations to use

Use these SDK operations in automation flows:
  1. client.oauth.listOAuthApps(deploymentId)
  2. client.oauth.createOAuthApp(deploymentId, request)
  3. client.oauth.updateOAuthApp(deploymentId, oauthAppSlug, request)
  4. client.oauth.createOAuthClient(deploymentId, oauthAppSlug, request)
  5. client.oauth.updateOAuthClient(deploymentId, oauthAppSlug, oauthClientId, request)
  6. client.oauth.rotateOAuthClientSecret(deploymentId, oauthAppSlug, oauthClientId)
Use direct backend endpoints only when SDKs are not available in your runtime.

Production checklist

  1. Different client IDs per environment.
  2. Redirect URIs pinned and reviewed.
  3. Secret rotation policy documented.
  4. Scope names and descriptions versioned in docs.
  5. Deprovisioning flow exists for leaked client secrets.
  1. Frontend OAuth Consent API Reference
  2. Node SDK OAuth Apps API
  3. Rust SDK OAuth Apps Guide
  4. Backend API Reference