Genesys Cloud - Developer Community!

 View Only

Sign Up

Expand all | Collapse all

Error : You are not authorized to perform the requested action

  • 1.  Error : You are not authorized to perform the requested action

    Posted 05-22-2025 17:05
    Edited by Chandan Vishwakarma Ramachandra 05-22-2025 17:06

    We have a stand alone app where we make genesys api call using platform SDK javascript.  This is a long running application, over 8 hours after each login. After certain duration, we are seeing "You are not authorized to perform the requested action"  when making api calls. These errors are random and not specific to a user.  Best guess is token expires and then we may be seeing this. Is there a way to refresh platform SDK session periodically like 15 mins once? 


    #PlatformSDK

    ------------------------------
    Chandan Vishwakarma Ramachandra
    na
    ------------------------------



  • 2.  RE: Error : You are not authorized to perform the requested action

    Posted 05-28-2025 09:03

    Hi Chandan,

    Quick question are you using a OAuth client credential grant or some other type of OAuth grant.

    Thanks,
        John



    ------------------------------
    John Carnell
    Director, Developer Engagement
    ------------------------------



  • 3.  RE: Error : You are not authorized to perform the requested action

    Posted 05-28-2025 11:39

    Hi John,  we use Javascript platform SDK from genesys to make api calls. The sdk client is initialized using implicitGrant method : client.loginImplicitGrant()



    ------------------------------
    Chandan Vishwakarma Ramachandra
    na
    ------------------------------



  • 4.  RE: Error : You are not authorized to perform the requested action

    Posted 05-28-2025 13:50

    Hi Chandan,

    It could be that your token is expiring.  Unfortunately, the Genesys Cloud implicit grant flow does not support automatic token refreshes.  The code authorization grant flow does.  The Implicit Grant flow is designed for client-side applications (like your web-based app) and returns an access token directly in the URL fragment, but it does not provide a refresh token. Access tokens typically expire after 3600 seconds (1 hour) in Genesys Cloud.

    To maintain a seamless user experience without requiring re-login, you can implement a strategy to proactively obtain a new access token before the current one expires. Below, I'll outline how to achieve this using the Genesys Cloud JavaScript SDK and best practices for handling token expiration.

    Why Implicit Grant Doesn't Support Refresh Tokens
    The OAuth Implicit Grant flow is intended for browser-based applications where the client (your web app) cannot securely store a client secret or refresh token. As a result, the flow prioritizes simplicity but sacrifices the ability to use refresh tokens. To "refresh" the token, you must re-initiate the OAuth flow, typically silently, to obtain a new access token.

    Solution: Silent Authentication with Genesys Cloud SDK
    You can use the Genesys Cloud JavaScript SDK (purecloud-platform-client-v2) to perform silent authentication, which allows your application to obtain a new access token without user interaction, provided the user's session with the Genesys Cloud identity provider (IdP) is still active. This is typically done by redirecting to the OAuth endpoint in a hidden iframe or using a session-based approach.
    Here's how you can implement automatic token refresh:
    1. Understand Token Expiration
    • Genesys Cloud access tokens have a default expiration of 3600 seconds (1 hour).
    • You need to track the token's expiration time and initiate a refresh before it expires (e.g., 5 minutes before expiration).
    2. Use the Genesys Cloud JavaScript SDK
    The SDK simplifies OAuth interactions. Ensure you have included the SDK in your project:
    html
    <script src="https://sdk-cdn.mypurecloud.com/javascript/2.0.0/purecloud-platform-client-v2.min.js"></script>
    3. Initialize the SDK and Store Token Metadata
    When the user authenticates via the Implicit Grant flow, the SDK will handle the token extraction from the URL fragment. Store the token and its expiration time for tracking.
    javascript
    const platformClient = require('purecloud-platform-client-v2/client');
    const clientId = 'YOUR_CLIENT_ID'; // Your OAuth client ID
    const redirectUri = 'https://your-app.com/auth-callback'; // Your redirect URI
    
    // Initialize the client
    const client = platformClient.ApiClient.instance;
    client.setEnvironment('mypurecloud.com'); // Set your Genesys Cloud region
    
    // Authenticate (this happens on page load after redirect)
    client.loginImplicitGrant(clientId, redirectUri)
      .then(() => {
        // Token is now available in client.authData
        const accessToken = client.authData.accessToken;
        const tokenExpiry = Date.now() + (client.authData.expiresIn * 1000); // expiresIn is in seconds
        console.log('Access Token:', accessToken);
        console.log('Token Expiry:', new Date(tokenExpiry));
    
        // Store token and expiry for refresh logic
        localStorage.setItem('accessToken', accessToken);
        localStorage.setItem('tokenExpiry', tokenExpiry);
    
        // Start the refresh timer
        scheduleTokenRefresh(tokenExpiry);
      })
      .catch((err) => {
        console.error('Authentication failed:', err);
      });
    4. Schedule Token Refresh
    To avoid token expiration, schedule a refresh before the token expires. For example, refresh 5 minutes (300,000 ms) before expiration.
    javascript
    function scheduleTokenRefresh(expiryTimestamp) {
      const bufferTime = 300000; // 5 minutes in milliseconds
      const timeUntilExpiry = expiryTimestamp - Date.now() - bufferTime;
    
      if (timeUntilExpiry > 0) {
        setTimeout(() => {
          refreshAccessToken();
        }, timeUntilExpiry);
      } else {
        // Token is already expired or about to expire; refresh immediately
        refreshAccessToken();
      }
    }
    5. Implement Silent Authentication for Refresh
    To refresh the token silently, you can use the SDK's loginImplicitGrant method with a silent authentication flow. This typically involves redirecting to the Genesys Cloud OAuth endpoint in a hidden iframe.
    javascript
    function refreshAccessToken() {
      // Re-initiate Implicit Grant flow with prompt=none for silent authentication
      const authUrl = `https://login.mypurecloud.com/oauth/authorize?` +
        `client_id=${encodeURIComponent(clientId)}` +
        `&response_type=token` +
        `&redirect_uri=${encodeURIComponent(redirectUri)}` +
        `&prompt=none`; // Ensures no user interaction (if session is active)
    
      // Create a hidden iframe for silent authentication
      const iframe = document.createElement('iframe');
      iframe.style.display = 'none';
      iframe.src = authUrl;
      document.body.appendChild(iframe);
    
      // Listen for the redirect in the iframe
      window.addEventListener('message', (event) => {
        // Ensure the message is from the expected origin
        if (event.origin === 'https://your-app.com') {
          const url = event.data;
          if (url && url.includes('access_token')) {
            // Extract token from URL fragment
            const tokenMatch = url.match(/access_token=([^&]+)/);
            const expiresInMatch = url.match(/expires_in=([^&]+)/);
            if (tokenMatch && expiresInMatch) {
              const newAccessToken = tokenMatch[1];
              const newExpiresIn = parseInt(expiresInMatch[1]) * 1000; // Convert to milliseconds
              const newTokenExpiry = Date.now() + newExpiresIn;
    
              // Update SDK with new token
              client.setAccessToken(newAccessToken);
              // Update stored token and expiry
              localStorage.setItem('accessToken', newAccessToken);
              localStorage.setItem('tokenExpiry', newTokenExpiry);
    
              console.log('Token refreshed successfully');
              // Schedule the next refresh
              scheduleTokenRefresh(newTokenExpiry);
            }
          }
          // Clean up iframe
          document.body.removeChild(iframe);
        }
      });
    }
    6. Configure the Auth Callback to Post Message
    In your redirect URI page (auth-callback), ensure the URL fragment (containing the new token) is sent to the parent window (main app) for processing.
    javascript

    // In your auth-callback.html or equivalent
    window.onload = () => {
      if (window.location.hash) {
        // Post the URL with the new token to the parent window
        window.parent.postMessage(window.location.href, 'https://your-app.com');
      }
    };
    7. Handle Errors and Fallback
    If silent authentication fails (e.g., the user's session with the IdP has expired), the prompt=none request will return an error. You should handle this by prompting the user to re-authenticate.

    javascript
    function refreshAccessToken() {
      const authUrl = `https://login.mypurecloud.com/oauth/authorize?` +
        `client_id=${encodeURIComponent(clientId)}` +
        `&response_type=token` +
        `&redirect_uri=${encodeURIComponent(redirectUri)}` +
        `&prompt=none`;
    
      const iframe = document.createElement('iframe');
      iframe.style.display = 'none';
      iframe.src = authUrl;
      document.body.appendChild(iframe);
    
      window.addEventListener('message', (event) => {
        if (event.origin === 'https://your-app.com') {
          const url = event.data;
          if (url.includes('error')) {
            console.error('Silent authentication failed:', url);
            // Fallback: Redirect user to full login
            client.loginImplicitGrant(clientId, redirectUri)
              .then(() => {
                console.log('User re-authenticated');
              })
              .catch((err) => {
                console.error('Re-authentication failed:', err);
              });
          } else if (url.includes('access_token')) {
            // Handle successful token refresh as above
            // ...
          }
          document.body.removeChild(iframe);
        }
      });
    }

    Key Considerations
    1. Session Dependency: Silent authentication (prompt=none) relies on an active session with the Genesys Cloud IdP (e.g., SSO provider or Genesys Cloud login). If the session expires, silent authentication will fail, and you'll need to prompt the user to log in again.
    2. Security: Ensure your application is served over HTTPS to protect tokens in transit. Avoid storing tokens in insecure locations (e.g., localStorage) if possible; consider sessionStorage or in-memory storage for better security.
    3. CORS and Redirect URI: Ensure the redirectUri is correctly configured in your Genesys Cloud OAuth client settings and matches the URL of your app.
    4. Token Expiry Buffer: Refresh tokens well before expiration (e.g., 5 minutes) to account for network delays or processing time.
    5. Error Handling: Always handle cases where silent authentication fails (e.g., user session expired) and provide a fallback to explicit login.

    Alternative: Switch to Authorization Code Grant
    If your application can be modified to run server-side logic (e.g., with a backend), consider switching to the Authorization Code Grant flow. This flow provides a refresh token, which is more secure and simplifies token refresh. The backend would handle token storage and refresh, reducing the risk of token exposure in the browser.
    To use Authorization Code Grant:
    • Configure a server-side endpoint to handle the OAuth callback.
    • Use the refresh token to obtain new access tokens via the /oauth/token endpoint.
    • Update your client-side app to fetch tokens from your backend securely.

    Example Full Flow
    Here's a simplified version of the complete JavaScript logic:
    javascript
    const platformClient = require('purecloud-platform-client-v2/client');
    const clientId = 'YOUR_CLIENT_ID';
    const redirectUri = 'https://your-app.com/auth-callback';
    const client = platformClient.ApiClient.instance;
    client.setEnvironment('mypurecloud.com');
    
    function scheduleTokenRefresh(expiryTimestamp) {
      const bufferTime = 300000; // 5 minutes
      const timeUntilExpiry = expiryTimestamp - Date.now() - bufferTime;
      if (timeUntilExpiry > 0) {
        setTimeout(refreshAccessToken, timeUntilExpiry);
      } else {
        refreshAccessToken();
      }
    }
    
    function refreshAccessToken() {
      const authUrl = `https://login.mypurecloud.com/oauth/authorize?` +
        `client_id=${encodeURIComponent(clientId)}` +
        `&response_type=token` +
        `&redirect_uri=${encodeURIComponent(redirectUri)}` +
        `&prompt=none`;
    
      const iframe = document.createElement('iframe');
      iframe.style.display = 'none';
      iframe.src = authUrl;
      document.body.appendChild(iframe);
    
      window.addEventListener('message', (event) => {
        if (event.origin === 'https://your-app.com') {
          const url = event.data;
          if (url.includes('access_token')) {
            const tokenMatch = url.match(/access_token=([^&]+)/);
            const expiresInMatch = url.match(/expires_in=([^&]+)/);
            if (tokenMatch && expiresInMatch) {
              const newAccessToken = tokenMatch[1];
              const newExpiresIn = parseInt(expiresInMatch[1]) * 1000;
              const newTokenExpiry = Date.now() + newExpiresIn;
    
              client.setAccessToken(newAccessToken);
              localStorage.setItem('accessToken', newAccessToken);
              localStorage.setItem('tokenExpiry', newTokenExpiry);
              scheduleTokenRefresh(newTokenExpiry);
            }
          } else if (url.includes('error')) {
            console.error('Silent auth failed, redirecting to login');
            client.loginImplicitGrant(clientId, redirectUri);
          }
          document.body.removeChild(iframe);
        }
      }, { once: true });
    }
    
    // Initial authentication
    client.loginImplicitGrant(clientId, redirectUri)
      .then(() => {
        const accessToken = client.authData.accessToken;
        const tokenExpiry = Date.now() + (client.authData.expiresIn * 1000);
        localStorage.setItem('accessToken', accessToken);
        localStorage.setItem('tokenExpiry', tokenExpiry);
        scheduleTokenRefresh(tokenExpiry);
      })
      .catch((err) => console.error('Initial auth failed:', err));

    Testing and Debugging
    • Test Silent Authentication: Ensure prompt=none works by testing with an active Genesys Cloud session. Open the browser's developer tools to monitor network requests and iframe behavior.
    • Handle Edge Cases: Test scenarios where the user's session has expired or the OAuth client is misconfigured.
    • Logging: Add logging to track token refresh success/failure for debugging.

    Additional Resources


    ------------------------------
    John Carnell
    Director, Developer Engagement
    ------------------------------



  • 5.  RE: Error : You are not authorized to perform the requested action

    Posted 05-29-2025 01:58

    Wow!, this is quiet detailed and innovative solution, I will give this a try.  Thank you for taking time to write such a detailed response John.



    ------------------------------
    Chandan Vishwakarma Ramachandra
    na
    ------------------------------



  • 6.  RE: Error : You are not authorized to perform the requested action

    Posted 05-29-2025 17:09

    Hi John.

    This is very interesting. Thanks for taking the time to explain the mechanics of how to achieve this.

    I wonder whether it would have been simpler to explain how to transition an Implicit Grant app to use PKCE Grant? Wouldn't that also solve the problem (because a refresh token would be issued) with a lot less code, and the app would also be more secure?

    Thanks,

    Nick.



    ------------------------------
    Nick Tait
    Genesys Consultant
    ------------------------------



  • 7.  RE: Error : You are not authorized to perform the requested action

    Posted 06-02-2025 06:51

    Hi Nick,
     yes you are correct. pkce will be more secured flow


    Thanks,
    Thirumurugan



    ------------------------------
    Thirumurugan Balamurugan
    Senior PS Consultant
    ------------------------------



  • 8.  RE: Error : You are not authorized to perform the requested action

    Posted 06-06-2025 14:22

    Hi Nick,

    Agree that PCKE is a better solution.  I answered it based on the current state of what was asked not what is the better solution :).  PCKE is a better solution.

    Thanks,

       John



    ------------------------------
    John Carnell
    Director, Developer Engagement
    ------------------------------



  • 9.  RE: Error : You are not authorized to perform the requested action

    Posted 06-08-2025 17:44

    Hi John.

    I saw a comment in https://community.genesys.com/discussion/pkce-grant-failing-with-loginpkcegrant-generic-error-at-apiclientjs-line-814#bmf343abf0-5686-4654-8e24-019735b30cf5 saying that PKCE doesn't generate a refresh token, which surprised me. If that is true then PKCE isn't the magic bullet to solve this particular issue after all? In other words you'd still need to resort to using a mechanism like what you described above with hidden iframes, etc, with PKCE?

    Thanks,

    Nick.



    ------------------------------
    Nick Tait
    Genesys Consultant
    ------------------------------



  • 10.  RE: Error : You are not authorized to perform the requested action

    Posted 06-02-2025 06:53

    Hi @Chandan Vishwakarma Ramachandra,
               if it just around 8 hours of working an application, is it possible to increase the expiration time as like below 


    Thanks,
    Thirumurugan



    ------------------------------
    Thirumurugan Balamurugan
    Senior PS Consultant
    ------------------------------