openid / AppAuth-JS

JavaScript client SDK for communicating with OAuth 2.0 and OpenID Connect providers.
Apache License 2.0
975 stars 162 forks source link

Notifier never calls the authorization listener #195

Open isgj opened 2 years ago

isgj commented 2 years ago

Expected Behavior

Clear documentation that shows how to handle the callback with code

[REQUIRED] Describe expected behavior

It will be nice to show what happens when the (web)app is redirected to the callback URL with the code

Describe the problem

I've configured and started the SSO flow. My app gets redirected to the callback URL /auth?code=jsDWmM6u8ebY1wtDM.... From this point it is not clear what should happen. The library does nothing, it doesn't exchange the code for tokens, the authorization listener gets never called. Can I make a request to exchange the code? How do I get the verifier? From the electron sample the verifier is taken from the request in the listener.

 AuthorizationServiceConfiguration.fetchFromIssuer('/oauth')
            .then(response => {
                this.configuration = response;
                this.showMessage('Completed fetching configuration');
            })
            .catch(error => {
                console.log('Something bad happened', error);
                this.showMessage(`Something bad happened ${error}`);
            });
        this.authorizationHandler.setAuthorizationNotifier(this.notifier);
        this.notifier.setAuthorizationListener((request, response, error) => {
            log('Authorization request complete ', request, response, error);
            if (response) {
                this.code = response.code;
                this.showMessage(`Authorization Code ${response.code}`);
            }
        });

[REQUIRED] Environment

bobber205 commented 2 years ago

@isgj It's because the port is hardcoded to 8000 so if your callback url isn't localhost:8000 the notifier won't capture/see the event and do the token exchange. I'm struggling with the same issue atm

bobber205 commented 2 years ago

@isgj Here's what we're doing

this.notifier.setAuthorizationListener(async (request, response, error) => {

    if (response) {
      const codeVerifier = request.internal && request.internal.code_verifier

      let code_verifier = this.originalExpressRequest.session.auth_request.internal.code_verifier

      let code_verifier_internal = this.originalExpressRequest.session.code_verifier

      this.makeTokenRequest(response.code, codeVerifier)
        .then(response => {
          this.authStateEmitter.emit(AuthStateEmitter.ON_TOKEN_RESPONSE, response)
        })
    }
  })
isgj commented 2 years ago

@bobber205 do you have an electron app or it's hosted?

tracplus-hpaterson commented 2 years ago

This issue could depend on what framework you're suing, and how it generates query strings. We found AppAuth's default query string handler assumes a # is present in the URL for routing, Angular style. Other frameworks, such as React, may omit this, confusing the parser so it won't pick up the OAuth response from the query string.

You can work around this by extending the BasicQueryStringUtils from AppAuth to assume a hash is never present:

/**
 * @class NoHashQueryStringUtils
 *
 * `NoHashQueryStringUtils` extends AppAuth.js' default query string parser
 * (designed for Angular) to never assume `#`s are used for internal routing.
 *
 * This works around a bug where React URLs feature no hash, and so the parser
 * never detects the query string and OAuth parameters.
 */
class NoHashQueryStringUtils extends BasicQueryStringUtils {
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  parse(input, useHash) {
    return super.parse(input, false);
  }
}

const ssoAuthHandler = new RedirectRequestHandler(new LocalStorageBackend(), new NoHashQueryStringUtils(), window.location, new DefaultCrypto());
flieks commented 2 years ago

I am also having this problem in React. I tried the NoHashQueryStringUtils. No errors but the callback never gets called.

srmxlt commented 2 years ago

I'm facing this problem in a react app as well. In the redirected page, the async notifier is never invoked e.g. notifier.setAuthorizationListener(async (request, response, error) => { }) anyone know when this authorization listener should be invoked? localstorage update event?

jordanchang commented 2 years ago

Does your app call await this.authorizationHandler.completeAuthorizationRequestIfPossible() on load?

thardyman commented 1 year ago

This issue could depend on what framework you're suing, and how it generates query strings. We found AppAuth's default query string handler assumes a # is present in the URL for routing, Angular style. Other frameworks, such as React, may omit this, confusing the parser so it won't pick up the OAuth response from the query string.

You can work around this by extending the BasicQueryStringUtils from AppAuth to assume a hash is never present:

/**
 * @class NoHashQueryStringUtils
 *
 * `NoHashQueryStringUtils` extends AppAuth.js' default query string parser
 * (designed for Angular) to never assume `#`s are used for internal routing.
 *
 * This works around a bug where React URLs feature no hash, and so the parser
 * never detects the query string and OAuth parameters.
 */
class NoHashQueryStringUtils extends BasicQueryStringUtils {
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  parse(input, useHash) {
    return super.parse(input, false);
  }
}

const ssoAuthHandler = new RedirectRequestHandler(new LocalStorageBackend(), new NoHashQueryStringUtils(), window.location, new DefaultCrypto());

I feel like this should be in the documentation, or even better, add the NoHashQueryStringUtils to the core library.

jv18creator commented 1 year ago

I am having same issue with my react app, any solution found for this?