Client authentication

Authenticate browser extensions, CLIs, desktop apps, and mobile clients through system-browser authorization, PKCE, rotation, and device-aware revocation.

Supported approaches

Authorization code with PKCE

available

Reuse the product's web authentication, MFA, SSO, and consent screens without embedding credentials in a public client.

Packages

@playstack/client-auth@playstack/auth@playstack/nest-auth

Frameworks and integrations

Native and extension callbacks

available

Complete authorization through extension redirects, mobile sessions, or verified loopback listeners with explicit state handling.

Frameworks and integrations

Device authorization flow

preview

S256-bound device grants, explicit consent, bounded polling and transactional one-time issuance.

Token rotation and revocation

available

Store credentials in platform-appropriate boundaries, rotate refresh families, and connect every client session to a revocable device.

Frameworks and integrations

framework

React

Headless domain bindings plus optional Chakra application and admin compositions, with server authority kept outside the client.

framework

Next.js

Compose Playstack server contracts, React bindings, SSR handoff, and route adapters at the Next.js application edge.

framework

NestJS

Connect portable Playstack capabilities to dependency injection, guards, decorators, request context, workers, and lifecycle hooks.

Package reference

@playstack/client-auth

Public clients cannot keep a secret

Extension bundles, desktop binaries, mobile applications, and installed CLIs are inspectable. Client authentication therefore uses authorization code with PKCE rather than pretending a bundled client secret is confidential.

The client opens the product's own authorization page in the system browser. Existing sessions, passkeys, MFA, SSO, and consent remain web application responsibilities.

One contract, client-specific launchers

ts
import {
  createClientAuth,
  createFetchTokenTransport,
} from '@playstack/client-auth'

const auth = createClientAuth({
  clientId: 'desktop-app',
  authorizationEndpoint: 'https://app.example.com/oauth/authorize',
  redirectUri,
  scopes: ['profile', 'devices'],
  transport: createFetchTokenTransport({
    tokenEndpoint: 'https://api.example.com/oauth/token',
    revocationEndpoint: 'https://api.example.com/oauth/revoke',
  }),
  storage: platformCredentialStorage,
  launcher: systemBrowserLauncher,
})

await auth.authorize()
const response = await auth.request(request, apiTransport)
await auth.signOut()

Browser extensions replace the launcher and callback with launchWebAuthFlow; mobile uses the platform authentication session; a CLI can fall back to the device authorization flow when a loopback callback is unavailable.

Authentication is not pairing

Client authentication establishes a session between one client and the application API. Device pairing establishes mutual trust between two peers. A desktop companion and extension may need both, and the contracts compose without sharing secrets or pretending they solve the same problem.

Package boundary

The authorization-server additions belong to @playstack/auth and @playstack/nest-auth. @playstack/client-auth owns launch, callback, exchange, storage, refresh, authenticated-request retry, and revocation on the installed client. Its Node-only loopback entrypoint binds an ephemeral 127.0.0.1 callback, accepts one response, and closes. Device authorization now ships through @playstack/auth/device, @playstack/client-auth/device and Nest integration. Successful exchange is one-time: a lost committed token response requires a new authorization flow, not recovery of the same secret.

Go

Playstack Pro tag
OriginsPricingBlogNewsletterChangelogStatusRoadmap
ContributorsCommunityIn Use ShowcaseCase StudiesPartnersSponsors
FAQsSupportContact

© 2026 Playstack. All rights reserved.

With OSS
Terms of ServicePrivacy PolicyCookie PolicyImprint

By

Commune Software