Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error: State is not matching #8

Open
nodesocket opened this issue Oct 29, 2014 · 13 comments
Open

Error: State is not matching #8

nodesocket opened this issue Oct 29, 2014 · 13 comments

Comments

@nodesocket
Copy link

Seeing intermittent errors calling OAuth.auth(), where it falls into the fail() block. The err is simply:

[Error: State is not matching]

What does that mean? We are using redis to store sessions by the way.

Here is the full block of code we are using:

////
// OAuth signin route
////
app.post('/oauth/signin', [
    oauthLogger,
    jsonParser,
    function(req, res) {
        if(!req.body) {
            res.status(400).send({
                error: {
                    status_code: 400,
                    status: 'Bad Request',
                    message: 'Missing required body'
                }
            }).end();

            return;
        }

        if(!req.body.provider || !req.body.code) {
            res.status(400).send({
                error: {
                    status_code: 400,
                    status: 'Bad Request',
                    message: 'Missing required parameter'
                }
            }).end();

            return;
        }

        OAuth.auth(req.body.provider, req.session, {
            code: req.body.code
        })
        .then(function(requestObject) {
            return requestObject.me();
        })
        .then(function(me) {
            if(!me.raw.id) {
                res.status(500).send({
                    error: {
                        status_code: 500,
                        status: 'Internal Server Error',
                        message: 'Missing required id'
                    }
                }).end();

                return;
            }

            req.session.oauth.provider = req.body.provider;
            req.session.oauth.id = me.raw.id;

            res.status(200).send({
                email: me.email,
                avatar: me.avatar
            }).end();
        })
        .fail(function(err) {
            res.status(503).send({
                error: {
                    status_code: 503,
                    status: 'Service Unavailable',
                    message: err.toString()
                }
            }).end();
        });
    }
]);
@nodesocket
Copy link
Author

Think I figure this out, was generating a state token, then calling req.session.destroy(). Oops.

@sparkyfen
Copy link

@nodesocket Can you be more specific on where you had the issue? I'm also have the same error and would like to know if it's related. Thanks!

@nodesocket
Copy link
Author

I thought we fixed it, but seeing it again.

Error: State is not matching

Any official word from oauth.io? It seems intermittent. It will happen, then we wait a few minutes and the error magically goes away. Looking at the coffee script source authentication.coffee, I found:

if (not session?.csrf_tokens? or response.state not in session.csrf_tokens)
    defer.reject new Error 'State is not matching'

@sparkyfen
Copy link

@nodesocket So I'm noticing that after the generateStateToken(req.session) method is called. The req.session method contains a value of csrf_tokens: [ 'xxxxx-xxx-xxx-xx-xxxxxxxx' ]. That value is being checked in session.csrf_tokens in your if statement above. The response.state value is when https://oauth.io/auth/accesstoken is called. Are you using the server-side only workflow or the client-side?

You can see a working example at the fork I made for the sdk-node-tutorial:

https://github.com/brutalhonesty/sdk-node-tutorial

I'm able to get the client to server-side workflow working, however, I cannot test my endpoint because I need to have a code to make with in the session right now. Going to see if I can load the req.session value with a static code and that way the if statement is true.

@nodesocket
Copy link
Author

We are using server-side only. Here is basically all the node.js code paths we have. Do you spot anything wrong?

https://gist.github.com/nodesocket/d23eef069cbfae27a248

@sparkyfen
Copy link

@nodesocket I'm a bit confused with the oauthLogger and jsonParser objects. Other than that the only thing that might be breaking it is you are using the me() method on line 49. I had trouble using reqObject.get('/me') when I was using Google as a provider but no trouble with Facebook. The reqObject.me() worked for Google but I did not test it with Facebook.

My client-server workflow is here:

https://gist.github.com/brutalhonesty/96b0450776cc5a27ca3b

@mzarella
Copy link

