Smoke Signal Blog

Dynamic Client Registration is the Missing Piece for Mobile and CLI Applications

Published by @smokesignal.events on 2025-08-07 19:45 UTC.

ATProtocol takes an interesting approach to OAuth by requiring client_ids to be fully-qualified HTTPS URLs pointing to publicly-accessible JSON metadata documents. This design enables global client identification across all PDS instances without any prior coordination. Developers can build once and authenticate everywhere across the decentralized network.

The system works well for web applications. A developer creates a client metadata document, hosts it at a stable URL, and can immediately start authenticating users across the entire network. No manual registration with each PDS, no waiting for approval processes, no managing different client credentials for different servers. The authorization server fetches and validates the metadata on-demand during the initial flow, automatically registering the client without any prior setup.

The security model incorporates modern standards by mandating DPoP (Demonstrating Proof of Possession) for all clients, which prevents token replay attacks even if tokens are intercepted. The requirement for HTTPS-hosted metadata documents provides cryptographic verification of client identity through existing web PKI infrastructure. This addresses the trust problem in a decentralized network where no central authority exists to validate client registrations.

For web applications, this approach works quite well. The infrastructure requirements align with typical web deployment patterns—if you're hosting a web app, you're already set up to host a metadata document. The browser-based OAuth flows integrate smoothly, and the security model provides good protection against common attack vectors. This foundation works effectively for its intended use case.

Why Mobile and CLI Applications Need More

However, the current implementation creates significant barriers for two critical categories of applications: mobile apps and command-line tools. These applications have fundamentally different operational characteristics and security models that don't align well with the requirement to host public metadata documents.

The Mobile App Dilemma

Mobile applications face a unique challenge: they're distributed through app stores as compiled binaries that users install on their devices, but ATProtocol OAuth requires them to maintain web infrastructure. Consider a simple mobile app for posting to Bluesky—perhaps a specialized photography app or a streamlined reader. Under the current model, this app must:

  1. Maintain a web server solely to host a client metadata document
  2. Ensure 24/7 availability of this infrastructure (if the metadata URL goes down, no users can authenticate)
  3. Handle operational complexity that has nothing to do with the app's core functionality
  4. Absorb hosting costs even for free or open-source applications

This creates a substantial barrier to entry. A talented mobile developer who wants to build a great ATProtocol experience shouldn't need to become a web infrastructure operator. The requirement fundamentally changes the economics of mobile app development—what could be a one-time development effort becomes an ongoing operational commitment.

More critically, the current model doesn't support per-installation credentials, a security best practice for mobile applications. When every installation of an app uses the same client_id (the metadata URL), you lose the ability to:

The CLI Tool Challenge

Command-line tools face even steeper challenges. Developers routinely create CLI tools for automation, research, and system administration. These tools are often:

