Skip to content

Authorize endpoint promise hangs Until timeout #333

Open
@rishijung377

Description

@rishijung377

Specify your setup

  • Operating System:
    Mac OS
  • Node version:
    v23.3.0
  • npm version:
    11.0.0
  • version of @node-oauth/oauth2-server
    5.2.0
  • which OAuth2 workflow:
    Grant Flow: "authorization_code"
  • at which workflow step does the error occur:
    Authorizatio flow

Describe the bug

Issue: After saving the authorization code, the response does not redirect with the redirect_uri and state parameter. Instead, the Promise hangs indefinitely.

Expected Behavior: The server should redirect to the provided redirect_uri with the state parameter after successfully saving the authorization code.

Observed Behavior: The process stalls, and the redirect does not occur.
Return data from authorize()

{
  authorizationCode: '6ac10a0b8a4f40f326646946308af63438bcb303c1ae5b021e26c265e183bcbc',
  client: {
    clientId: '12345',
    clientSecret: 'secret',
    grants: [ 'authorization_code' ],
    redirectUris: [ 'http://localhost:3003/secret' ]
  },
  user: { name: 'Rishi', _id: 1 }
}

Response Object:

Response {
  status: 302,
  body: {},
  headers: {
    location: 'http://localhost:3002/secret?code=f8db24809f8779d051f1d8a2c3a73e06a1689a81e6921550a6094ef170389bdb&state=ssotest'
  },
}

To Reproduce

Steps to reproduce the behavior:
Npm install with mentioned version, create a sample app with authorization grand type, the redirect does not happen
Alternatively, please add a link to a GitHub repo
that reproduces the error/s.

Expected behavior

A clear and concise description of what you expected to happen.

Screenshots

If applicable, add screenshots to help explain your problem.

Additional context

Add any other context about the problem here.

Activity

jankapunkt

jankapunkt commented on Feb 3, 2025

@jankapunkt
Member

Thank you @rishijung377 for reporting. I have some follow-up questions. First, did you encounter any error/warning in the consoles?

What was the exact last call, when the promise stalls and does not resolve? Can you paste a minimal code example, especially with the model implementation you used, please?

I also see you pasted a response object. Once the client receives this response it should redirect.

FInally, we have covered this in our integration tests, here:

https://github.com/node-oauth/node-oauth2-server/blob/4e40cc878efe96b756aa441c4d458087d05d6c64/test/integration/handlers/authorize-handler_test.js#L310..L360

Is there anything you can spot that might help you?

rishijung377

rishijung377 commented on Feb 5, 2025

@rishijung377
Author

Thank you @jankapunkt for the response. I have tried to answer all your queries, let me know if additional information is required.

Thank you @rishijung377 for reporting. I have some follow-up questions. First, did you encounter any error/warning in the consoles?

There is no any error logs. As stated earlier, the application does not route to the redirect url, and it keeps on loading. I followed the code flow, and the updateResponse does set the response to the redirect URL(hence the response object....)

What was the exact last call, when the promise stalls and does not resolve? Can you paste a minimal code example, especially with the model implementation you used, please?

I created a very simple code snippet where I was able to reproduce the error. This is not the full application setup, as it is missing token implementation. But the main gist of this sample application to show that the application does not gets routed to ${redirectURL}?code=``

const authCode = {};

const model = {
  getClient: async () => {
    return {
      clientId: '12345',
      clientSecret: 'secret',
      grants: ['authorization_code'],
      redirectUris: ['http://localhost:3003/secret']
    }
  },

  saveAuthorizationCode: async (code, client, user) => {
    authCode[code] = { code, client, user }
    return { authorizationCode: code.authorizationCode, client, user }
  },

  getAccessToken: async (code) => {
    return authCode[code]
  }
};

app.oauth = new oauthserver({
    model,
});

const checkAuthGrant = async (req, res, next) => {
  console.log('Hello from check auth grant')
  // note that all these are hard-coded for now
  req.query.client_id = '12345';
  req.query.redirect_uri = 'http://localhost:3003/secret';
  req.query.state='ssotest';
  req.query.response_type = 'code'
  req = new oauthserver.Request(req);
  res = new oauthserver.Response(res)

  const authRequest = {
    authenticateHandler: {
      handle: () => {
        return {
          name: 'Rishi',
          _id: 1
        }
      }
    }
  }
  const x = await app.oauth.authorize(req, res, authRequest);
  console.log(x);
  console.log(res.headers.location);
  console.log(res.status);
// the application keeps on hanging at this stage.
}

app.get('/', checkAuthGrant, function (req, res) {
    console.log(res);
});

app.all('/secret', (req, res) => {
  res.send('secret area')
})

I also see you pasted a response object. Once the client receives this response it should redirect.
Finally, we have covered this in our integration tests, here:
https://github.com/node-oauth/node-oauth2-server/blob/4e40cc878efe96b756aa441c4d458087d05d6c64/test/integration/handlers/authorize-handler_test.js#L310..L360

I saw the integration test, and I do have the same headers as in the integration tests, but I am not routed to the redirect URL. Logically, if the response has the correct Location header and status code, the redirect should work, right? However, it's not working here. Note that redirects work fine in other parts of the application—for example, in the auth flow, users without credentials are correctly redirected to the login page.

shrihari-prakash

shrihari-prakash commented on Feb 5, 2025

@shrihari-prakash
Contributor

Hello @rishijung377,

As I understand, the library in itself does not send any response to the client (matter of fact, it doesn't even assume what framework you use to build the server).

You would need to get the code and redirect it yourself:

/// rest of your code
const code = await app.oauth.authorize(req, res, authRequest);
const url = new URL(req.query.redirect_uri);
url.searchParams.set("code", code.authorizationCode);
url.searchParams.set("state", (req.query.state) || "abc");
return res.redirect(url.toString());
rishijung377

rishijung377 commented on Feb 5, 2025

@rishijung377
Author

Hello @rishijung377,

As I understand, the library in itself does not send any response to the client (matter of fact, it doesn't even assume what framework you use to build the server).

Hey @shrihari-prakash, I do not think that is the case. Check this code here https://github.com/node-oauth/node-oauth2-server/blob/master/lib/handlers/authorize-handler.js#L366. The package is setting the response to the redirect uri and making the redirect.
Anyways, I did try to route the application to the redirect url manually, It did not redirect to the required URL
redirect uri created from provided code snippet.
http://localhost:3003/secret?code=da1bee50dcf7c5f8403183bee948db511c5d625a25a32a9dfdd346525dc6a0b7&state=ssotest

shrihari-prakash

shrihari-prakash commented on Feb 5, 2025

@shrihari-prakash
Contributor

Hmm I just noticed the integration test sent by @jankapunkt as well... Looks like the status was indeed supposed to be set to a redirect.

About the manual redirect, what was your expectation? To me, what you pasted in your comment looks correct with code and state in the URL specified in the req.query.redirect_uri variable?

rishijung377

rishijung377 commented on Feb 5, 2025

@rishijung377
Author

Hmm I just noticed the integration test sent by @jankapunkt as well... Looks like the status was indeed supposed to be set to a redirect.

About the manual redirect, what was your expectation? To me, what you pasted in your comment looks correct with code and state in the URL specified in the req.query.redirect_uri variable?

The expected redirect does not happen and the application keeps on loading indefinetly

shrihari-prakash

shrihari-prakash commented on Feb 5, 2025

@shrihari-prakash
Contributor

@rishijung377 in that case, I think the problem is that you are overriting the express req and res objects with the ones in the library directly. By doing this, you lose all the functionalities like res.redirect. Consider doing something like:

const checkAuthGrant = async (req, res, next) => {
    console.log('Hello from check auth grant')
    req.query.client_id = '12345';
    req.query.redirect_uri = 'http://localhost:3003/secret';
    req.query.state='ssotest';
    req.query.response_type = 'code'
    const request = new oauthserver.Request(req);
    const response = new oauthserver.Response(res);
  
    const authRequest = {
      authenticateHandler: {
        handle: async () => {
          return ({
            name: 'Rishi',
            _id: 1
          })
        }
      }
    }
    const code = await app.oauth.authorize(request, response, authRequest);
    const url = new URL(req.query.redirect_uri);
    url.searchParams.set("code", code.authorizationCode);
    url.searchParams.set("state", req.query.state);
    console.log("Redirecting to", url.toString());
    return res.redirect(url.toString());
 }

This way, you are not overwriting the references permanently.

rishijung377

rishijung377 commented on Feb 6, 2025

@rishijung377
Author

@rishijung377 in that case, I think the problem is that you are overriting the express req and res objects with the ones in the library directly. By doing this, you lose all the functionalities like res.redirect.

Thank you @shrihari-prakash, That was the issue in the sample application I created, and with the suggested fix, I am now able to redirect successfully.

@jankapunkt The expected behavior I assume(based on here) is that the application (consumer) should be redirected automatically, but that’s not happening. Instead, we had to manually implement the redirect. Is this the intended behavior? If yes, we may want to document that.
I also had to manually redirect after token authentication using res.jsonp(response.body); wrapping the response.body in the Express Response object.

    const request = new OauthServer.Request(req);
    const response = new OauthServer.Response(res);

    await oauthServer.token(request, response, {});
    // Send a JSONP response to passport auth with response.body send from the oauth2-server
    res.jsonp(response.body);

Does this suggests the problem lies in how the response is being handled within the OAuth flow?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @jankapunkt@shrihari-prakash@rishijung377

        Issue actions

          Authorize endpoint promise hangs Until timeout · Issue #333 · node-oauth/node-oauth2-server