Followed process for web Messenger, getting 401 error

Hello!

I'm trying to integrate web messaging for my company, but I've consistently encountered the 401 error after submitting an auth code obtained from an authorize endpoint. I decided to try and break it down in a sandbox environment, but I'm still getting a 401 error. Here's as much detail as possible.

I created a new application in Auth0 with the redirect URI http://localhost:3000/oauth2/redirect. The application is also a "Regular Web Application". "Authorization Code" is a valid grant type.

With Genesys, I believe I am following the documentation very closely as found here. I setup an oauth integration by finding the /.well-known/openid-configuration endpoint and I put in the client id and secret. I setup a web messenger configuration using the oauth integration I set, and I setup a web messenger deployment using the most recent configuration version. I am able to get the unauthorized web messenger working.

I setup a new minimal web application with an Express.js backend and a vanilla JS frontend. I setup the authentication ExpressJS backend using passport-openidconnect with the following configuration.

passport.use(
  new OpenIDConnectStrategy(
    {
      issuer: `https://${process.env["AUTH0_DOMAIN"]}/`,
      authorizationURL: `https://${process.env["AUTH0_DOMAIN"]}/authorize`,
      tokenURL: `https://${process.env["AUTH0_DOMAIN"]}/oauth/token`,
      userInfoURL: `https://${process.env["AUTH0_DOMAIN"]}/userinfo`,
      clientID: process.env["AUTH0_CLIENT_ID"],
      clientSecret: process.env["AUTH0_CLIENT_SECRET"],
      callbackURL: "/oauth2/redirect",
      scope: ["profile", "email", "openid", "offline_access"],
    },
    function verify(issuer, profile, cb) {
      return cb(null, profile);
    }
  )
);

Additionally, I have the following login and authorize endpoints using passport. Additionally I have the redirect URI.

router.get("/login", passport.authenticate("openidconnect"));
router.get("/authorize", passport.authorize("openidconnect"));

router.get(
  "/oauth2/redirect",
  passport.authenticate("openidconnect", {
    // successRedirect: "/",
    failureRedirect: "/login",
  }),
  (req, res, next) => {
    console.log("redirecting to /");
    console.log(req.user);
    console.log(req.account);

    const url = new URL(req.url, `http://${req.headers.host}`);
    const code = url.searchParams.get("code");
    console.log("URL:", url);
    console.log("Referrer", req.headers.referer);
    console.log("Code query param:", code);
    res.redirect(`/?code=${code}`);
  }
);

Next with the UI, I have a "login" and an "authorize" button. The login button is set to login the user, and the authorize button is set to authorize the user. The authorize endpoint is the one that gets the new auth code.

<!-- ... -->
<p class="help">Already have an account? <a href="/login">Sign in</a></p>

<!-- ... -->

<li>
     <form action="/authorize" method="get">
          <button class="authorize" id="authorizeButton" type="submit">Authorize</button>
     </form>
</li>

The login flow does login the user with Auth0, and the authorize flow does get a new auth code. Both flows will attach an auth code in the URI, so I assume the login flow wouldn't work since that auth code has been used, so I will click "authorize" after logging in to get a new auth code. Here is what I have to try and integrate with Genesys web messenger.

		const queryParams = new URLSearchParams(window.location.search);
		const code = queryParams.get('code')

		function rejectionHandler(e) {
			console.error(e);
		}

		if (code && Genesys) {
			console.log('code', code);

			Genesys(
				'subscribe',
				'Messenger.ready',
				() => {
					console.log('Messenger is ready');
				}
			);

			Genesys("subscribe", "Auth.ready", function () {
				console.log("Auth plugin is ready.");
			});

			Genesys("subscribe", "Auth.authenticated", function (data) {
				console.log("Auth plugin authenticated:", data);
			});

			Genesys("subscribe", "Auth.unauthenticated", function (data) {
				console.log("Auth plugin unauthenticated:", data);
			});

			Genesys("subscribe", "Auth.error", function (error) {
				console.error("Auth plugin error:", error)
			});

			Genesys("subscribe", "Auth.authenticating", function (data) {
				console.log("Auth plugin authenticating:", data)
			});

			Genesys('registerPlugin', 'AuthProvider', (AuthProvider) => {
				console.log('AuthProvider plugin registered');

				AuthProvider.registerCommand('getAuthCode', (e) => {
					console.log('getAuthCode command called');
					e.resolve({
						authCode: code.trim(),
						redirectUri: "http://localhost:3000/oauth2/redirect"
					});
				});

				// 	AuthProvider.registerCommand('reAuthenticate', (e) => {
				// 		console.log('reAuthenticate command called');
				// 		document.getElementById('authorizeButton').click();
				// 		e.resolve();
				// 	});
				AuthProvider.ready();
			});
		}

In summary, I believe I am setting up all the integrations, configurations, and deployments correctly, but it's still not getting past a 401 error. Therefore there is something that I am doing wrong, but I'm not sure what I'm missing.

Here is the response from the exchange endpoint.

{
    "message": "Failed to identify user for token:... deploymentId: ...",
    "code": "unauthorized",
    "status": 401,
    "contextId": "69207510-f3be-4b51-ac18-87b27ec3fdb6",
    "details": [],
    "errors": []
}

I have it figured out in my sandbox! From my current code base, I needed two redirect URI instead of just one. I'll follow up on this when I have the main codebase updated.

Hi,

Glad you figured this out.

As a general rule, I would advise to test manually the token exchange before any code implementation just to validate the auth provider configuration.
Starting with a basic config, and then adding more security parameters (nonce, pkce, iss, etc) if needed.

Best regards,
V.P.

With all due respect, that's what I've been doing. I didn't realize that the authentication seemingly needs two redirects, one to handle the login and one to handle the code.

Update, this did not work cross systems.

My little sandbox environment used Auth0, and everything seemed to work fine.

The company IDP is using PingFederate. Is there a known difference between the two? I'm seeing at ping federate that they have a special process for offline_access. Will this matter at all?