The Shuttle OAuth 2.0 API is designed for multi-tenant platforms and service providers who need to manage multiple merchant accounts. This OAuth 2.0 implementation allows your platform to enable individual merchants (tenants) to authorize access to their Shuttle accounts, giving your application the ability to perform operations on their behalf.
Use Cases
This authorization method is ideal for:
- Payment service providers managing multiple merchant accounts
- E-commerce platforms integrating Shuttle payments for their merchants
- Accounting software that needs to access payment data across multiple businesses
- Any multi-tenant SaaS platform requiring access to merchants' Shuttle accounts
How It Works
When a merchant uses your platform, they can authorize your application to access their Shuttle account through OAuth 2.0. Once authorized, your platform receives access tokens that are scoped to that specific merchant's instance, allowing you to make API calls on their behalf.
OAuth Flow Overview
The Shuttle OAuth API supports the Authorization Code flow:
- Authorization Request: Redirect merchants to
/authorizeto grant permissions - Authorization Code: Receive an authorization code via redirect
- Token Exchange: Exchange the code for access and refresh tokens using
/token - API Access: Use access tokens to make authenticated API calls on behalf of the merchant
- Token Management: Refresh expired tokens or revoke unused tokens
Detailed OAuth Walkthrough
This section provides a step-by-step guide for implementing the complete OAuth flow for multi-tenant applications.
Step 1: Initiate Authorization
When a merchant wants to connect their Shuttle account to your platform, redirect them to Shuttle's authorization endpoint.
Request Format:
GET https://merchant.shuttleglobal.com/authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_url=https://yourplatform.com/callback
&scope=payments%20orders // Optional: space-separated scopes
&state=merchant_123_random // Include merchant identifier for tracking
What happens next:
- Merchant is redirected to Shuttle's login page
- Merchant authenticates with their Shuttle credentials
- Merchant reviews and approves the permissions your platform is requesting
- Merchant is redirected back to your
redirect_url
Step 2: Handle the Authorization Response
After the merchant approves access, Shuttle redirects back to your platform with an authorization code.
Success Response:
https://yourplatform.com/callback
?code=AUTH_CODE_HERE
&state=merchant_123_random // Use this to identify which merchant authorized
Error Response:
https://yourplatform.com/callback
?error=access_denied
&error_description=User%20denied%20access
&state=merchant_123_random
Important: The authorization code expires in 15 minutes and can only be used once.
Step 3: Exchange Code for Tokens
Immediately exchange the authorization code for access and refresh tokens. Store these tokens securely associated with the merchant's account in your system.
Request:
POST https://merchant.shuttleglobal.com/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTH_CODE_HERE
&redirect_uri=https://yourplatform.com/callback // Must match Step 1
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRETAlternative using Basic Auth (recommended):
POST https://merchant.shuttleglobal.com/token
Authorization: Basic BASE64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTH_CODE_HERE
&redirect_uri=https://yourplatform.com/callbackSuccess Response:
{
"access_token": "at_1234_h480djs93hd8",
"refresh_token": "rt_1234_8xLOxBtZp8",
"token_type": "Bearer",
"expires_in": 3600
}Step 4: Using Access Tokens for Multi-Tenant API Calls
Use the access token to make API requests on behalf of the specific merchant. Each token is scoped to a particular merchant instance.
Example API Request:
GET https://merchant.shuttleglobal.com/c/api/payments
Authorization: Bearer at_1234_h480djs93hd8Multi-Tenant Token Usage Notes:
- Tokens are instance-specific - each merchant's token only accesses their data
- Works with
/c/api/*endpoints, automatically scoped to the merchant's instance - Token is valid for 1 hour (3600 seconds)
- Store tokens securely mapped to each merchant in your database
- Never mix tokens between different merchants
Step 5: Refreshing Tokens for Multiple Merchants
Manage token refresh for all your connected merchants. Implement automatic refresh before tokens expire.
Request:
POST https://merchant.shuttleglobal.com/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=rt_1234_8xLOxBtZp8
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRETResponse:
{
"access_token": "new_access_token_here",
"token_type": "Bearer",
"expires_in": 3600
}Multi-Tenant Refresh Strategy:
- Implement a background job to refresh tokens before expiry
- Track token expiration times for each merchant
- Handle refresh failures gracefully (merchant may have revoked access)
Step 6: Revoking Tokens
Revoke tokens when a merchant disconnects from your platform or upon their request.
Revoke Access Token:
POST https://merchant.shuttleglobal.com/revoke
Content-Type: application/x-www-form-urlencoded
token=at_1234_h480djs93hd8
&token_type_hint=access_token
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRETRevoke Refresh Token:
POST https://merchant.shuttleglobal.com/revoke
Content-Type: application/x-www-form-urlencoded
token=rt_1234_8xLOxBtZp8
&token_type_hint=refresh_token
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRETComplete Multi-Tenant Example
Here's a complete example for a multi-tenant platform:
// Multi-tenant OAuth implementation
class ShuttleOAuth {
constructor(clientId, clientSecret) {
this.clientId = clientId;
this.clientSecret = clientSecret;
}
// 1. Generate authorization URL for a merchant
getAuthorizationUrl(merchantId, redirectUrl) {
const state = `${merchantId}_${generateRandomString()}`;
// Store state in database for verification
await db.saveOAuthState(merchantId, state);
return `https://merchant.shuttleglobal.com/authorize?` +
`response_type=code&` +
`client_id=${this.clientId}&` +
`redirect_url=${encodeURIComponent(redirectUrl)}&` +
`state=${state}`;
}
// 2. Handle OAuth callback
async handleCallback(code, state) {
// Verify state and get merchant ID
const merchantId = await db.verifyOAuthState(state);
if (!merchantId) throw new Error('Invalid state');
// 3. Exchange code for tokens
const tokens = await this.exchangeCodeForTokens(code, redirectUrl);
// 4. Store tokens for this merchant
await db.saveMerchantTokens(merchantId, {
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
expiresAt: Date.now() + (tokens.expires_in * 1000)
});
return merchantId;
}
// 5. Make API call for a specific merchant
async makeApiCall(merchantId, endpoint) {
const tokens = await db.getMerchantTokens(merchantId);
// Check if token needs refresh
if (Date.now() >= tokens.expiresAt - 300000) { // 5 min buffer
await this.refreshMerchantToken(merchantId);
tokens = await db.getMerchantTokens(merchantId);
}
const response = await fetch(`https://merchant.shuttleglobal.com${endpoint}`, {
headers: {
'Authorization': `Bearer ${tokens.accessToken}`
}
});
if (response.status === 401) {
// Token might be revoked, handle accordingly
await this.handleRevokedToken(merchantId);
}
return response;
}
// 6. Refresh tokens for all merchants (run as cron job)
async refreshExpiringTokens() {
const expiringTokens = await db.getTokensExpiringIn(3600000); // 1 hour
for (const merchant of expiringTokens) {
try {
await this.refreshMerchantToken(merchant.id);
} catch (error) {
console.error(`Failed to refresh token for merchant ${merchant.id}`, error);
// Notify merchant to re-authorize
}
}
}
}Common Multi-Tenant Scenarios
Merchant Onboarding:
- Merchant signs up for your platform
- Merchant initiates Shuttle connection
- Your platform redirects to Shuttle OAuth
- Merchant authorizes access
- Your platform stores tokens for future use
Handling Disconnections:
- Provide UI for merchants to disconnect
- Revoke tokens when merchant disconnects
- Clean up stored tokens from your database
- Handle gracefully if merchant revokes access from Shuttle's side
Error Handling:
- Token expiration: Automatic refresh using refresh tokens
- Invalid tokens: Prompt merchant to re-authorize
- API errors: Differentiate between auth errors and other API errors
- Rate limiting: Implement proper backoff strategies
Security Best Practices for Multi-Tenant Platforms
- Isolate merchant data: Never allow tokens from one merchant to access another's data
- Encrypt stored tokens: Use encryption at rest for all stored OAuth tokens
- Implement token rotation: Regularly refresh tokens even if not expired
- Audit token usage: Log all token usage for security and compliance
- Handle merchant offboarding: Ensure tokens are revoked when merchants leave
- Use HTTPS everywhere: All token handling must be over secure connections
- Validate state parameter: Always verify state to prevent CSRF attacks
- Secure your client secret: Never expose client credentials in client-side code