OAuth Scopes and Permissions
Scopes define the level of access that your application is requesting from WeTrials users. Users see the requested scopes during the authorization process and can choose to grant or deny access.
Understanding Scopes
What are Scopes?
Scopes are strings that represent specific permissions your application is requesting. They follow a pattern of action:resource to clearly indicate what access is being requested.
Scope Format
<action>:<resource>- action: The operation type (
read,write,delete) - resource: The resource being accessed (
profile,studies,participants)
Example Authorization Request
When requesting scopes, include them in the authorization URL:
const authUrl = new URL('https://auth.wetrials.com/v1/oauth/authorize');
authUrl.searchParams.append('scope', 'read:profile read:studies write:participants');Users will see a consent screen showing:
- Your application name
- Requested permissions in human-readable format
- Option to approve or deny access
Available Scopes
User Profile Scopes
| Scope | Description | Data Access |
|---|---|---|
read:profile | Read user profile information | Email, name, profile picture, created/updated dates |
write:profile | Update user profile | Modify name, profile picture, preferences |
read:account | Read account settings | Subscription status, billing information, settings |
write:account | Modify account settings | Update preferences, notification settings |
Study Management Scopes
| Scope | Description | Data Access |
|---|---|---|
read:studies | View studies | Study details, protocols, sites, enrollment status |
write:studies | Create and modify studies | Create new studies, update existing study details |
delete:studies | Delete studies | Remove studies and associated data |
read:study_participants | View study participants | Participant lists, enrollment data, demographics |
write:study_participants | Manage study participants | Enroll/unenroll participants, update status |
Participant Scopes
| Scope | Description | Data Access |
|---|---|---|
read:participants | View participant data | Basic participant information, consent status |
write:participants | Manage participants | Create participants, update information |
read:participant_data | Access participant research data | Survey responses, clinical data, outcomes |
write:participant_data | Submit participant data | Record visits, submit forms, upload documents |
Administrative Scopes
| Scope | Description | Data Access |
|---|---|---|
read:organizations | View organization details | Organization info, members, settings |
write:organizations | Manage organizations | Update settings, manage members |
read:audit_logs | View audit trails | System logs, user activities, compliance records |
read:reports | Access reports | Analytics, metrics, compliance reports |
write:reports | Generate reports | Create custom reports, export data |
Communication Scopes
| Scope | Description | Data Access |
|---|---|---|
read:messages | Read messages | Internal messages, notifications |
write:messages | Send messages | Send messages to participants, team members |
read:notifications | View notifications | System notifications, alerts |
write:notifications | Manage notifications | Mark as read, configure preferences |
Optional Consent Tags
In addition to scopes, WeTrials supports optional consent tags for granular data access control. These are presented separately during authorization.
Available Optional Tags
// Request optional consents during authorization
const optionalConsents = [
'analytics', // Share usage analytics
'marketing', // Receive marketing communications
'research', // Participate in platform research
'data_sharing', // Allow data sharing with partners
];Requesting Optional Consents
Optional consents are handled separately from scopes:
// In the consent endpoint
const consentData = {
client_id: 'your_client_id',
// ... other parameters
optional_consents: 'analytics,research', // Comma-separated
};Accessing Consented Tags
Consented optional tags are included in the access token and user info response:
{
"sub": "user_id",
"email": "user@example.com",
"consented_optional_tags": ["analytics", "research"]
}Scope Combinations
Common Scope Patterns
Read-Only Integration
For applications that only need to view data:
read:profile read:studies read:participantsStudy Management
For study coordinators and managers:
read:profile read:studies write:studies read:study_participants write:study_participantsFull Administrative Access
For administrative tools:
read:profile write:profile read:studies write:studies delete:studies read:organizations write:organizations read:audit_logsParticipant Portal
For participant-facing applications:
read:profile write:profile read:participant_data write:participant_data read:messagesIncremental Authorization
WeTrials supports incremental authorization, allowing you to request additional scopes as needed.
Initial Authorization
Start with minimal scopes:
// Initial login - basic profile access
oauth.authorize(['read:profile']);Requesting Additional Scopes
Later, request additional permissions:
// User wants to access studies
function requestStudyAccess() {
// Request additional scopes
oauth.authorize([
'read:profile', // Include existing scopes
'read:studies', // Add new scopes
'write:studies',
]);
}Checking Current Scopes
Decode the access token to check granted scopes:
function getGrantedScopes(accessToken) {
try {
const payload = JSON.parse(atob(accessToken.split('.')[1]));
return payload.scopes || [];
} catch (error) {
console.error('Failed to decode token:', error);
return [];
}
}
// Check if specific scope is granted
function hasScope(accessToken, scope) {
const scopes = getGrantedScopes(accessToken);
return scopes.includes(scope);
}
// Usage
if (hasScope(accessToken, 'write:studies')) {
// Show create study button
showCreateStudyUI();
}Scope Validation
Server-Side Validation
Always validate scopes server-side before performing operations:
// Node.js example
function requireScope(scope) {
return (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
try {
const decoded = jwt.verify(token, publicKey);
if (!decoded.scopes.includes(scope)) {
return res.status(403).json({
error: 'Insufficient permissions',
required_scope: scope,
});
}
req.user = decoded;
next();
} catch (error) {
return res.status(401).json({ error: 'Invalid token' });
}
};
}
// Protect endpoints with scope requirements
app.get('/api/studies', requireScope('read:studies'), (req, res) => {
// Handler code
});
app.post('/api/studies', requireScope('write:studies'), (req, res) => {
// Handler code
});Client-Side UI Control
Use scopes to control UI elements:
// React component example
function StudyManager({ accessToken }) {
const scopes = getGrantedScopes(accessToken);
const canRead = scopes.includes('read:studies');
const canWrite = scopes.includes('write:studies');
const canDelete = scopes.includes('delete:studies');
return (
<div>
{canRead && <StudyList />}
{canWrite && <CreateStudyButton />}
{canDelete && <DeleteStudyButton />}
{!canRead && <NoPermissionMessage />}
</div>
);
}Best Practices
1. Request Minimal Scopes
Only request the scopes you actually need:
// ✅ GOOD - Minimal scopes
oauth.authorize(['read:profile', 'read:studies']);
// ❌ BAD - Requesting everything
oauth.authorize([
'read:profile',
'write:profile',
'read:studies',
'write:studies',
'delete:studies',
'read:participants',
'write:participants',
// ... all scopes
]);2. Explain Scope Usage
Provide clear explanations before requesting scopes:
function ScopeExplanation({ requestedScopes }) {
return (
<div className="scope-explanation">
<h3>This application needs permission to:</h3>
<ul>
{requestedScopes.includes('read:profile') && <li>View your profile information</li>}
{requestedScopes.includes('write:studies') && <li>Create and modify studies on your behalf</li>}
{requestedScopes.includes('read:participants') && <li>Access participant information for your studies</li>}
</ul>
<p>You can revoke these permissions at any time in your account settings.</p>
</div>
);
}3. Handle Scope Denial
Gracefully handle when users deny certain scopes:
// After authorization callback
const grantedScopes = tokens.scope.split(' ');
const requiredScopes = ['read:profile', 'read:studies'];
const missingScopes = requiredScopes.filter((scope) => !grantedScopes.includes(scope));
if (missingScopes.length > 0) {
// Inform user about limited functionality
showWarning(`Limited functionality: Missing permissions for ${missingScopes.join(', ')}`);
// Disable features that require missing scopes
disableFeatures(missingScopes);
}4. Progressive Enhancement
Start with basic functionality and enhance with additional scopes:
class ProgressiveApp {
constructor() {
this.baseScopes = ['read:profile'];
this.enhancedScopes = [];
}
async initializeApp() {
// Start with basic profile access
await this.authorize(this.baseScopes);
this.showBasicUI();
}
async enableStudyFeatures() {
// Request additional scopes when needed
this.enhancedScopes = ['read:studies', 'write:studies'];
await this.authorize([...this.baseScopes, ...this.enhancedScopes]);
this.showStudyUI();
}
async enableAdminFeatures() {
// Request admin scopes only for admin users
this.enhancedScopes.push('read:organizations', 'write:organizations');
await this.authorize([...this.baseScopes, ...this.enhancedScopes]);
this.showAdminUI();
}
}Scope Changes and Versioning
Handling Scope Changes
WeTrials may occasionally update available scopes. Your application should handle:
- New Scopes: Additional permissions become available
- Deprecated Scopes: Old scopes are phased out
- Scope Splits: One scope becomes multiple specific scopes
Version Management
// Check API version and available scopes
async function getAvailableScopes() {
const response = await fetch('https://api.wetrials.com/v1/oauth/scopes');
const data = await response.json();
return {
version: data.version,
scopes: data.scopes,
deprecated: data.deprecated_scopes,
};
}
// Handle deprecated scopes
function mapDeprecatedScopes(requestedScopes) {
const scopeMapping = {
full_access: ['read:profile', 'read:studies', 'write:studies'],
participant_access: ['read:participants', 'write:participants'],
};
return requestedScopes.flatMap((scope) => scopeMapping[scope] || [scope]);
}Testing Scopes
Test Different Scope Combinations
# Test with minimal scopes
curl -G "https://auth.wetrials.com/v1/oauth/authorize" \
--data-urlencode "scope=read:profile"
# Test with multiple scopes
curl -G "https://auth.wetrials.com/v1/oauth/authorize" \
--data-urlencode "scope=read:profile read:studies write:studies"
# Test with invalid scope
curl -G "https://auth.wetrials.com/v1/oauth/authorize" \
--data-urlencode "scope=invalid:scope"Verify Token Scopes
# Decode JWT to check scopes
echo $ACCESS_TOKEN | cut -d. -f2 | base64 -d | jq '.scopes'Next Steps
- Managing OAuth Applications - Register and manage your apps
- Security Best Practices - Secure scope handling
- Error Handling - Handle scope-related errors
- Authorization Code Flow - Implement OAuth flow