@nodesocket @brutalhonesty Did either of you get around this? I'm currently at the state of #12, but when I add in oauth.initialize 'LONG_STRING_FOR_APP_ID', 'LONG_STRING_FOR_APP_SECRET', I am receiving this error.

@mzarella
Copy link

@brutalhonesty @nodesocket, below is an rfc:

Potential Cause 1

Does the client need to pass the token to the server, is that what is causing this? If so, how do you instruct oauth to look for this token?

Potential Cause 2

Does req.body.code or in the case of what is referenced in #12, req.body.data, need to be the same as req.session.id when calling the auth method (oauth.auth)?

Potential Cause 3

Does it have something to do with csrf? I am not using csrf so it makes sense that oauth.__getCache() would return an empty array for csrf_tokens:

{ public_key: 'LONG_STRING_FOR_APP_ID',
  secret_key: 'LONG_STRING_FOR_APP_SECRET',
  csrf_tokens: [],
  oauthd_url: 'https://oauth.io',
  oauthd_base: '/auth' }

@sparkyfen
Copy link

When I dug into this issue a few months ago, I think it was somewhat related to cause 3 but I need to dig into it again soon for an existing project. Until then, I won't be able to help much with a possible solution.

@mzarella
Copy link

@brutalhonesty I think you are right. I'm looking at what oauth.generateStateToken does and it creates csrf tokens. I thought I wouldn't need to use csrf since I thought I was using token based authentication, but that isn't the case.

@mzarella
Copy link

@william26 I could use your help on this. A POST to /oauth/signin always fails with Error: State is not matching. In that first debugger statement, req.session.csrf_tokens doesn't exist, so that's probably why it's not matching. Why would it be empty? /oauth/token calls generateStateToken which calls oauthio/js/lib/csrf_generator.js which generates a csrf token and places it in the session, right? Any kind of tip would be helpful.

Here's the code:

  app.get('/oauth/token', function(req, res) {
    var token = oauth.generateStateToken(req.session);
    return res.send(token);
  });

  app.post("/oauth/signin", function(req, res) {
    debugger;
    var code = req.body.data;
    oauth.auth("google", req.session, {
      code: code
    }).then(function(request_object) {
      debugger;
      return res.send(200, "The user is authenticated");
    }).fail(function(e) {
      debugger;
      console.log(e);
      return res.send(400, "Code is incorrect");
    });
    return res.redirect(req.session.returnTo || '/');
  });

@bbonet
Copy link

bbonet commented Apr 15, 2016

I had the same issue and figured out that I was not using the express-session middleware in node. Using this and node-uuid I was able to pass in the session to the generateStateToken() call. The following are just code snippets and are not intended to run as is.

Where you do your requires

var session = require('express-session')
var uuid = require('node-uuid');

Where you set up your middleware

app.use(session({ 
    genid: function(req) { 
        return uuid.v4();
        },  
    secret: 'some secret', 
    resave: false, 
    saveUninitialized: true
}));

Where you request the initial token from the client. I do this on client page load.

app.get('/email/twitter/oauth/token', function(req, res){
   var token = OAuth.generateStateToken(req.session);
    console.log('token', token);
    console.log('OAuthgetCsrfTokens', OAuth.getCsrfTokens(req.session));
    console.log('req.session.id', req.session.id);
    res.status(200).send(token);
});

Now when the post to log in is received I call this function which now works

function twitterLogin(req, res, code) {
    console.log('twitterLogin', code);
    OAuth.auth('google', req.session, {
            code: code
        })
        .then(function (r) {
            // Do something with r.access_token,
            // or r.get|post|put|delete|patch|me()
            // Or just send a success message :
            console.log('twitterLogin success response', r);
            res.status(200).send(r.access_token);
        })
        .fail(function (e) {
            // Handle an error
            console.log(e);
            res.status(500).send('An error occured');
        });
}

I hope this helps someone. It was a bit of a struggle to figure out.

Brent

@skyserpent
Copy link

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants