Skip to main content

OAuth 2.0

Implement user authorization for applications that act on behalf of users.

Overview​

OAuth 2.0 allows users to grant your application access to their PropertyBase data without sharing their credentials.

Getting Started​

Register Your Application​

  1. Go to Settings > API > OAuth Apps
  2. Click Create OAuth App
  3. Fill in details:
    • App name
    • Description
    • Redirect URIs
    • Scopes needed
  4. Save and note your Client ID and Client Secret

OAuth Endpoints​

EndpointURL
Authorizationhttps://auth.propertybase.ai/oauth/authorize
Tokenhttps://auth.propertybase.ai/oauth/token
Revokehttps://auth.propertybase.ai/oauth/revoke
User Infohttps://api.propertybase.ai/v1/me

Authorization Flow​

Step 1: Redirect to Authorization​

const authUrl = new URL('https://auth.propertybase.ai/oauth/authorize');
authUrl.searchParams.set('client_id', CLIENT_ID);
authUrl.searchParams.set('redirect_uri', 'https://yourapp.com/callback');
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('scope', 'read:leads write:leads read:properties');
authUrl.searchParams.set('state', generateRandomState());

window.location.href = authUrl.toString();

Parameters​

ParameterRequiredDescription
client_idYesYour OAuth client ID
redirect_uriYesCallback URL
response_typeYesMust be code
scopeYesSpace-separated scopes
stateRecommendedCSRF protection

Step 2: User Grants Permission​

User sees authorization page and approves access.

Step 3: Handle Callback​

// https://yourapp.com/callback?code=AUTH_CODE&state=STATE

app.get('/callback', async (req, res) => {
const { code, state } = req.query;

// Verify state matches
if (state !== req.session.oauthState) {
return res.status(400).send('Invalid state');
}

// Exchange code for tokens
const tokens = await exchangeCodeForTokens(code);

// Store tokens securely
await saveTokens(req.user.id, tokens);

res.redirect('/dashboard');
});

Step 4: Exchange Code for Tokens​

async function exchangeCodeForTokens(code: string) {
const response = await fetch('https://auth.propertybase.ai/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grant_type: 'authorization_code',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
code,
redirect_uri: 'https://yourapp.com/callback',
}),
});

return response.json();
}

Response​

{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "dGhpcyBpcyBhIHJlZnJl...",
"scope": "read:leads write:leads read:properties"
}

Using Access Tokens​

API Requests​

const response = await fetch('https://api.propertybase.ai/v1/leads', {
headers: {
'Authorization': `Bearer ${accessToken}`,
},
});

Get User Info​

const response = await fetch('https://api.propertybase.ai/v1/me', {
headers: {
'Authorization': `Bearer ${accessToken}`,
},
});

// Response
{
"id": "user_123",
"email": "user@example.com",
"name": "John Doe",
"workspace_id": "ws_456",
"role": "admin"
}

Refreshing Tokens​

Access tokens expire after 1 hour. Use refresh tokens to get new ones:

async function refreshAccessToken(refreshToken: string) {
const response = await fetch('https://auth.propertybase.ai/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grant_type: 'refresh_token',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
refresh_token: refreshToken,
}),
});

return response.json();
}

Automatic Refresh​

async function apiRequest(url: string, options: RequestInit = {}) {
let tokens = await getStoredTokens();

// Check if access token is expired
if (isTokenExpired(tokens.access_token)) {
tokens = await refreshAccessToken(tokens.refresh_token);
await saveTokens(tokens);
}

return fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${tokens.access_token}`,
},
});
}

Scopes​

ScopeDescription
read:profileRead user profile
read:propertiesRead properties
write:propertiesCreate/update properties
read:leadsRead leads
write:leadsCreate/update leads
read:agentsRead agent data
read:analyticsRead analytics
webhooksManage webhooks

Revoking Access​

User Revokes​

Users can revoke from Settings > Connected Apps.

Programmatic Revoke​

await fetch('https://auth.propertybase.ai/oauth/revoke', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
token: accessToken,
}),
});

Security Best Practices​

  1. Store secrets securely - Never expose client secret
  2. Use HTTPS - All redirect URIs must use HTTPS
  3. Validate state - Prevent CSRF attacks
  4. Minimize scopes - Request only what you need
  5. Secure token storage - Encrypt tokens at rest
  6. Short token lifetime - Refresh tokens regularly

Error Handling​

Authorization Errors​

ErrorDescription
access_deniedUser denied authorization
invalid_requestMissing required parameter
invalid_scopeRequested scope is invalid
server_errorServer error

Token Errors​

ErrorDescription
invalid_grantAuthorization code expired or invalid
invalid_clientClient credentials invalid
invalid_tokenToken is invalid or expired