ClientCasa
API

OAuth Apps

Build a Sign in with ClientCasa integration — registration, the authorization-code flow with PKCE, token refresh, scope selection, and best practices.

Use OAuth 2.0 when your integration needs to act on behalf of a user (e.g. a Zapier-style automation). For server-to-server access without a user, use API keys instead — they're simpler and have the same scope vocabulary.

1. Register your app

In the ClientCasa dashboard, go to Settings → OAuth Apps and click Create app.

You'll provide:

  • App name — displayed on the consent screen
  • Description — explains what your app does, also shown at consent
  • Homepage URL — links from the consent screen back to your site
  • Logo URL — square image, displayed at 40×40 on consent
  • Redirect URIs — must use HTTPS (or http://localhost for development). You can register multiple.
  • Scopes — pick the minimum set your integration needs

After creation, you'll see your client_id and client_secret exactly once. Store the secret somewhere safe — it cannot be recovered. You can rotate it at any time, which invalidates the previous secret immediately.

2. Start the authorization flow

OAuth 2.0 authorization code flow with PKCE. Generate a code verifier (43-128 random URL-safe characters) and its SHA-256 challenge.

const codeVerifier = base64url(crypto.getRandomValues(new Uint8Array(32)))
const codeChallenge = base64url(
  await crypto.subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier))
)

const url = new URL('https://www.clientcasa.com/api/auth/oauth2/authorize')
url.searchParams.set('client_id', YOUR_CLIENT_ID)
url.searchParams.set('response_type', 'code')
url.searchParams.set('redirect_uri', YOUR_REDIRECT_URI)
url.searchParams.set('scope', 'openid offline_access read:clients write:invoices')
url.searchParams.set('state', CSRF_STATE_TOKEN)
url.searchParams.set('code_challenge', codeChallenge)
url.searchParams.set('code_challenge_method', 'S256')

window.location.href = url.toString()

The user signs in (if needed), sees your app's consent screen, and approves. They're redirected back to your redirect URI with ?code=…&state=….

3. Exchange the code for a token

curl -X POST https://www.clientcasa.com/api/auth/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=<the code from step 2>" \
  -d "redirect_uri=<same redirect_uri>" \
  -d "client_id=<your client_id>" \
  -d "client_secret=<your client_secret>" \
  -d "code_verifier=<the verifier from step 2>"

Response:

{
  "access_token": "eyJhbGc...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "rt_xxxxxxxxxx",
  "scope": "openid offline_access read:clients write:invoices"
}

Access tokens are JWTs valid for 1 hour. Refresh tokens are valid for 30 days and can be exchanged for a new access token before expiry.

4. Make API requests

curl https://www.clientcasa.com/api/v1/clients \
  -H "Authorization: Bearer eyJhbGc..."

Every request acts on the organization that was active when the user consented. The organizationId is embedded in the token's organizationId claim.

5. Refresh the token

When the access token nears expiry, exchange the refresh token for a new pair:

curl -X POST https://www.clientcasa.com/api/auth/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "refresh_token=rt_xxxxxxxxxx" \
  -d "client_id=<your client_id>" \
  -d "client_secret=<your client_secret>"

A new refresh token is issued each time; rotate it.

Best practices

Always request the minimum scopes

Users are more likely to consent to a small, well-explained scope list. You can request more scopes later by re-running the authorization flow.

  • Store refresh tokens encrypted at rest
  • Don't bundle the client secret in browser or mobile bundles — backend only
  • Validate the state parameter on callback to prevent CSRF
  • Rotate the client secret if you suspect it leaked; users keep their connections
  • Use a separate OAuth app for dev/staging vs. production

Revocation

Users can revoke your app at any time in Settings → Connected Apps. Once revoked:

  • Their access tokens stop working immediately (401 unauthorized)
  • Their refresh tokens are deleted
  • The user must re-authorize from scratch to reconnect

You can also disable your own app from Settings → OAuth Apps — this prevents new authorizations and invalidates existing tokens.

Reference

EndpointPurpose
GET /api/auth/oauth2/authorizeStart the flow (redirect users here)
POST /api/auth/oauth2/tokenExchange code or refresh for tokens
GET /.well-known/oauth-authorization-serverDiscovery metadata
GET /api/auth/jwksJSON Web Key Set for verifying access token signatures

On this page