passport-oauth icon indicating copy to clipboard operation
passport-oauth copied to clipboard

Optionally avoid parsing callbackURL

Open dbitting opened this issue 11 years ago • 11 comments

Certain Google+ OAuth flows make use of a callbackURL of postmessage, which is really just a string token. It should not be treated as a URL. This change introduces a new config property that can be included in the call to authenticate: parseCallbackURL. If this is present and has a value of false, then the value of callbackURL will be included AS IS into the OAuth request.

Usage looks something like this:

    passport.authenticate('google', 
        { callbackURL: 'postmessage',
          parseCallbackURL: false }),

Please review/incorporate as desired.

Thanks, --Doug

dbitting avatar Jun 06 '13 21:06 dbitting

Can you point me to some documentation on this so I can understand it better?

jaredhanson avatar Jun 06 '13 21:06 jaredhanson

This specifically supports the Google+ signin where you will be passing the one-time code from client to web server. See here: https://developers.google.com/+/web/signin/server-side-flow#step_4_add_the_sign-in_button_to_your_page

I'm beginning to wonder if there doesn't need to be a separate strategy for Google+ signin: while the existing profile retrieval works, the recommended API endpoint for Google+ signin is different, and returns a slightly different data structure.

Of course, this is all pretty new to me, so I'm feeling my way around these things. :)

dbitting avatar Jun 06 '13 21:06 dbitting

Thanks, that is interesting. I'm going to investigate this some more, and experiment with handling some of the flow on the client.

At first read, if you are doing the client-side flow, those shouldn't actually be redirecting to a new location, and thus the server would never see this value. If you are mixing client and server-side flows, I'd recommend using different callback URLs for them.

jaredhanson avatar Jun 06 '13 22:06 jaredhanson

After investigating this further, the postmessage flow that Google supports is entire a client-side. If you use this flow, the browser won't be redirected to your servers callback URL. Rather, client-side JavaScript is used to intercept the token.

I'm closing this down. If I'm overlooking any server-side involvement in this flow, I'd tend to think it should be a separate strategy.

jaredhanson avatar Aug 02 '13 07:08 jaredhanson

I may be misunderstanding what you mean by "entire[ly] client-side." In my flow, the browser initiates the OAuth flow via the G+ APIs and a callbackURL of postmessage. The browser receives back a one-time code that is passed to the server, like so:

Client: $http.get('/auth/google/callback', { params : { code : authResult.code } })

Server:

   var GoogleStrategy = require("passport-google-oauth").OAuth2Strategy;
...
        passport.use(new GoogleStrategy({
            clientID: ...,
            clientSecret: ...,
            callbackURL: "postmessage"
...
        app.get('/auth/google/callback', 
            passport.authenticate('google', { failureRedirect: 'http://...', parseCallbackURL: false }),
...

This code presumes the presence of my change so that postmessage isn't parsed, but assumed just to be a string token to be passed along. After this, the server can now interact with G+ to the extent allowed by the scope(s) indicated via the original client request.

Perhaps that better illustrates what's going on and helps determine if a separate strategy is warranted.

dbitting avatar Aug 04 '13 16:08 dbitting

I'm not 100% sure on all the ways to use this, but this was my impression:

If you want to initiate this "postmessage" flow (which typically uses a popup window), you start that process via JavaScript on the client side. Since "postmessage" isn't a URL, Google will never redirect back to it. Rather, they load a page that uses cross-document messaging to pass a token (and possible code) to the parent window (the one that opened the popup).

Because there's no server involved here anywhere, there's no reason to add special handling for this case to this strategy.

Now, I agree that you may want to post the code to the server. However, I don't think you should be posting that to the callback route, since that is intended for redirect-based flows, which this is not. Rather, post it to some API endpoint that handles things however your app requires.

I may be missing something here though. What happens in your "callback" route after you post to it via ajax? Most of these routes will typically end up establishing a session and redirecting. But, presumably, none of that is needed if you are just doing Ajax requests after a login popup.

jaredhanson avatar Aug 04 '13 17:08 jaredhanson

Your assessment seems about right. In my case, I'm not redirecting. I'm establishing a passport session, performing a bit of user-specific processing making use of the G+ session afforded by the one-time code (i.e., the server is interacting with G+), and returning some information back to the client based on that processing. It's entirely possible that I'm using the wrong tool for the job. :)

dbitting avatar Aug 04 '13 17:08 dbitting

Don't get me wrong, I think its great that your using this!

What I'm not clear on, though, is why callbackURL: 'postmessage' needs to be set as an option in this case? Couldn't you just set it to a normal URL, knowing that it'll never actually be redirected to?

Also, what interaction do you do with G+ on the server in this case? I'm assuming its the typical "exchange" portion of the OAuth flow, ie, submit the code and get a token? When you do this, does the redirect_uri parameter need to be set to "postmessage"? What if it is just a "dummy" URL?

Thanks for answering my questions!

jaredhanson avatar Aug 04 '13 18:08 jaredhanson

My recollection is that postmessage is required on both the client and the server side of this flow, or it fails to work. It would appear to be significant to Google.

The interaction between the server and G+ is illustrated above; it's completely controlled by passport and makes use of the code from the client side. Beyond that, in the callback provided to passport, I'm simply retrieving some user details (user id, email, etc.) for use within the server.

dbitting avatar Aug 04 '13 21:08 dbitting

OK, thanks for the info. Re-opening as a reminder for myself to revisit this.

jaredhanson avatar Aug 05 '13 14:08 jaredhanson

I would benefit from this change as well. I'll start working on a pull request when I get a chance.

losttime avatar Dec 17 '13 18:12 losttime