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://localhostfor 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
stateparameter 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
| Endpoint | Purpose |
|---|---|
GET /api/auth/oauth2/authorize | Start the flow (redirect users here) |
POST /api/auth/oauth2/token | Exchange code or refresh for tokens |
GET /.well-known/oauth-authorization-server | Discovery metadata |
GET /api/auth/jwks | JSON Web Key Set for verifying access token signatures |