OAuth 2.0 Authentication
Roubler's API is secured using OAuth 2.0 and OpenID Connect.
- How OAuth 2.0 Authorization Code Flow Works
- OAuth 2.0 Flow Diagram
- Quick Start: Step-by-Step Authentication Flow
- Detailed Configuration
- Token Management
- Testing Your Authentication
- Common Pitfalls
- Troubleshooting
- Security Best Practices
- Environment-Specific Notes
- Frequently Asked Questions
How OAuth 2.0 Authorization Code Flow Works
At a high-level, the authorization code flow has the following steps:
- Your application directs the browser to the authorization page
- The user authenticates and approves your application's request
- The user is redirected back to your application with an authorization code in the query string
- Your application sends this code to exchange for an API access token and a refresh token
- Your application can now use these tokens to call the API on behalf of the user
OAuth 2.0 Flow Diagram
Quick Start: Step-by-Step Authentication Flow
Step 1: Get Your Credentials
Before you begin, you'll need these credentials from Roubler:
CLIENT_IDCLIENT_SECRETDOMAIN(specific to your environment)- API user credentials (username/password)
Step 2: Choose Your Environment
Select the appropriate environment and domain:
| Environment | Domain | API URL | Description |
|---|---|---|---|
| Staging AU | oidc.staging.roubler.net |
https://graphql.au.staging.roubler.net/graphql |
Sandbox environment for AU region |
| Staging EU | oidc.staging.roubler.net |
https://graphql.eu.staging.roubler.net/graphql |
Sandbox environment for EU region |
| Production AU | oidc.roubler.com |
https://graphql.au.roubler.com/graphql |
Production environment for AU region |
| Production EU | oidc.roubler.com |
https://graphql.eu.roubler.com/graphql |
Production environment for EU region |
Step 3: Get Authorization Code
Visit this URL in your browser (replace <DOMAIN> and <CLIENT_ID> with your values):
https://<DOMAIN>/oauth2/auth?response_type=code&client_id=<CLIENT_ID>&redirect_uri=http://localhost/callback&scope=offline_access&state=123456789
- Login with your API user credentials
- You'll be redirected to
http://localhost/callback?code=AUTHORIZATION_CODE&state=123456789 - Copy the
codeparameter from the URL
Important: This documentation uses the state 123456789 as an example. You should use a unique string or UUID for each request.
Step 4: Exchange Code for Tokens
Make a POST request to exchange your authorization code for access and refresh tokens:
curl -X POST "https://<DOMAIN>/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Authorization: Basic <BASE64_ENCODED_CLIENT_ID_AND_SECRET>" \
-d "grant_type=authorization_code" \
-d "code=<AUTHORIZATION_CODE>" \
-d "redirect_uri=http://localhost/callback"
To create the Base64 encoded credentials:
echo -n "your_client_id:your_client_secret" | base64
Step 5: Test Your Authentication
Run the viewer query to verify everything is working:
query Viewer {
viewer {
user {
id
fullName
email
}
employees {
company {
id
name
}
location {
id
name
}
}
}
}
Step 6: Use Your Access Token
Include your access token in all API requests:
Authorization: Bearer <access_token>
X-Company-ID: <company_id>
Understanding X-Company-ID:
- Purpose: Tells the API which company's data to access (required for most operations)
- Where to get it: Run the
viewerquery to see available companies and their IDs - Multi-tenant support: If you have access to multiple companies, you can switch between them by changing this header
- Always required: Even if you only have access to one company, you must include this header
Getting your company ID:
- Run the viewer query from Step 5
- Look at the
employees[].company.idvalues in the response - Use one of those company IDs in your
X-Company-IDheader
Example request:
POST https://graphql.au.staging.roubler.net/graphql
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
X-Company-ID: 789
Content-Type: application/json
{
"query": "query { employees { id fullName } }"
}
Detailed Configuration
OAuth 2.0 Parameters
| Parameter | Value | Description |
|---|---|---|
| Auth URL | <DOMAIN>/oauth2/auth |
Authorization endpoint |
| Token URL | <DOMAIN>/oauth2/token |
Token exchange endpoint |
| Callback URL | http://localhost/callback |
Redirect URI (additional URLs can be added on request) |
| Client ID | CLIENT_ID |
Provided by Roubler |
| Client Secret | CLIENT_SECRET |
Provided by Roubler |
| Scope | offline_access |
Required scope for refresh tokens |
| State | Random string/UUID |
Security parameter (use a unique value for each request) |
Token Response
When successful, you'll receive:
{
"access_token": "your_access_token_here",
"refresh_token": "your_refresh_token_here",
"expires_in": 3600,
"token_type": "Bearer"
}
access_token: Use this to authenticate API calls (expires in 1 hour)refresh_token: Use this to get new access tokens (expires in 30 days)expires_in: Time until access token expires (in seconds)token_type: Always "Bearer"
Token Management
Refreshing Access Tokens
When to refresh:
- Access tokens expire after 1 hour
- Use your refresh token to get a new access token
- Each refresh token can only be used once
Manual testing with curl:
curl -X POST "https://<DOMAIN>/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Authorization: Basic <BASE64_ENCODED_CLIENT_ID_AND_SECRET>" \
-d "grant_type=refresh_token" \
-d "refresh_token=<REFRESH_TOKEN>"
Response:
{
"access_token": "new_access_token_here",
"refresh_token": "new_refresh_token_here",
"expires_in": 3600,
"token_type": "Bearer"
}
Important: Always store the new refresh token - the old one becomes invalid.
Reactive vs Proactive Token Management
Reactive Approach:
- Detect when the access token is about to expire and refresh it
- Monitor token expiration time and refresh before expiry
Proactive Approach:
- Refresh the token at the start of each operation or session
- Always ensure you have a fresh token before making API calls
Reactive Implementation (javascript):
// Check if token expires within the next 5 minutes
const tokenExpiryTime = tokenIssuedAt + expiresIn * 1000;
const refreshThreshold = 5 * 60 * 1000; // 5 minutes
if (Date.now() > tokenExpiryTime - refreshThreshold) {
// Refresh token before it expires
await refreshAccessToken();
}
Proactive Implementation (javascript):
// Refresh token at the start of each session/operation
async function makeApiCall() {
await refreshAccessToken(); // Always refresh first
return await callRoublerAPI();
}
Testing Your Authentication
After obtaining your access token, the first query you should run is the viewer query to verify your authentication and understand your access level.
What is the Viewer Query?
The viewer query returns information about your API user account and the employee records that your API credentials have access to, along with their company and location details. This helps you understand what data you can query and which company/location contexts are available for your integration.
Basic Viewer Query
query Viewer {
viewer {
user {
id
fullName
email
}
employees {
company {
id
name
}
location {
id
name
}
}
}
}
Example Response
{
"data": {
"viewer": {
"user": {
"id": "123",
"fullName": "API User",
"email": "api@company.com"
},
"employees": [
{
"company": {
"id": "789",
"name": "Acme Corporation"
},
"location": {
"id": "10101",
"name": "Head Office"
}
}
]
}
}
}
Error Handling
If the viewer query fails, check:
- Authentication: Verify your access token is valid and not expired
- Headers: Ensure you're including the
Authorization: Bearer <token>header - Token Refresh: If you get a 401 error, refresh your access token
Next Steps
After successfully running the viewer query:
- Note your company IDs - Use these for company-specific queries with the
X-Company-IDheader - Note your location IDs - Use these for location-specific queries with the
X-Location-IDheader - Test a simple query - Try a basic employee or locations query to verify full API access
Understanding the Response
user: Your API user account informationemployees: All employees you have access to, including their company and location contextcompany: Company information for each employeelocation: Location information for each employee
Using the Viewer Query
- Authentication Verification: If this query succeeds, your authentication is working correctly
- Context Discovery: Use the company and location IDs for subsequent API calls
- Multi-tenant Setup: If you have access to multiple companies/locations, you can switch contexts
Understanding X-Company-ID and X-Location-ID Headers
Most API endpoints require one of these headers to scope your request:
X-Company-ID (Recommended):
- Purpose: Scopes requests to a specific company's data
- Required for: Most API operations
- Where to get it: Run the
viewerquery and use theemployees[].company.idvalues - Usage: Include as a header:
X-Company-ID: <company_id> - Recommendation: Use this as your default header for all API requests
X-Location-ID (Use for specific endpoints):
- Purpose: Scopes requests to a specific location within a company
- When to use: Some endpoints (like
locationSales) don't accept alocationIdparameter and require this header instead - Where to get it: Run the
viewerquery and use theemployees[].location.idvalues or query thelocationsendpoint - Usage: Include as a header:
X-Location-ID: <location_id>
Which Header Should I Use?
- For most endpoints: Use
X-Company-IDand pass location-specific IDs as query parameters (e.g.,locationId,employeeId) - For endpoints without location parameters: Use
X-Location-IDwhen the query doesn't have alocationIdparameter (e.g.,locationSales) - If unsure: Check the endpoint documentation's "Implementation Notes" for guidance
- Never provide both: Only use one header at a time to avoid confusion
Example Request with X-Company-ID:
POST https://graphql.au.staging.roubler.net/graphql
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
X-Company-ID: 789
Content-Type: application/json
{
"query": "query { employees { id fullName } }"
}
Example Request with X-Location-ID (For queries without locationId parameter):
POST https://graphql.au.staging.roubler.net/graphql
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
X-Location-ID: 10101
Content-Type: application/json
{
"query": "query LocationSales($startTime: DateTime!, $endTime: DateTime!) { locationSales(startTime: $startTime, endTime: $endTime) { day forecast actual } }",
"variables": {
"startTime": "2024-05-18T16:00:00.000Z",
"endTime": "2024-05-26T17:59:59.000Z"
}
}
Common Pitfalls
Token Management Mistakes:
- Not storing refresh tokens securely
- Trying to reuse refresh tokens
- Not handling token refresh failures gracefully
Header Issues:
- Forgetting the X-Company-ID header
- Using expired access tokens
- Incorrect Authorization header format
State Parameter Issues:
- Using the same state value for multiple requests
- Not validating the returned state matches the sent state
- Using predictable state values (use UUIDs)
Troubleshooting
"Invalid client" error:
- Check your CLIENT_ID is correct
- Verify you're using the right environment domain
"Invalid grant" error:
- Authorization code may have expired (they expire after 5 mins)
- Code may have already been used
- Check your CLIENT_SECRET is correct
"Invalid redirect_uri" or "redirect_uri_mismatch" error:
- The callback URL in your request doesn't match what's configured for your client
- Check you're using exactly
http://localhost/callback(or your configured URL) - Ensure the URL is registered with Roubler for your CLIENT_ID
- Common mistakes: using
https://instead ofhttp://, different ports, trailing slashes
"Access denied" on API calls:
- Verify X-Company-ID header is included
- Check the company ID exists in your viewer query response
- Ensure your access token hasn't expired
Security Best Practices
- Never expose client secrets in client-side code
- Use HTTPS only for all OAuth endpoints
- Validate state parameters to prevent CSRF attacks
- Store tokens securely using encrypted storage
- Implement proper error handling without exposing sensitive information
- Use environment variables for credentials in production
Environment-Specific Notes
Staging Environment:
- Use your staging credentials
- Use for development and testing
- Test data may be reset periodically but not wihtout prior notice
Production Environment:
- Use your production credentials
- Use production URLs
- Monitor rate limits carefully
- Implement proper error monitoring
Frequently Asked Questions
How long does the access token last?
1 hour.
How long does the refresh token last?
30 days or until the refresh token is exchanged for a new access token.
Do I need to re-authenticate/complete 2FA handshake again when tokens expire?
No, you only need to authenticate and complete the 2FA handshake once, when you first request the authorization code in the browser.
How many times can I use my refresh token?
Once. A new refresh token is provided with each new access token.
Can I refresh the access token concurrently with the same refresh token?
No, each refresh token is single-use. Attempting to refresh the access token concurrently with the same refresh token may result in errors or unexpected behavior.
Can I generate and manage multiple access/refresh tokens against my API credentials?
Yes, as long as you store the refresh tokens for each access token, you can manage access/refresh tokens individually.
You can also generate an access/refresh token per user who authenticates against your API credentials. As long as you store the refresh tokens for each access token, you can manage access/refresh tokens individually.
Can we use the client credential flow instead of authorization code?
No, we currently only support the authorization code flow.
Can you add a new callback url?
Yes, contact Roubler to add additional URLs.
What are your rate limits?
100 calls per 60 seconds.
Our rate limit, remaining calls and time till reset is returned in the headers. We are currently set to monitoring mode meaning we will not block any subsequent calls over these limits. This is subject to change.