Requiring each of these tools to host public metadata documents is simply impractical. The localhost exception (http://127.0.0.1/) helps during development but doesn't address production use cases. A researcher wanting to analyze public ATProtocol data, a system administrator automating account management, or a developer building deployment scripts—all face the same barrier: they must operate web infrastructure just to authenticate.

The current 30-minute token lifetime for public clients compounds these issues. CLI tools that perform long-running operations must implement complex token refresh logic, and the 7-day maximum session lifetime means users must frequently re-authenticate through browser flows—a particularly poor experience in headless server environments.

How RFC 7591 Addresses Mobile Authentication Needs

RFC 7591's Dynamic Client Registration protocol, specifically Section A.4.1, provides a comprehensive solution for mobile authentication challenges. The specification explicitly acknowledges that "mobile applications are considered public clients" and provides mechanisms tailored to their unique requirements.

Per-Installation Registration

The RFC's mobile guidance centers on a critical security principle: each installation should obtain its own credentials. Section A.4.1 states:

"Mobile applications that are dynamically registered are able to generate a unique client identifier for each installation, providing device-specific identification."

This approach transforms the security model. Instead of every instance of "SuperPhoto for Bluesky" sharing the same client credentials embedded in the app binary, each installation registers itself with the PDS during initial setup. This provides:

Platform-Specific Redirect URIs

Mobile platforms use various mechanisms for OAuth redirect handling, from custom URL schemes to Universal Links (iOS) and App Links (Android). RFC 7591 accommodates this diversity by allowing clients to register platform-specific redirect URIs during the registration process:

{
  "redirect_uris": [
    "com.example.app://oauth/callback",
    "https://app.example.com/oauth/ios",
    "https://app.example.com/oauth/android"
  ]
}

The specification recognizes that mobile apps cannot use traditional web-based redirects and explicitly supports custom schemes, making it possible for apps to complete OAuth flows without requiring web infrastructure.

Secure Credential Storage

Section A.4.1 emphasizes that dynamically registered credentials should be stored using platform-specific secure storage mechanisms:

"Client credentials obtained via dynamic registration MUST be stored in secure storage appropriate to the platform, such as the iOS Keychain or Android Keystore."

This guidance ensures that even though mobile apps are public clients (cannot keep secrets in their distributed code), the per-installation credentials obtained through DCR can be properly secured using hardware-backed secure enclaves available on modern devices.

JWK-Based Authentication

The RFC supports registering public keys (JWKs) during client registration, enabling mobile apps to use asymmetric cryptography for authentication:

{
  "token_endpoint_auth_method": "private_key_jwt",
  "jwks": {
    "keys": [{
      "kty": "RSA",
      "use": "sig",
      "kid": "device-key-001",
      "n": "...",
      "e": "AQAB"
    }]
  }
}

This is particularly powerful on mobile platforms where modern devices provide hardware-backed key generation and storage. The private key never leaves the secure enclave, providing stronger security than any shared secret approach.

Implementing DCR for ATProtocol: Request and Response Examples

Here's how Dynamic Client Registration could work in practice for ATProtocol PDS implementations, following RFC 7591 while maintaining compatibility with ATProtocol's security model.

Mobile Application Registration Request

When a mobile app launches for the first time, it would generate a unique keypair and register itself with the user's PDS:

POST /oauth/register HTTP/1.1
Host: alice.pds.example.com
Content-Type: application/json

{
  "client_name": "SuperPhoto for Bluesky",
  "client_uri": "https://superphoto.app",
  "logo_uri": "https://superphoto.app/icon.png",
  "redirect_uris": [
    "com.superphoto.app://oauth/callback",
    "https://superphoto.app/oauth/mobile"
  ],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "private_key_jwt",
  "application_type": "native",
  "contacts": ["support@superphoto.app"],
  "policy_uri": "https://superphoto.app/privacy",
  "tos_uri": "https://superphoto.app/terms",
  "jwks": {
    "keys": [{
      "kty": "EC",
      "use": "sig",
      "crv": "P-256",
      "kid": "2024-device-key",
      "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
      "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"
    }]
  },
  "dpop_bound_access_tokens": true,
  "software_id": "com.superphoto.app",
  "software_version": "2.3.1"
}

Successful Registration Response

The PDS would validate the request and return unique credentials for this specific installation:

HTTP/1.1 201 Created
Content-Type: application/json
Cache-Control: no-store

{
  "client_id": "dcr_3h7kb2pd5t8w9r",
  "client_id_issued_at": 1735689600,
  "client_secret": "yk_2KsJ5P9rN3mXvQ7dLwR8T6",
  "client_secret_expires_at": 0,
  "registration_access_token": "reg_7Fjw9kLm3Nv8Qp2Sx5Ty",
  "registration_client_uri": "https://alice.pds.example.com/oauth/clients/dcr_3h7kb2pd5t8w9r",
  "client_name": "SuperPhoto for Bluesky",
  "redirect_uris": [
    "com.superphoto.app://oauth/callback",
    "https://superphoto.app/oauth/mobile"
  ],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "private_key_jwt",
  "application_type": "native",
  "dpop_bound_access_tokens": true,
  "software_id": "com.superphoto.app",
  "software_version": "2.3.1",
  "scope": "atproto",
  "jwks": {
    "keys": [{
      "kty": "EC",
      "use": "sig",
      "crv": "P-256",
      "kid": "2024-device-key",
      "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
      "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"
    }]
  }
}

CLI Tool Registration

Command-line tools could use a simplified registration flow, appropriate for their operational context:

POST /oauth/register HTTP/1.1
Host: alice.pds.example.com
Content-Type: application/json

{
  "client_name": "atproto-cli v1.2.0",
  "grant_types": ["urn:ietf:params:oauth:grant-type:device_code"],
  "response_types": ["device_code"],
  "token_endpoint_auth_method": "none",
  "application_type": "native",
  "software_id": "com.example.atproto-cli",
  "software_version": "1.2.0"
}

The response would include a client_id suitable for device flow authentication:

HTTP/1.1 201 Created
Content-Type: application/json

{
  "client_id": "dcr_cli_8j3kd9sk2",
  "client_id_issued_at": 1735689600,
  "registration_access_token": "reg_Kj39Fk2Lm8Nv",
  "registration_client_uri": "https://alice.pds.example.com/oauth/clients/dcr_cli_8j3kd9sk2",
  "client_name": "atproto-cli v1.2.0",
  "grant_types": ["urn:ietf:params:oauth:grant-type:device_code"],
  "token_endpoint_auth_method": "none",
  "application_type": "native"
}

Integration with Existing ATProtocol OAuth

The beauty of RFC 7591 is that it can coexist with ATProtocol's current URL-based client identification. The PDS could support both:

  1. URL-based clients: Continue to work exactly as they do today
  2. Dynamically registered clients: Use the DCR endpoint for registration

The authorization server would simply check if a client_id is a URL (existing behavior) or a DCR-issued identifier (new behavior). This allows for a smooth transition and backwards compatibility with existing clients while enabling the new functionality for mobile and CLI applications.

Mobile Development Becomes Accessible

With DCR support, mobile developers could focus on what they do best—creating great user experiences—without becoming web infrastructure operators. The registration happens automatically when users first launch the app:

  1. App launches → Checks for stored credentials
  2. No credentials found → Registers with user's PDS
  3. Credentials received → Stored in platform secure storage
  4. Future launches → Use stored credentials

No web servers. No hosting costs. No operational burden. Just a clean, secure authentication flow that works offline-first and provides better security through per-installation credentials.

The security benefits are substantial:

CLI Tools Become Practical

For command-line tools, DCR would eliminate the current friction entirely. Developers could create tools that self-register on first use:

$ atproto-tool sync-posts
First time setup required. Authenticating with your PDS...
Enter your handle: alice.bsky.social
Authenticating... [opens browser for OAuth consent]
 Authentication successful! Credentials saved.
Syncing posts... Done!

The tool registers itself, completes the OAuth flow (using device flow for headless environments), and stores credentials locally. No metadata hosting, no public URLs, no infrastructure requirements.

This enables entire categories of tools that are currently impractical:

Maintaining Ecosystem Innovation

Crucially, adding DCR support wouldn't fragment the ecosystem or complicate the protocol. Web applications would continue using URL-based client identification—the current approach works perfectly for them. Mobile and CLI applications would use DCR where it makes sense. The authorization server handles both seamlessly.

This pragmatic approach acknowledges that different types of applications have different needs. Web apps need global portability across all PDS instances. Mobile apps need per-installation security without infrastructure overhead. CLI tools need simplicity and local operation. One size doesn't fit all, and that's okay.

The Path Forward

The ATProtocol community has already recognized these challenges. GitHub discussions (OAuth Roadmap #2656) show active engagement around OAuth limitations for mobile and CLI use cases. The Bluesky team has acknowledged the need for better solutions. Adding RFC 7591 support represents a natural evolution that:

  1. Preserves the innovation of URL-based client identification for web apps
  2. Adds flexibility for mobile and CLI applications with different needs
  3. Improves security through per-installation credentials
  4. Reduces barriers to ecosystem participation
  5. Maintains compatibility with existing OAuth infrastructure

The implementation work is straightforward—many OAuth libraries already support DCR, and the protocol is well-understood with years of production use across providers like Auth0, Okta, and Keycloak. The PDS reference implementation could add a /oauth/register endpoint that follows RFC 7591, storing registered clients in the same database used for other OAuth state.

Conclusion: Completing the OAuth Story

ATProtocol's URL-based client identification works well for web applications and enables portability across the network. Adding more complete RFC 7591 Dynamic Client Registration support would extend this foundation to properly support mobile and CLI applications.

The benefits are clear: mobile developers could build applications without maintaining web infrastructure, security would improve through per-installation credentials, and CLI tools could operate without public hosting requirements. This isn't about replacing the current system, it's about expanding it to support different application types with different needs.

The technical specification exists, the implementation patterns are proven, and the community has identified the need. Adding Dynamic Client Registration to ATProtocol would be a welcome addition to the ATmosphere.

This post has had 4 interactions.