For external applications using OAuth 2.0
Third-Party Integration is for external applications and clients that are not part of the RegardingWork ecosystem. These integrations use the OAuth 2.0 Authorization Code Flow with PKCE for secure, standardized authentication.
Your app redirects user to Hub authorization endpoint.
User reviews permissions and grants access to your app.
Hub redirects back with authorization code.
Your server exchanges code for access & refresh tokens.
Use access token to make authorized API calls.
sequenceDiagram
participant User
participant ThirdParty
participant Hub
User->>ThirdParty: Use App
ThirdParty->>Hub: Authorization Request
Hub->>User: Login & Consent Form
User->>Hub: Grant Permission
Hub->>ThirdParty: Authorization Code
ThirdParty->>Hub: Exchange Code for Tokens
Hub->>ThirdParty: Access & Refresh Tokens
ThirdParty->>Hub: API Calls with Access Token
Hub->>ThirdParty: User Data
Scope | Description | Access Level |
---|---|---|
profile:read |
Read user profile information | Username, email, display name |
teams:read |
Read user's team memberships | Team names and user roles |
teams:write |
Manage team memberships | Add/remove team members |
Create code verifier and challenge for security:
// Generate PKCE parameters
function generateCodeVerifier() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return btoa(String.fromCharCode.apply(null, array))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
function generateCodeChallenge(verifier) {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
return crypto.subtle.digest('SHA-256', data).then(digest => {
return btoa(String.fromCharCode.apply(null, new Uint8Array(digest)))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
});
}
// Store code verifier for later use
const codeVerifier = generateCodeVerifier();
sessionStorage.setItem('pkce_verifier', codeVerifier);
const codeChallenge = await generateCodeChallenge(codeVerifier);
Redirect user to Hub's authorization endpoint:
// OAuth authorization request
function initiateOAuth() {
const params = new URLSearchParams({
client_id: 'your-registered-client-id',
response_type: 'code',
redirect_uri: 'https://your-app.com/oauth/callback',
scope: 'profile:read teams:read',
state: generateRandomState(), // CSRF protection
code_challenge: codeChallenge,
code_challenge_method: 'S256'
});
const authUrl = `https://hub.regardingwork.com/api/oauth/authorize?${params}`;
window.location.href = authUrl;
}
function generateRandomState() {
const array = new Uint8Array(16);
crypto.getRandomValues(array);
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
}
Process the authorization code from Hub:
// OAuth callback handler: /oauth/callback
app.get('/oauth/callback', async (req, res) => {
const { code, state, error } = req.query;
if (error) {
return res.redirect(`/login?error=${error}`);
}
// Verify state parameter (CSRF protection)
const expectedState = req.session.oauth_state;
if (state !== expectedState) {
return res.redirect('/login?error=invalid_state');
}
try {
// Exchange authorization code for tokens
const tokens = await exchangeCodeForTokens(code);
// Store tokens securely
req.session.access_token = tokens.access_token;
req.session.refresh_token = tokens.refresh_token;
res.redirect('/dashboard');
} catch (error) {
res.redirect('/login?error=token_exchange_failed');
}
});
Server-side token exchange with PKCE:
// Token exchange function
async function exchangeCodeForTokens(authorizationCode) {
const codeVerifier = sessionStorage.getItem('pkce_verifier');
const response = await fetch('https://hub.regardingwork.com/api/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
client_id: 'your-registered-client-id',
code: authorizationCode,
redirect_uri: 'https://your-app.com/oauth/callback',
code_verifier: codeVerifier
})
});
if (!response.ok) {
throw new Error('Token exchange failed');
}
return await response.json();
// Returns: { access_token, refresh_token, token_type, expires_in }
}
Use access tokens for API requests:
// Authorized API call example
async function getUserProfile(accessToken) {
const response = await fetch('https://hub.regardingwork.com/api/auth/me', {
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
});
if (!response.ok) {
if (response.status === 401) {
// Token expired, refresh it
return await refreshTokenAndRetry();
}
throw new Error('API call failed');
}
return await response.json();
}
// Token refresh example
async function refreshAccessToken(refreshToken) {
const response = await fetch('https://hub.regardingwork.com/api/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'refresh_token',
client_id: 'your-registered-client-id',
refresh_token: refreshToken
})
});
return await response.json();
}
Endpoint | Method | Purpose | Parameters |
---|---|---|---|
/api/oauth/authorize |
GET | OAuth authorization | client_id , redirect_uri , scope , state , code_challenge |
/api/oauth/token |
POST | Token exchange | grant_type , code , code_verifier |
/api/auth/me |
GET | Get user profile | Authorization: Bearer {token} |
/api/user/teams |
GET | Get user teams | Authorization: Bearer {token} |