Authentication
OAuth 2.0 Flows
There are 4 different OAuth 2 authenticaiton mechanism available and each with specific use case:
- Device Code Authorization
- For dumb terminal authentication
- Use this when your target customer’s authentication device has limited capability
- Example includes customers with no sophisticated phones but have access to USSD or SMS
- It allows customers to authentication and give consent over dumb channels
- Authorization Code Flow
- For smart terminals
- Use this for web applications where ther eis a backend that can exchange the generated
authorisation code for a token
- Bakend can securle store your cleint id and client secret away from the web browser
- Authorization Code Flow with PKCE (Proof Key for Code Exchange)
- For browser/untrusted applications e.g. Single Page Application
- Use this when you are developing a single page application (SPA) for enhanced security
- Client Credentials
- Use this when calling API that does not require Customer consent
- Example includes APIs like:
Open new Account, Get Biller Categoryies, Get Billers etc
API Authentication
After obtaining your acess token via the OAuth 2 flow described earlier, use the obtained acess token to access the APIs. All API calls require:
- Authorization:
Bearer <access_token> - OAuth 2.0 access token
- signature:
<signature> - Request signature for security
- idempotency_key:
<idempotency_key> - Unique identifier for request deduplication
Authorization Endpoint
Request
Parameters
| Parameter | Type | Required | Description |
|---|
client_id | string | Yes | Your application’s client ID |
redirect_uri | string | Yes | Your registered redirect URI during dynamic client registration |
response_type | string | Yes | Must be code |
scope | string | Yes | Space-separated list of requested scopes |
state | string | No | Random string to prevent CSRF attacks |
code_challenge | string | Yes | PKCE code challenge |
code_challenge_method | string | Yes | Must be S256 |
Example Request
// Authorisation via PKCE challenge
const generatePKCE = () => {
const codeVerifier = crypto.randomUUID();
const codeChallenge = btoa(codeVerifier);
return { codeVerifier, codeChallenge };
};
// Step 1: Redirect user to authorization URL
const { codeVerifier, codeChallenge } = generatePKCE();
const authUrl = `${AUTH_BASE_URL}/oauth/authorize?` +
`client_id=${credentials.client_id}&` +
`redirect_uri=${encodeURIComponent(credentials.redirect_urls[0])}&` +
`response_type=code&` +
`scope=accounts payments&` +
`code_challenge=${codeChallenge}&` +
`code_challenge_method=S256`;
window.location.href = authUrl;
// Step 2: Exchange authorization code for access token
const exchangeCodeForToken = async (code) => {
const response = await fetch('${AUTH_BASE_URL}/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'authorization_code',
client_id: credentials.client_id,
client_secret: credentials.client_secret,
code: code,
redirect_uri: credentials.redirect_urls[0],
code_verifier: codeVerifier
})
});
const tokenData = await response.json();
return tokenData.access_token;
};
Token Endpoint
Request
Parameters
| Parameter | Type | Required | Description |
|---|
grant_type | string | Yes | Must be authorization_code |
client_id | string | Yes | Your application’s client ID |
client_secret | string | Yes | Your application’s client secret |
code | string | Yes | Authorization code from previous step |
redirect_uri | string | Yes | Must match the one used in authorization |
code_verifier | string | Yes | PKCE code verifier |
Example Request
// Exchange authorization code for access token
const exchangeCodeForToken = async (code) => {
const response = await fetch('${AUTH_BASE_URL}/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'authorization_code',
client_id: credentials.client_id,
client_secret: credentials.client_secret,
code: code,
redirect_uri: credentials.redirect_urls[0],
code_verifier: codeVerifier
})
});
const tokenData = await response.json();
return tokenData.access_token;
};
Response
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "refresh_token_here",
"scope": "accounts payments"
}
PKCE Implementation
PKCE (Proof Key for Code Exchange) adds an extra layer of security to the OAuth flow. Here’s how to implement it:
// Generate PKCE challenge and verifier
const generatePKCE = () => {
const codeVerifier = crypto.randomUUID();
const codeChallenge = btoa(codeVerifier);
return { codeVerifier, codeChallenge };
};
// Store code verifier securely (session storage, etc.)
const { codeVerifier, codeChallenge } = generatePKCE();
sessionStorage.setItem('code_verifier', codeVerifier);
// Use code challenge in authorization request
const authUrl = `https://sandbox-api.sparkle.ng/oauth/authorize?` +
`client_id=${clientId}&` +
`redirect_uri=${encodeURIComponent(redirectUri)}&` +
`response_type=code&` +
`scope=accounts payments&` +
`code_challenge=${codeChallenge}&` +
`code_challenge_method=S256`;
// Retrieve code verifier for token exchange
const storedCodeVerifier = sessionStorage.getItem('code_verifier');
Available Scopes
| Scope | Description | Access Level |
|---|
accounts | Read account information and balances | Account data access |
payments | Initiate payments and transfers | Payment initiation |
transactions | Read transaction history | Transaction data access |
standing_orders | Manage standing orders | Standing order management |
Using Access Tokens
Once you have an access token, include it in the Authorization header for all API requests:
curl -X GET https://sandbox-api.sparkle.ng/accounts \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "X-Participant-ID: YOUR_PARTICIPANT_ID" \
-H "X-Connection-ID: YOUR_CONNECTION_ID"
Token Refresh
Access tokens expire after 1 hour. Use the refresh token to get a new access token:
curl -X POST https://sandbox-api.sparkle.ng/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token&client_id=your_client_id&client_secret=your_client_secret&refresh_token=your_refresh_token"
Security Best Practices
1. Secure Storage
- Store client secrets server-side only
- Use environment variables for sensitive data
- Never expose credentials in client-side code
2. Token Management
- Store access tokens securely
- Implement automatic token refresh
- Clear tokens on logout
3. PKCE Implementation
- Always use PKCE for web applications
- Generate cryptographically secure random values
- Store code verifier securely during the flow
4. Redirect URI Validation
- Use exact redirect URI matching
- Avoid wildcard redirect URIs
- Validate redirect URIs server-side
5. State Parameter
- Always include a random state parameter
- Validate state parameter on callback
- Use cryptographically secure random values
Error Handling
Common OAuth errors and how to handle them:
| Error | Description | Solution |
|---|
invalid_client | Invalid client credentials | Check your client_id and client_secret |
invalid_grant | Invalid authorization code | Ensure code is used only once and not expired |
invalid_redirect_uri | Redirect URI mismatch | Verify redirect URI matches registration |
invalid_scope | Invalid scope requested | Check scope format and permissions |
access_denied | User denied consent | Handle gracefully and retry |
Always implement proper error handling and provide clear feedback to users when authentication fails.
Testing Authentication
Use our sandbox environment to test your authentication implementation:
- Register your application in sandbox
- Test the complete OAuth flow
- Verify token exchange and API calls
- Test error scenarios and edge cases