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

Question: How to use bell to link multiple providers to one saved user account? #134

Closed
Icehunter opened this issue Sep 7, 2015 · 10 comments
Labels
documentation Non-code related changes support Questions, discussions, and general support

Comments

@Icehunter
Copy link

Hi!

As the title says; once I login with one; how would I go about using bell to login with another but linking the two on the back end?

Between google/facebook/twitter I get different information. On the first one I save the user to the session with hapi-auth-cookie; however by the second time around (since I use bell for the auth strategy) it's gone at that time and not shared between routes.

Thanks!

@ldesplat
Copy link
Contributor

ldesplat commented Sep 9, 2015

Are you missing some information?

Can you share how your are doing it? If you are only storing state in the cookie, then you could store the auth tokens depending on which strategy is used such as:

auth: {
   twitter: {
      token: 'blabla',
      profile: {}
   },
   facebook: {},
   last_used: 'twitter'
}

Or something similar? I have to guess, the state you have to keep in the cookie would get pretty big, so maybe offload that to some in-memory database if the cookie gets too big.

@Icehunter
Copy link
Author

I have the /login/{provider} routes setup like in the examples.

On the main service I have:

server.auth.strategy('session', 'cookie', {
    password: 'mypassword',
    cookie: 'sid-site',
    redirectTo: '/login',
    redirectOnTry: false,
    clearInvalid: true,
    isSecure: false,
    validateFunc: function (request, session, callback) {
        callback(null, true, {});
    }
});

So in any routes I have session as my strategy. But of course for the login routes I have twitter, google, etc.

When I log:
request.auth in the handlers for twitter/etc I only see the object which has come back/been set the bell plugin, and only after I do request.auth.session.set(data); does it set it in the hapi-auth-cookie.

I guess the issue I see is that I can't share the data from request.auth (cookie) with the routes on login therefore not knowing if someone's already logged in.

I could be doing this completely wrong though.

@ldesplat
Copy link
Contributor

I would have to put a gist together to show this but if you want to try for yourself, until I get to it, you basically have to combine the auth strategies together. There is no Hapi way to do it, but basically you create a new strategy which does the hapi-auth-cookie stuff, and then does the bell login. Like this, you have access to the things you need. On your other routes, you use the hapi-auth-cookie strategy like always.

@ldesplat ldesplat added the documentation Non-code related changes label Sep 10, 2015
@ldesplat
Copy link
Contributor

Oh and just in case your use case is simpler than I think it is. You can set up multiple strategies on a route. So you can set hapi-auth-cookie and then facebook by doing auth: ['session', 'facebook']. The downside is that facebook login will not be attempted if the user is already logged in via the previous strategy.

I think I am correct that you need to link multiple social accounts to one login but it may help some other people with the more common use case.

@ravisuhag
Copy link

@Icehunter the way I am handling it, is by checking request.state['Basic-auth'] for hapi-cookie-auth in case it is there, integrate the social data otherwise sign up user. Would love to receive comments. One thing I am struggling with is, figuring out the best approach for long-lived tokens. @ldesplat @hueniverse any suggestion for that ?

exports.connect = function(provider) {
    // Return config object for hapi route
    return {
        auth: provider,
        handler: function(request, reply) {
            if (!request.auth.isAuthenticated) {
                request.session.flash('error', 'Authentication failed due to: ' + request.auth.error.message);
                reply.redirect('/me/settings/networks');
            }
            // If user is signed in then connect existing account with provider
            if (request.state['Basic-auth']) {
                delete request.auth.credentials.profile.raw;
                var id = request.state['Basic-auth']._id.toString();
                var update = {
                    $set: {}
                };
                update.$set['networks.' + provider] = request.auth.credentials;
                var options = {
                    new: true
                };
                User.findByIdAndUpdate(id, update, options, function(err, user) {
                    if (err) {
                        request.session.flash('error', 'An internal server error occurred');
                        reply.redirect('/me/settings/networks');
                    }
                    request.session.flash('success', 'Profile successfully saved');
                    reply.redirect('/me/settings/networks');
                });
            }
            // TODO : Signup user if not logged in 


        }
    };
};

@ldesplat
Copy link
Contributor

@ravisuhag There are lots of ways to handle long lived tokens. But in the end it's a simple call to exchange the short lived one for the long lived one. I like to do it in the background through some sort of queue. You can also do it when a user logs in/signs up, verify the token and after you've replied to the user, just go ahead and exchange the token if you don't have a long term one already.

I store the long lived token in my database since I usually need to do stuff while the user is not interacting with the application. If you need access to it, I like using hapijs/yar for storing session data.

@ravisuhag
Copy link

Great, thanks. Seems like we have to have a differnt exchange strategy for each provider. I was thinking of writing a generic function for all the providers as in above comment. But I guess we can live with one helper for each provider to get long lived token.

@Icehunter
Copy link
Author

@ravisuhag Thanks for the response although I tried what you have and I never see hapi-auth-cookie in state. Or basic auth.

I have one global handler function and buy the time it gets the request info the state and credentials have been overwritten with the new strategy used.

    server.route({
        method: [
            'GET',
            'POST'
        ],
        path: '/login/twitter',
        handler: function (request, reply) {
            handleLogin(request, reply, 'twitter');
        },
        config: {
            auth: {
                strategies: [
                    'twitter'
                ]
            }
        }
    });

@ldesplat ldesplat closed this as completed Feb 3, 2016
@acmoune
Copy link

acmoune commented May 5, 2017

@Icehunter This is not a good idea anyway, why would you do that ? You should instead make sure it is not allowed in your app, for example by checking if the email address from the provider is already in your database, considering that you use the same email on all your providers accounts.

@Marsup Marsup added support Questions, discussions, and general support and removed question labels Sep 20, 2019
@lock
Copy link

lock bot commented Jan 9, 2020

This thread has been automatically locked due to inactivity. Please open a new issue for related bugs or questions following the new issue template instructions.

@lock lock bot locked as resolved and limited conversation to collaborators Jan 9, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
documentation Non-code related changes support Questions, discussions, and general support
Projects
None yet
Development

No branches or pull requests

5 participants