OAuth & Access
OAuth 2.0 (Connect with Washeej)
A Meta/Google-style "Connect with Washeej" flow: your button opens a popup with a dedicated Washeej sign-in and consent page, then returns the user to your app with an authorization code.
Prerequisites
- Create a Developer App in the Developer Portal — you get
client_id(wa_…) andclient_secret(was_…). - Register your callback URL as a redirect URI on the app. Apps without registered redirect URIs cannot use the authorize flow.
- Select the scopes your app needs — users can never grant more than the app registered.
Step 1 — Authorize URL
Point your "Connect with Washeej" button at the authorize endpoint. Add display=popup when opening in a popup window.
https://staging-mobile.washeej.com/oauth/authorize?client_id=APP_ID&redirect_uri=REDIRECT_URI&response_type=code&scope=messages:send%20messages:read%20devices:read&state=RANDOM_CSRF&code_challenge=CHALLENGE&code_challenge_method=S256&display=popup
- If the user is not signed in, Washeej shows a dedicated OAuth sign-in page (compact, popup-friendly, shows your app name) — not the public login page. Password and social sign-in both resume the flow.
- The consent screen lists the requested permissions with Allow / Cancel.
- Allow → redirect to
redirect_uri?code=wac_…&state=… - Cancel → redirect to
redirect_uri?error=access_denied&error_description=…&state=…— always handle this case.
Popup pattern (recommended UX)
Open the authorize URL in a popup; your redirect_uri page posts the result back to the opener and closes itself. The code→token exchange must happen on your backend — never expose client_secret in the browser.
// 1. Open the Connect popup
const authorizeUrl = 'AUTHORIZE_URL_FROM_STEP_1';
const popup = window.open(authorizeUrl, 'washeej_connect',
'width=480,height=720,menubar=no,toolbar=no');
// 2. Your redirect_uri page (on YOUR domain) receives ?code=&state=
// and notifies the opener, then closes itself:
// window.opener.postMessage({ source: 'washeej', code, state }, 'https://your-app.example.com');
// window.close();
// 3. Listen for the result in the parent window
window.addEventListener('message', (event) => {
if (event.origin !== 'https://your-app.example.com') return;
if (event.data && event.data.source === 'washeej' && event.data.code) {
// Verify event.data.state matches what you generated, then
// send the code to YOUR backend to exchange it for tokens.
}
});
Step 2 — Exchange the code
Codes expire after 10 minutes and are single-use.
curl -X POST "https://staging-mobile.washeej.com/oauth/token" \
-d "grant_type=authorization_code" \
-d "code=AUTH_CODE" \
-d "redirect_uri=REDIRECT_URI" \
-d "client_id=APP_ID" \
-d "client_secret=APP_SECRET" \
-d "code_verifier=VERIFIER"
{
"access_token": "wat_...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "wrt_...",
"scope": "messages:send messages:read devices:read"
}
Use the access token as Authorization: Bearer wat_… on all API v1 endpoints. Access tokens live 1 hour; refresh tokens live 30 days.
Step 3 — Refresh tokens
Refresh rotates both tokens: the old pair is revoked and a new pair is returned.
curl -X POST "https://staging-mobile.washeej.com/oauth/token" \
-d "grant_type=refresh_token" \
-d "refresh_token=wrt_..." \
-d "client_id=APP_ID" \
-d "client_secret=APP_SECRET"
Disconnect / revoke
Two ways to disconnect at any time:
- From your platform: a "Disconnect" button that calls the revoke endpoint from your backend with either token plus your app
client_idandclient_secret(missing or wrong client credentials return401 invalid_client), then deletes your stored tokens. - From Washeej: the user opens Dashboard → Developer → Connected Apps and revokes your app — your stored tokens stop working, so handle
401 invalid_tokenby prompting to reconnect.
When tokens stop working. Access tokens, refresh tokens and unused authorization codes are refused at once (401 invalid_token on the API, 400 invalid_grant on /oauth/token) while any of these is true:
- the account revoked your app (Connected Apps); approving it again issues new tokens;
- you disabled your app in your Developer Apps; enabling it again makes the same tokens work again;
- you deleted your app;
- the account has no active plan (
403 subscription_required).
Webhooks you register belong to your app. Endpoints your app creates through POST /v1/webhooks are visible to, and manageable by, your app only (and by the account owner). They stop receiving events while your app is disabled or disconnected, and resume when it is enabled or reconnected; they are never deleted automatically. Account-wide endpoints (created by the account in the dashboard or with its API key) stay listed for your app as before.
curl -X POST "https://staging-mobile.washeej.com/oauth/revoke" \
-d "token=wat_...or_wrt_..." \
-d "client_id=APP_ID" \
-d "client_secret=APP_SECRET"
Security notes
- Always send a random
stateand verify it on return (CSRF protection). - PKCE (
code_challenge+code_verifier, S256) is strongly recommended. redirect_urimust exactly match a URI registered on your Developer App — validated on both the authorize page and the approve action.- Granted scopes are the intersection of what you request and what the app registered.