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​
- Go to Settings > API > OAuth Apps
- Click Create OAuth App
- Fill in details:
- App name
- Description
- Redirect URIs
- Scopes needed
- Save and note your Client ID and Client Secret
OAuth Endpoints​
| Endpoint | URL |
|---|---|
| Authorization | https://auth.propertybase.ai/oauth/authorize |
| Token | https://auth.propertybase.ai/oauth/token |
| Revoke | https://auth.propertybase.ai/oauth/revoke |
| User Info | https://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​
| Parameter | Required | Description |
|---|---|---|
client_id | Yes | Your OAuth client ID |
redirect_uri | Yes | Callback URL |
response_type | Yes | Must be code |
scope | Yes | Space-separated scopes |
state | Recommended | CSRF 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​
| Scope | Description |
|---|---|
read:profile | Read user profile |
read:properties | Read properties |
write:properties | Create/update properties |
read:leads | Read leads |
write:leads | Create/update leads |
read:agents | Read agent data |
read:analytics | Read analytics |
webhooks | Manage 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​
- Store secrets securely - Never expose client secret
- Use HTTPS - All redirect URIs must use HTTPS
- Validate state - Prevent CSRF attacks
- Minimize scopes - Request only what you need
- Secure token storage - Encrypt tokens at rest
- Short token lifetime - Refresh tokens regularly
Error Handling​
Authorization Errors​
| Error | Description |
|---|---|
access_denied | User denied authorization |
invalid_request | Missing required parameter |
invalid_scope | Requested scope is invalid |
server_error | Server error |
Token Errors​
| Error | Description |
|---|---|
invalid_grant | Authorization code expired or invalid |
invalid_client | Client credentials invalid |
invalid_token | Token is invalid or expired |