Genesys Cloud - Developer Community!

 View Only

Sign Up

Expand all | Collapse all

PKCE redirect: state not restored from search string in getQueryParameters (purecloud-premium-app)

  • 1.  PKCE redirect: state not restored from search string in getQueryParameters (purecloud-premium-app)

    Posted 3 hours ago
    Edited by Sylvain RIBEYRON 3 hours ago

    Hi all,

    I've been adapting the purecloud-premium-app sample wizard for our ISV submission and ran into a behavior I'd like to confirm.

    When using PKCE Grant (which the docs now recommend over the deprecated Implicit Grant), Genesys redirects back to the wizard with ?code=...&state=... in the search string. After this redirect, in docs/wizard/scripts/utils.js, the getQueryParameters() function falls into:


    } else if (window.location.search) {
        let urlParams = new URLSearchParams(window.location.search);
        let language = urlParams.get(config.languageQueryParam);
        let environment = urlParams.get(config.genesysCloudEnvironmentQueryParam);
        let hostOrigin = urlParams.get(config.genesysCloudHostOriginQueryParam);
        let targetEnv = urlParams.get(config.genesysCloudTargetEnvQueryParam);
        // ...
    }

    This branch only knows how to parse the original langTag/hostOrigin/targetEnv query params from a first invocation and it does not decode the state param, that is sent by the OAuth authorization service.
    As a result appParams is empty, and setup() falls back to gcEnvironment = config.defaultGcEnvironment. The PKCE token exchange is then attempted on the wrong region's login.<env> endpoint.

    When wizard used to authenticate using implicit grant, the URL was built with a hash (#). Since code has beed adapted to PKCE, the URL is no longer built with a hash, but with a question mark (?). The getQueryParameters method has not beed adapted to this new kind of URL, and as a result it does not decode the state.

    Here is my workaround:

    } else if (window.location.search) {
        let urlParams = new URLSearchParams(window.location.search);


        if (urlParams.has('code')) {
            // PKCE Grant success: state contains the original appParams
            let stateParam = urlParams.get('state');
            if (stateParam) {
                ret = JSON.parse(decodeURIComponent(stateParam));
            } else {
                ret.errorCode = "400";
                ret.errorDescription = "Missing state";
                ret.error = true;
            }
        } else if (urlParams.has('error')) {
            // PKCE Grant error
            let stateParam = urlParams.get('state');
            if (stateParam) {
                ret = JSON.parse(decodeURIComponent(stateParam));
            }
            ret.error = true;
            ret.errorCode = urlParams.get('error');
            ret.errorDescription = urlParams.get('error_description');
        } else {
            // First invocation: params provided directly in URL
            let language = urlParams.get(config.languageQueryParam);
            let environment = urlParams.get(config.genesysCloudEnvironmentQueryParam);
            let hostOrigin = urlParams.get(config.genesysCloudHostOriginQueryParam);
            let targetEnv = urlParams.get(config.genesysCloudTargetEnvQueryParam);
            let uninstall = urlParams.get('uninstall');


            if (language) ret.language = language;
            if (environment) ret.environment = environment;
            if (hostOrigin) ret.hostOrigin = hostOrigin;
            if (targetEnv) ret.targetEnv = targetEnv;
            if (uninstall) ret.uninstall = uninstall;
        }
    }

    This is essentialy the same process as with hash mark.

    Has anyone else encountered this?

    Note that in our case, the issue would have gone unnoticed if our defaultGcEnvironment had matched our test org's region.

    Regards,

    Sylvain.


    #Integrations

    ------------------------------
    Sylvain RIBEYRON
    ------------------------------



  • 2.  RE: PKCE redirect: state not restored from search string in getQueryParameters (purecloud-premium-app)

    Posted 22 minutes ago

    Hi Sylvain,

    Yes, your analysis makes sense, and your workaround looks valid.

    What's happening is that the original getQueryParameters() logic in the sample wizard was designed around the older Implicit Grant flow, where the OAuth response parameters were returned in the URL hash (#).

    With PKCE Grant, the authorization server now returns:

    • code
    • state

    in the query string (?) instead.

    So after the redirect, the existing logic incorrectly assumes this is still a first invocation and only tries to parse:

    • langTag
    • hostOrigin
    • targetEnv

    As you identified, it never restores the original app context from state, which causes the fallback to defaultGcEnvironment.

    Your workaround correctly:

    • Detects PKCE success/error responses
    • Extracts and decodes the state
    • Restores the original app parameters before token exchange

    That aligns with standard OAuth PKCE handling patterns.

    Important point

    This issue can easily stay hidden when:

    • defaultGcEnvironment
    • and the org region

    happen to match, exactly as you mentioned.

    Recommendation

    Your approach is the correct direction for PKCE compatibility and should probably be integrated into the sample wizard officially, since the current implementation still appears partially oriented to the legacy implicit flow behavior.

    The logic separation you added between:

    • first invocation
    • PKCE success
    • PKCE error

    is also much cleaner and safer operationally.



    ------------------------------
    Gabriel Garcia
    NA
    ------------------------------