OAuth & Access
OAuth Connect
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://app.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://app.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://app.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 with either token, 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.
curl -X POST "https://app.washeej.com/oauth/revoke" \
-d "token=wat_...or_wrt_..."
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.