Access Denied Error

https://bessapay.net/errors/access-denied

Overview

An Access Denied error occurs when a user has proper authentication but is denied access to a resource due to business rules or policy restrictions, rather than just missing permissions. This error is returned with HTTP status code 403 (Forbidden).

Unlike the more general Forbidden error, Access Denied errors are typically related to business policies, account status, or specific circumstances that prevent an otherwise authenticated and authorized user from performing an action.

Details

  • Error Code: ACCESS_DENIED
  • HTTP Status: 403
  • Error Type: Client Error

Common scenarios

  • • Account is suspended or under review
  • • User has exceeded transaction limits
  • • Geographical restrictions
  • • Business hours restrictions
  • • Compliance or regulatory restrictions

Example

Access Denied Error Response
1{
2  "type": "https://bessapay.net/errors/access-denied",
3  "title": "Access Denied",
4  "status": 403,
5  "detail": "Your account has been suspended due to suspicious activity",
6  "instance": "/api/v1/payments/transfer",
7  "errorCode": "ACCESS_DENIED",
8  "timestamp": "2023-06-15T21:05:18Z",
9  "reason": "ACCOUNT_SUSPENDED",
10  "resolutionSteps": [
11    "Contact customer support to verify your identity",
12    "Complete the account verification process"
13  ]
14}

How to Handle

Check account status

Verify that the user's account is in good standing and not suspended or restricted. Access denied errors often relate to account status issues.

Look for resolution steps

The error response may include a resolutionSteps array with actions that can be taken to resolve the issue. Display these steps to the user.

Check transaction limits

If the error occurs during a transaction, check if the user has exceeded their transaction limits (daily, monthly, or per-transaction limits).

Contact support

Many access denied errors require intervention from support staff. Provide easy access to contact support when these errors occur.

Handling Access Denied with Support Contact
1// Example of handling Access Denied with support contact
2function handleAccessDenied(errorData) {
3  // Create a case for the support team
4  const caseId = createSupportCase({
5    errorType: errorData.type,
6    errorDetail: errorData.detail,
7    errorCode: errorData.errorCode,
8    timestamp: errorData.timestamp,
9    userId: currentUser.id,
10    instance: errorData.instance
11  });
12  
13  // Show error dialog with support case info
14  showErrorDialog({
15    title: "Access Restricted",
16    message: errorData.detail,
17    supportCaseId: caseId,
18    contactOptions: [
19      {
20        type: "email",
21        label: "Email Support",
22        action: () => openEmailSupport(caseId)
23      },
24      {
25        type: "chat",
26        label: "Live Chat",
27        action: () => openLiveChat(caseId)
28      },
29      {
30        type: "phone",
31        label: "Call Support",
32        action: () => showPhoneSupport(caseId)
33      }
34    ]
35  });
36  
37  // Log the event for analytics
38  logAccessDeniedEvent({
39    reason: errorData.reason,
40    timestamp: errorData.timestamp,
41    userId: currentUser.id,
42    caseId: caseId
43  });
44}

Look for alternative options

Sometimes there are alternative ways to achieve what the user wants that don't trigger the restrictions. For example, splitting a large transaction into smaller ones if there's a per-transaction limit.

Code Example: Comprehensive Access Denied Handling

Complete Access Denied Handling
1// Complete example of handling various access denied scenarios
2class AccessManager {
3  constructor(apiClient) {
4    this.apiClient = apiClient;
5  }
6  
7  // Handle an access denied error
8  async handleAccessDenied(errorData, actionContext) {
9    // Determine the reason for access denial
10    const reason = errorData.reason || 'UNKNOWN';
11    
12    switch (reason) {
13      case 'ACCOUNT_SUSPENDED':
14        return this.handleAccountSuspended(errorData, actionContext);
15        
16      case 'LIMIT_EXCEEDED':
17        return this.handleLimitExceeded(errorData, actionContext);
18        
19      case 'GEO_RESTRICTED':
20        return this.handleGeoRestriction(errorData, actionContext);
21        
22      case 'VERIFICATION_REQUIRED':
23        return this.handleVerificationRequired(errorData, actionContext);
24        
25      default:
26        return this.handleGenericAccessDenied(errorData, actionContext);
27    }
28  }
29  
30  // Handle suspended account
31  async handleAccountSuspended(errorData, actionContext) {
32    // Check if the account can be automatically reactivated
33    const accountStatus = await this.apiClient.getAccountStatus();
34    
35    if (accountStatus.canReactivate) {
36      return {
37        handled: true,
38        resolution: 'action',
39        message: 'Your account has been temporarily suspended.',
40        actions: [
41          {
42            label: 'Reactivate Account',
43            action: () => this.apiClient.reactivateAccount()
44          },
45          {
46            label: 'Contact Support',
47            action: () => this.openSupportChannel()
48          }
49        ]
50      };
51    }
52    
53    // Account needs manual intervention
54    return {
55      handled: true,
56      resolution: 'support',
57      message: 'Your account has been suspended and requires verification.',
58      supportCaseId: this.createSupportCase(errorData),
59      actions: [
60        {
61          label: 'Contact Support',
62          action: () => this.openSupportChannel()
63        }
64      ]
65    };
66  }
67  
68  // Handle transaction limits
69  async handleLimitExceeded(errorData, actionContext) {
70    // Get the current limits and usage
71    const limits = await this.apiClient.getTransactionLimits();
72    
73    // Check if this is a transaction amount limit
74    if (actionContext.type === 'transaction' && actionContext.amount > limits.perTransaction) {
75      // Offer to split the transaction
76      const recommendedSplit = Math.ceil(actionContext.amount / limits.perTransaction);
77      
78      return {
79        handled: true,
80        resolution: 'adjust',
81        message: `This transaction exceeds your limit of ${formatCurrency(limits.perTransaction)}`,
82        limitInfo: limits,
83        recommendations: [
84          {
85            label: `Split into ${recommendedSplit} transactions`,
86            action: () => this.splitTransaction(actionContext, recommendedSplit)
87          },
88          {
89            label: 'Reduce Amount',
90            action: () => this.adjustTransactionAmount(actionContext, limits.perTransaction)
91          },
92          {
93            label: 'Request Limit Increase',
94            action: () => this.requestLimitIncrease(actionContext.amount)
95          }
96        ]
97      };
98    }
99    
100    // Check if this is a daily limit
101    if (limits.dailyRemaining <= 0) {
102      const resetTime = new Date(limits.dailyResetTimestamp);
103      
104      return {
105        handled: true,
106        resolution: 'wait',
107        message: 'You have reached your daily transaction limit',
108        limitInfo: limits,
109        resetInfo: {
110          time: resetTime,
111          formattedTime: formatDateTime(resetTime)
112        },
113        actions: [
114          {
115            label: 'Request Limit Increase',
116            action: () => this.requestLimitIncrease(actionContext.amount)
117          },
118          {
119            label: 'Try Tomorrow',
120            action: () => this.scheduleTransaction(actionContext, resetTime)
121          }
122        ]
123      };
124    }
125    
126    // Generic limit handling
127    return {
128      handled: true,
129      resolution: 'info',
130      message: 'You have exceeded your transaction limits',
131      limitInfo: limits,
132      actions: [
133        {
134          label: 'View Limits',
135          action: () => this.showLimitsPage()
136        },
137        {
138          label: 'Request Increase',
139          action: () => this.requestLimitIncrease()
140        }
141      ]
142    };
143  }
144  
145  // Handle geographical restrictions
146  handleGeoRestriction(errorData, actionContext) {
147    return {
148      handled: true,
149      resolution: 'info',
150      message: 'This action is not available in your current location',
151      restrictionInfo: {
152        currentRegion: errorData.currentRegion,
153        allowedRegions: errorData.allowedRegions
154      },
155      actions: [
156        {
157          label: 'Learn More',
158          action: () => this.showRegionalRestrictions()
159        }
160      ]
161    };
162  }
163  
164  // Handle verification requirements
165  async handleVerificationRequired(errorData, actionContext) {
166    // Get the verification status
167    const verificationStatus = await this.apiClient.getVerificationStatus();
168    
169    return {
170      handled: true,
171      resolution: 'verification',
172      message: 'Additional verification is required to perform this action',
173      verificationInfo: verificationStatus,
174      actions: [
175        {
176          label: 'Complete Verification',
177          action: () => this.startVerificationProcess(verificationStatus.requiredSteps)
178        },
179        {
180          label: 'Learn More',
181          action: () => this.showVerificationInfo()
182        }
183      ]
184    };
185  }
186  
187  // Generic handler for other access denied reasons
188  handleGenericAccessDenied(errorData, actionContext) {
189    return {
190      handled: true,
191      resolution: 'support',
192      message: errorData.detail || 'You do not have permission to perform this action',
193      supportCaseId: this.createSupportCase(errorData),
194      actions: [
195        {
196          label: 'Contact Support',
197          action: () => this.openSupportChannel()
198        },
199        {
200          label: 'Try Later',
201          action: () => this.dismissError()
202        }
203      ]
204    };
205  }
206  
207  // Create a support case
208  createSupportCase(errorData) {
209    // Implementation details...
210    return 'CASE-' + Math.random().toString(36).substr(2, 9).toUpperCase();
211  }
212  
213  // Open support channel
214  openSupportChannel() {
215    // Implementation details...
216  }
217}
218
219// Usage example
220async function performAction(action, context) {
221  try {
222    const result = await apiClient.request(action, context);
223    return result;
224  } catch (error) {
225    if (error.type === 'https://bessapay.net/errors/access-denied') {
226      const accessManager = new AccessManager(apiClient);
227      const handlerResult = await accessManager.handleAccessDenied(error, context);
228      
229      if (handlerResult.handled) {
230        // Display appropriate UI based on the handler result
231        displayAccessDeniedUI(handlerResult);
232        return null;
233      }
234    }
235    
236    // Handle other errors
237    console.error('Action failed:', error);
238    showErrorMessage('An error occurred while performing this action.');
239    return null;
240  }
241}