Linking multiple social accounts to a single user #14

Closed
francisdb opened this Issue Dec 26, 2011 · 43 comments

Comments

Projects
None yet
@francisdb
Contributor

francisdb commented Dec 26, 2011

As far as I can see this is not available.

@jaliss

This comment has been minimized.

Show comment
Hide comment
@jaliss

jaliss Dec 26, 2011

Owner

You are right. This is not something that SecureSocial provides now.

Owner

jaliss commented Dec 26, 2011

You are right. This is not something that SecureSocial provides now.

@poornerd

This comment has been minimized.

Show comment
Hide comment
@poornerd

poornerd Jan 16, 2012

Contributor

I would also like to do this... @francisdb did you come up with a solution?

Or @jaliss do you have any ideas or tips?

Contributor

poornerd commented Jan 16, 2012

I would also like to do this... @francisdb did you come up with a solution?

Or @jaliss do you have any ideas or tips?

@francisdb

This comment has been minimized.

Show comment
Hide comment
@francisdb

francisdb Jan 16, 2012

Contributor

I'm still early in development of our service and as it is a side project I have not looked at this yet.

Contributor

francisdb commented Jan 16, 2012

I'm still early in development of our service and as it is a side project I have not looked at this yet.

@poornerd

This comment has been minimized.

Show comment
Hide comment
@poornerd

poornerd Jan 16, 2012

Contributor

me too... ;-) just thought I would check before trying a few more ideas

Contributor

poornerd commented Jan 16, 2012

me too... ;-) just thought I would check before trying a few more ideas

@jaliss

This comment has been minimized.

Show comment
Hide comment
@jaliss

jaliss Jan 16, 2012

Owner

We need to think well how this should be done. Not everybody will need to connect multiple accounts to a single account.

Owner

jaliss commented Jan 16, 2012

We need to think well how this should be done. Not everybody will need to connect multiple accounts to a single account.

@francisdb

This comment has been minimized.

Show comment
Hide comment
@francisdb

francisdb Jan 20, 2012

Contributor

Maybe some page where you can see your linked accounts and where you can add more by clicking on the icons? A user might also want to disconnect a social account.

Contributor

francisdb commented Jan 20, 2012

Maybe some page where you can see your linked accounts and where you can add more by clicking on the icons? A user might also want to disconnect a social account.

@jaliss

This comment has been minimized.

Show comment
Hide comment
@jaliss

jaliss Sep 24, 2012

Owner

This is in the roadmap for version 2.

Owner

jaliss commented Sep 24, 2012

This is in the roadmap for version 2.

@gguan

This comment has been minimized.

Show comment
Hide comment
@gguan

gguan Nov 16, 2012

Can we do something similar to play-authenticate? Like add merge and link methods in UserService trait?
The link/merge account feature is very useful.

gguan commented Nov 16, 2012

Can we do something similar to play-authenticate? Like add merge and link methods in UserService trait?
The link/merge account feature is very useful.

@phammer

This comment has been minimized.

Show comment
Hide comment
@phammer

phammer Nov 16, 2012

Contributor

feature request +1.
Rename SocialUser to SocialAccount and use a user object to combine several SocialAccounts?

@jaliss: I would contribute for this feature request (implementation, testing, whatever helps you the most). I'll have a closer look at this over the weekend.

Contributor

phammer commented Nov 16, 2012

feature request +1.
Rename SocialUser to SocialAccount and use a user object to combine several SocialAccounts?

@jaliss: I would contribute for this feature request (implementation, testing, whatever helps you the most). I'll have a closer look at this over the weekend.

@jaliss

This comment has been minimized.

Show comment
Hide comment
@jaliss

jaliss Nov 16, 2012

Owner

@gguan @phammer I have this on the road map, in fact I was going to start working on it soon. There's been a lot of chances in the code lately ;-)

I'll think what's the best way to implement this and we can share ideas @phammer.

Owner

jaliss commented Nov 16, 2012

@gguan @phammer I have this on the road map, in fact I was going to start working on it soon. There's been a lot of chances in the code lately ;-)

I'll think what's the best way to implement this and we can share ideas @phammer.

@phammer

This comment has been minimized.

Show comment
Hide comment
@phammer

phammer Nov 16, 2012

Contributor

Glad to hear that and happy to give some input. Let me know when you have the master plan ready :)

Contributor

phammer commented Nov 16, 2012

Glad to hear that and happy to give some input. Let me know when you have the master plan ready :)

@jaliss

This comment has been minimized.

Show comment
Hide comment
@jaliss

jaliss Nov 16, 2012

Owner

thanks! I will :)

Owner

jaliss commented Nov 16, 2012

thanks! I will :)

@phammer

This comment has been minimized.

Show comment
Hide comment
@phammer

phammer Nov 26, 2012

Contributor

Hi Jorge

Just a short update from my side. In order to clear the way for account linking, we went with the following data model (Mongo DB as persistence backend).

  • socialuser: we use the given object as "runtime user object", and do not store it directly in the DB.
  • user
    • lastLogin
    • loginCount
    • List[Account]
    • "further subobjects holding user related information such as contact information"
  • account
    • accountId (provider specific ID)
    • providerId (facebook, google, userpass)
    • lastLogin
    • loginCount
    • authMethod

Matching between a SocialUser and a user is done via account (account.accountId == socialUser.id.id && account.providerId). Linking is simple, i.e., it means a user has several accounts.

Having this data model, we have two nice features:

  • separation of runtime and persistence data
  • quite independent of securesocial, i.e., we can plugin securesocial without any customization.

But what would be really cool is to have a user trait that is used through out SecureSocial instead of an actual clase. For example, introduce "RuntimeUser" as trait and extend SocialUser with RuntimeUser. Having that, it would be even easier to customize SecureSocial to every projects need.

=> actually, if you agree. I could do the change (introduce the RuntimeUser trait) and sent a pull request.

Regards,
Patrick

Contributor

phammer commented Nov 26, 2012

Hi Jorge

Just a short update from my side. In order to clear the way for account linking, we went with the following data model (Mongo DB as persistence backend).

  • socialuser: we use the given object as "runtime user object", and do not store it directly in the DB.
  • user
    • lastLogin
    • loginCount
    • List[Account]
    • "further subobjects holding user related information such as contact information"
  • account
    • accountId (provider specific ID)
    • providerId (facebook, google, userpass)
    • lastLogin
    • loginCount
    • authMethod

Matching between a SocialUser and a user is done via account (account.accountId == socialUser.id.id && account.providerId). Linking is simple, i.e., it means a user has several accounts.

Having this data model, we have two nice features:

  • separation of runtime and persistence data
  • quite independent of securesocial, i.e., we can plugin securesocial without any customization.

But what would be really cool is to have a user trait that is used through out SecureSocial instead of an actual clase. For example, introduce "RuntimeUser" as trait and extend SocialUser with RuntimeUser. Having that, it would be even easier to customize SecureSocial to every projects need.

=> actually, if you agree. I could do the change (introduce the RuntimeUser trait) and sent a pull request.

Regards,
Patrick

@jaliss

This comment has been minimized.

Show comment
Hide comment
@jaliss

jaliss Nov 26, 2012

Owner

@phammer thanks a lot for the update. Give me a couple of days to go over this. Having a busy week. I also need to consider how the Java API would work.

Owner

jaliss commented Nov 26, 2012

@phammer thanks a lot for the update. Give me a couple of days to go over this. Having a busy week. I also need to consider how the Java API would work.

@jaliss

This comment has been minimized.

Show comment
Hide comment
@jaliss

jaliss Nov 29, 2012

Owner

@phammer would you be willing to chat a few minutes about this? send me an email if interested.

Owner

jaliss commented Nov 29, 2012

@phammer would you be willing to chat a few minutes about this? send me an email if interested.

@phammer

This comment has been minimized.

Show comment
Hide comment
@phammer

phammer Dec 2, 2012

Contributor

@jaliss did you got my invitation on linkedin? did not have your email address and didn't want to post mine here.

Contributor

phammer commented Dec 2, 2012

@jaliss did you got my invitation on linkedin? did not have your email address and didn't want to post mine here.

@poornerd

This comment has been minimized.

Show comment
Hide comment
@poornerd

poornerd Jan 1, 2013

Contributor

Is this what you meant with "Introduced Identity trait" in the Change Log?

Contributor

poornerd commented Jan 1, 2013

Is this what you meant with "Introduced Identity trait" in the Change Log?

@jaliss

This comment has been minimized.

Show comment
Hide comment
@jaliss

jaliss Jan 1, 2013

Owner

Not yet, but that will help because users will have different identities later.

Owner

jaliss commented Jan 1, 2013

Not yet, but that will help because users will have different identities later.

@DrewEaster

This comment has been minimized.

Show comment
Hide comment
@DrewEaster

DrewEaster Apr 7, 2013

Is there any update on when account linking might appear in SecureSocial?

Is there any update on when account linking might appear in SecureSocial?

@mikenikles

This comment has been minimized.

Show comment
Hide comment
@mikenikles

mikenikles Apr 21, 2013

+1 for an update as we're about to go down that path. Thanks

+1 for an update as we're about to go down that path. Thanks

@almeidap

This comment has been minimized.

Show comment
Hide comment
@almeidap

almeidap May 13, 2013

+1 Waiting for an update on this as well. Thanks!

+1 Waiting for an update on this as well. Thanks!

@cndreiter

This comment has been minimized.

Show comment
Hide comment
@cndreiter

cndreiter May 13, 2013

+1 Also waiting for an update. Thanks

+1 Also waiting for an update. Thanks

@mikenikles

This comment has been minimized.

Show comment
Hide comment
@mikenikles

mikenikles May 20, 2013

We took @phammer's approach as a starting point and from there it's not a big deal to implement the rest. It works fine and if/when SecureSocial provides a built-in solution, I don't expect too much re-work to get our implementation changed.

We took @phammer's approach as a starting point and from there it's not a big deal to implement the rest. It works fine and if/when SecureSocial provides a built-in solution, I don't expect too much re-work to get our implementation changed.

@kotancode

This comment has been minimized.

Show comment
Hide comment
@kotancode

kotancode May 29, 2013

+1
I'm also in the camp where I have one internal account that can be mapped to multiple social network identities. I haven't implemented it yet, and would like to be able to do it as part of SecureSocial and not as a bolt-on or a hack.

+1
I'm also in the camp where I have one internal account that can be mapped to multiple social network identities. I haven't implemented it yet, and would like to be able to do it as part of SecureSocial and not as a bolt-on or a hack.

@andrewswan

This comment has been minimized.

Show comment
Hide comment
@andrewswan

andrewswan Jun 6, 2013

+1 for this enhancement please!

At the moment, if a person chooses to log into my application with say Google one day and Facebook the next, they will end up with two totally separate accounts, which is not good from a UX point of view.

P.S. Thanks for SecureSocial to start with, it's already saved me a lot of work!

+1 for this enhancement please!

At the moment, if a person chooses to log into my application with say Google one day and Facebook the next, they will end up with two totally separate accounts, which is not good from a UX point of view.

P.S. Thanks for SecureSocial to start with, it's already saved me a lot of work!

@gmethvin

This comment has been minimized.

Show comment
Hide comment
@gmethvin

gmethvin Jun 11, 2013

Contributor

I'm really interested in this feature. I already have models set up in my application which should allow me to link multiple social users to a single user account.

Is there a hack/workaround I can use in the meantime (without completely forking SecureSocial)? It seems like I might be able to create my own providers and override the doAuth method to do what I want.

Contributor

gmethvin commented Jun 11, 2013

I'm really interested in this feature. I already have models set up in my application which should allow me to link multiple social users to a single user account.

Is there a hack/workaround I can use in the meantime (without completely forking SecureSocial)? It seems like I might be able to create my own providers and override the doAuth method to do what I want.

@ajevans85

This comment has been minimized.

Show comment
Hide comment
@ajevans85

ajevans85 Jun 15, 2013

I'm just looking in to @phammer solution.

I'm guessing this is implemented by a custom UserServicePlugin?

I can see how the User having a list of Accounts work. What I'm not understanding is a reasonable way to populate an existing User with a new Account object?

As an example:

I'm a new user, I sign in using facebook. My custom UserServicePlugin would call save creating a new User object with a new account linked to it.

Next time I visit the site I sign in with my Twitter account. I now have a brand new account but I'd like it to link to the old one.

This is where it gets complicated. When I sign in we get email Option[String] . I could see if this email address already exists under a account (if the email is provided), if it is found we can link it that way? Can we trust this email address to link against?

For Facebook / Linkedin providers I guess we can trust the email as they require email verification before enabling accounts. If it's a different provider there's potential for a malicious user to provide a email which isn't theres, when we then check our database and find a match with a existing user someone has just hijacked the account.

I'd ideally like linking to happen on sign in time if we detect common information, going through the above I'm not so sure now.

My second thought is to allow linking an account when signed in. This I think would be complex? Eg I login via facebook but have a previous twitter account, I'm thinking on the my account page have something to link the accounts. So I click a link twitter account button and then get given a twitter sign in, when authenticated if the twitter id returned matches an existing account object, it moves that account object to the new user as we can trust this id value. This then gives the issue of cleaning up any content associated to a user.

This get's a even bigger problem to solve the more I think about it, to do it in a clean secure way.

I'm just looking in to @phammer solution.

I'm guessing this is implemented by a custom UserServicePlugin?

I can see how the User having a list of Accounts work. What I'm not understanding is a reasonable way to populate an existing User with a new Account object?

As an example:

I'm a new user, I sign in using facebook. My custom UserServicePlugin would call save creating a new User object with a new account linked to it.

Next time I visit the site I sign in with my Twitter account. I now have a brand new account but I'd like it to link to the old one.

This is where it gets complicated. When I sign in we get email Option[String] . I could see if this email address already exists under a account (if the email is provided), if it is found we can link it that way? Can we trust this email address to link against?

For Facebook / Linkedin providers I guess we can trust the email as they require email verification before enabling accounts. If it's a different provider there's potential for a malicious user to provide a email which isn't theres, when we then check our database and find a match with a existing user someone has just hijacked the account.

I'd ideally like linking to happen on sign in time if we detect common information, going through the above I'm not so sure now.

My second thought is to allow linking an account when signed in. This I think would be complex? Eg I login via facebook but have a previous twitter account, I'm thinking on the my account page have something to link the accounts. So I click a link twitter account button and then get given a twitter sign in, when authenticated if the twitter id returned matches an existing account object, it moves that account object to the new user as we can trust this id value. This then gives the issue of cleaning up any content associated to a user.

This get's a even bigger problem to solve the more I think about it, to do it in a clean secure way.

@kotancode

This comment has been minimized.

Show comment
Hide comment
@kotancode

kotancode Jun 15, 2013

Linking once already signed in is the only way I can see it being done reliably. My Facebook and twitter emails do not match, which is a common scenario. I may also have two twitter accounts, guaranteeing that they don't match.

What I envisioned is once you log in to create a new account, that is a master account with a unique id. If you then sign in to a second oauth account while there is still a root account id in your session, you get a linked account instead of a new one.

Sent from my iPhone

On Jun 15, 2013, at 5:35 PM, Adam Evans notifications@github.com wrote:

I'm just looking in to @phammer solution.

I'm guessing this is implemented by a custom UserServicePlugin?

I can see how the User having a list of Accounts work. What I'm not understanding is a reasonable way to populate an existing User with a new Account object?

As an example:

I'm a new user, I sign in using facebook. My custom UserServicePlugin would call save creating a new User object with a new account linked to it.

Next time I visit the site I sign in with my Twitter account. I now have a brand new account but I'd like it to link to the old one.

This is where it gets complicated. When I sign in we get email Option[String] . I could see if this email address already exists under a account (if the email is provided), if it is found we can link it that way? Can we trust this email address to link against?

For Facebook / Linkedin providers I guess we can trust the email as they require email verification before enabling accounts. If it's a different provider there's potential for a malicious user to provide a email which isn't theres, when we then check our database and find a match with a existing user someone has just hijacked the account.

I'd ideally like linking to happen on sign in time if we detect common information, going through the above I'm not so sure now.

My second thought is to allow linking an account when signed in. This I think would be complex? Eg I login via facebook but have a previous twitter account, I'm thinking on the my account page have something to link the accounts. So I click a link twitter account button and then get given a twitter sign in, when authenticated if the twitter id returned matches an existing account object, it moves that account object to the new user as we can trust this id value. This then gives the issue of cleaning up any content associated to a user.

This get's a even bigger problem to solve the more I think about it, to do it in a clean secure way.


Reply to this email directly or view it on GitHub.

Linking once already signed in is the only way I can see it being done reliably. My Facebook and twitter emails do not match, which is a common scenario. I may also have two twitter accounts, guaranteeing that they don't match.

What I envisioned is once you log in to create a new account, that is a master account with a unique id. If you then sign in to a second oauth account while there is still a root account id in your session, you get a linked account instead of a new one.

Sent from my iPhone

On Jun 15, 2013, at 5:35 PM, Adam Evans notifications@github.com wrote:

I'm just looking in to @phammer solution.

I'm guessing this is implemented by a custom UserServicePlugin?

I can see how the User having a list of Accounts work. What I'm not understanding is a reasonable way to populate an existing User with a new Account object?

As an example:

I'm a new user, I sign in using facebook. My custom UserServicePlugin would call save creating a new User object with a new account linked to it.

Next time I visit the site I sign in with my Twitter account. I now have a brand new account but I'd like it to link to the old one.

This is where it gets complicated. When I sign in we get email Option[String] . I could see if this email address already exists under a account (if the email is provided), if it is found we can link it that way? Can we trust this email address to link against?

For Facebook / Linkedin providers I guess we can trust the email as they require email verification before enabling accounts. If it's a different provider there's potential for a malicious user to provide a email which isn't theres, when we then check our database and find a match with a existing user someone has just hijacked the account.

I'd ideally like linking to happen on sign in time if we detect common information, going through the above I'm not so sure now.

My second thought is to allow linking an account when signed in. This I think would be complex? Eg I login via facebook but have a previous twitter account, I'm thinking on the my account page have something to link the accounts. So I click a link twitter account button and then get given a twitter sign in, when authenticated if the twitter id returned matches an existing account object, it moves that account object to the new user as we can trust this id value. This then gives the issue of cleaning up any content associated to a user.

This get's a even bigger problem to solve the more I think about it, to do it in a clean secure way.


Reply to this email directly or view it on GitHub.

@gmethvin

This comment has been minimized.

Show comment
Hide comment
@gmethvin

gmethvin Jun 16, 2013

Contributor

I agree with @kotancode. You can't rely on the email. You need to use something from a logged in user's session to link the accounts.

I've implemented my own version of linking accounts on top of SecureSocial. It seems like a bit of a hack, but here's how I did it:

  • I store the user ID (the user account in my application, not the social user ID) in the session while the user is logged in.
  • I wrote my own implementation of the Identity trait called UserIdentity which contains the user ID. This is essentially a wrapper for SocialUser.
  • I have a trait called UserIdentityProvider which overrides the authenticate method of the providers and creates a UserIdentity, and mix this in with all the providers I use.
  • In a custom implementation of UserService, I use the user ID in my custom UserIdentity to link the newly authenticated social user to an existing user account.

That said, when a user authenticates for the first time, you do probably want to check if the user's email matches another in the system and provide a way to link the accounts. This might be something you'd just want to allow users to provide a custom implementation for though. Making the controllers extensible classes instead of objects (particularly ProviderController) would help a lot for this sort of thing.

Contributor

gmethvin commented Jun 16, 2013

I agree with @kotancode. You can't rely on the email. You need to use something from a logged in user's session to link the accounts.

I've implemented my own version of linking accounts on top of SecureSocial. It seems like a bit of a hack, but here's how I did it:

  • I store the user ID (the user account in my application, not the social user ID) in the session while the user is logged in.
  • I wrote my own implementation of the Identity trait called UserIdentity which contains the user ID. This is essentially a wrapper for SocialUser.
  • I have a trait called UserIdentityProvider which overrides the authenticate method of the providers and creates a UserIdentity, and mix this in with all the providers I use.
  • In a custom implementation of UserService, I use the user ID in my custom UserIdentity to link the newly authenticated social user to an existing user account.

That said, when a user authenticates for the first time, you do probably want to check if the user's email matches another in the system and provide a way to link the accounts. This might be something you'd just want to allow users to provide a custom implementation for though. Making the controllers extensible classes instead of objects (particularly ProviderController) would help a lot for this sort of thing.

@kotancode

This comment has been minimized.

Show comment
Hide comment
@kotancode

kotancode Jun 16, 2013

Greg,
That's basically the hack I was planning on implementing I just haven't gotten around to it yet :)

If you could share any of that, it would be great!

Thanks,
Kevin

Sent from my iPhone

On Jun 15, 2013, at 10:43 PM, Greg Methvin notifications@github.com wrote:

I agree with @kotancode. You can't rely on the email. You need to use something from a logged in user's session to link the accounts.

I've implemented my own version of linking accounts on top of SecureSocial. It seems like a bit of a hack, but here's how I did it:

I store the user ID (the user account in my application, not the social user ID) in the session while the user is logged in.
I wrote my own implementation of the Identity trait called UserIdentity which contains the user ID. This is essentially a wrapper for SocialUser.
I have a trait called UserIdentityProvider which overrides the authenticate method of the providers and creates a UserIdentity, and mix this in with all the providers I use.
In a custom implementation of UserService, I use the user ID in my custom UserIdentity to link the newly authenticated social user to an existing user account.
That said, when a user authenticates for the first time, you do probably want to check if the user's email matches another in the system and provide a way to link the accounts. This might be something you'd just want to allow users to provide a custom implementation for though. Making the controllers extensible classes instead of objects (particularly ProviderController) would help a lot for this sort of thing.


Reply to this email directly or view it on GitHub.

Greg,
That's basically the hack I was planning on implementing I just haven't gotten around to it yet :)

If you could share any of that, it would be great!

Thanks,
Kevin

Sent from my iPhone

On Jun 15, 2013, at 10:43 PM, Greg Methvin notifications@github.com wrote:

I agree with @kotancode. You can't rely on the email. You need to use something from a logged in user's session to link the accounts.

I've implemented my own version of linking accounts on top of SecureSocial. It seems like a bit of a hack, but here's how I did it:

I store the user ID (the user account in my application, not the social user ID) in the session while the user is logged in.
I wrote my own implementation of the Identity trait called UserIdentity which contains the user ID. This is essentially a wrapper for SocialUser.
I have a trait called UserIdentityProvider which overrides the authenticate method of the providers and creates a UserIdentity, and mix this in with all the providers I use.
In a custom implementation of UserService, I use the user ID in my custom UserIdentity to link the newly authenticated social user to an existing user account.
That said, when a user authenticates for the first time, you do probably want to check if the user's email matches another in the system and provide a way to link the accounts. This might be something you'd just want to allow users to provide a custom implementation for though. Making the controllers extensible classes instead of objects (particularly ProviderController) would help a lot for this sort of thing.


Reply to this email directly or view it on GitHub.

@gmethvin

This comment has been minimized.

Show comment
Hide comment
@gmethvin

gmethvin Jun 17, 2013

Contributor

@kotancode Sure. The app isn't open source but I can provide a few snippets. Here's a gist with some of the code: https://gist.github.com/gmethvin/5794543

I also would like to have a way to offer to link accounts when I think the user might have another user on the system (e.g. if the user has the same email). If it were possible to override the methods of ProviderController, I think this would be easy to customize, but I'm not sure how best to do it now.

Contributor

gmethvin commented Jun 17, 2013

@kotancode Sure. The app isn't open source but I can provide a few snippets. Here's a gist with some of the code: https://gist.github.com/gmethvin/5794543

I also would like to have a way to offer to link accounts when I think the user might have another user on the system (e.g. if the user has the same email). If it were possible to override the methods of ProviderController, I think this would be easy to customize, but I'm not sure how best to do it now.

@andrewswan

This comment has been minimized.

Show comment
Hide comment
@andrewswan

andrewswan Jun 19, 2013

@gmethvin You guys are obviously working in Scala. I was wondering how a Java-based Play developer would go about linking multiple social accounts to the same user. I tried modifying my BaseUserService subclass so that it could detect if a user was already logged in, and if so, add the social account (if new) to that existing user. The code (or pseudo code in some places) looks as follows:

@Override
public Identity doSave(final Identity identity) {
    final UserId userId = identity.id();
    // In my data model, one User can have many Account instances
    final Account existingAccount = getAccountByUserId(userId); // this works
    if (existingAccount == null) {
        // This account is unknown to the application
        final Identity loggedInIdentity = SecureSocial.currentUser(); // FAILS (no HTTP context)
        if (loggedInIdentity == null) {
            LOGGER.debug("Creating new User with UserId '{}'", userId);
            // TODO store a new User and Account in the db
        }
        else {
            User existingUser = getUserFromIdentity(loggedInIdentity); // TODO implement this
            Account newAccount = Account.newFromIdentity(loggedInIdentity);
            // TODO add new account to existing user and save it in the db
        }
    }
    else {
        // This account is already known to the application (and therefore linked to a User)
    }
    return identity;
}

My problem is that the call to SecureSocial.currentUser() fails with the following error:

java.lang.RuntimeException: There is no HTTP Context available from here. at play.mvc.Http$Context.current(Http.java:30) ~[play_2.10.jar:2.1.1] at securesocial.core.java.SecureSocial.currentUser(SecureSocial.java:133) ~[securesocial_2.10-master-SNAPSHOT.jar:master-SNAPSHOT] at services.PersistentUserService.doSave(PersistentUserService.java:53) ~[classes/:na] at securesocial.core.java.BaseUserService.save(BaseUserService.java:88) ~[securesocial_2.10-master-SNAPSHOT.jar:master-SNAPSHOT] at securesocial.core.UserService$$anonfun$save$2.apply(UserService.scala:169) ~[securesocial_2.10-master-SNAPSHOT.jar:master-SNAPSHOT] at securesocial.core.UserService$$anonfun$save$2.apply(UserService.scala:169) ~[securesocial_2.10-master-SNAPSHOT.jar:master-SNAPSHOT]

Is there any way in Java for a BaseUserService subclass to detect whether a user is already logged in?

@gmethvin You guys are obviously working in Scala. I was wondering how a Java-based Play developer would go about linking multiple social accounts to the same user. I tried modifying my BaseUserService subclass so that it could detect if a user was already logged in, and if so, add the social account (if new) to that existing user. The code (or pseudo code in some places) looks as follows:

@Override
public Identity doSave(final Identity identity) {
    final UserId userId = identity.id();
    // In my data model, one User can have many Account instances
    final Account existingAccount = getAccountByUserId(userId); // this works
    if (existingAccount == null) {
        // This account is unknown to the application
        final Identity loggedInIdentity = SecureSocial.currentUser(); // FAILS (no HTTP context)
        if (loggedInIdentity == null) {
            LOGGER.debug("Creating new User with UserId '{}'", userId);
            // TODO store a new User and Account in the db
        }
        else {
            User existingUser = getUserFromIdentity(loggedInIdentity); // TODO implement this
            Account newAccount = Account.newFromIdentity(loggedInIdentity);
            // TODO add new account to existing user and save it in the db
        }
    }
    else {
        // This account is already known to the application (and therefore linked to a User)
    }
    return identity;
}

My problem is that the call to SecureSocial.currentUser() fails with the following error:

java.lang.RuntimeException: There is no HTTP Context available from here. at play.mvc.Http$Context.current(Http.java:30) ~[play_2.10.jar:2.1.1] at securesocial.core.java.SecureSocial.currentUser(SecureSocial.java:133) ~[securesocial_2.10-master-SNAPSHOT.jar:master-SNAPSHOT] at services.PersistentUserService.doSave(PersistentUserService.java:53) ~[classes/:na] at securesocial.core.java.BaseUserService.save(BaseUserService.java:88) ~[securesocial_2.10-master-SNAPSHOT.jar:master-SNAPSHOT] at securesocial.core.UserService$$anonfun$save$2.apply(UserService.scala:169) ~[securesocial_2.10-master-SNAPSHOT.jar:master-SNAPSHOT] at securesocial.core.UserService$$anonfun$save$2.apply(UserService.scala:169) ~[securesocial_2.10-master-SNAPSHOT.jar:master-SNAPSHOT]

Is there any way in Java for a BaseUserService subclass to detect whether a user is already logged in?

@gmethvin

This comment has been minimized.

Show comment
Hide comment
@gmethvin

gmethvin Jun 19, 2013

Contributor

@andrewswan This would be ugly, but you could have the same logic I have in the UserIdentityProvider#authenticate method in a static helper method that takes a provider and a request. You'll have to use scala types like Either in your Java code, but I think it would work.

Contributor

gmethvin commented Jun 19, 2013

@andrewswan This would be ugly, but you could have the same logic I have in the UserIdentityProvider#authenticate method in a static helper method that takes a provider and a request. You'll have to use scala types like Either in your Java code, but I think it would work.

@chrisbeach

This comment has been minimized.

Show comment
Hide comment
@chrisbeach

chrisbeach Jun 20, 2013

Contributor

+1 interested in this feature

Contributor

chrisbeach commented Jun 20, 2013

+1 interested in this feature

@nelsonblaha

This comment has been minimized.

Show comment
Hide comment
@nelsonblaha

nelsonblaha Nov 18, 2013

Contributor

Sounds like many of you have figured out how to do this, and that Identity has been implemented to make it a little easier. I'm just getting started with Play, but I'm going to attempt it from what's supplied here. Would love to see it supported.

Contributor

nelsonblaha commented Nov 18, 2013

Sounds like many of you have figured out how to do this, and that Identity has been implemented to make it a little easier. I'm just getting started with Play, but I'm going to attempt it from what's supplied here. Would love to see it supported.

@mkbehbehani

This comment has been minimized.

Show comment
Hide comment
@mkbehbehani

mkbehbehani Nov 26, 2013

I want this as well, and I'm hoping someone with massive power of the internets can create it.

Good luck, we're all counting on you.

I want this as well, and I'm hoping someone with massive power of the internets can create it.

Good luck, we're all counting on you.

@jaliss

This comment has been minimized.

Show comment
Hide comment
@jaliss

jaliss Jan 31, 2014

Owner

I just updated master-SNAPSHOT and included this feature. Please see the sample apps to see how that is being done and use it as a guide for your own apps. I'll close this issue but please open new ones if issues arise with the new implementation.

Thanks!

Owner

jaliss commented Jan 31, 2014

I just updated master-SNAPSHOT and included this feature. Please see the sample apps to see how that is being done and use it as a guide for your own apps. I'll close this issue but please open new ones if issues arise with the new implementation.

Thanks!

@jaliss jaliss closed this Jan 31, 2014

@andrewswan

This comment has been minimized.

Show comment
Hide comment
@andrewswan

andrewswan Feb 1, 2014

Super-excited to try this out - thanks heaps @jaliss !

Super-excited to try this out - thanks heaps @jaliss !

@andrewswan

This comment has been minimized.

Show comment
Hide comment
@andrewswan

andrewswan Feb 6, 2014

Please see the sample apps to see how that is being done

Jorge, could you please give a little more detail about which classes we should look at? I've looked in samples/java and samples/scala, but nothing jumps out at me. Are your changes on master or some other branch?

Please see the sample apps to see how that is being done

Jorge, could you please give a little more detail about which classes we should look at? I've looked in samples/java and samples/scala, but nothing jumps out at me. Are your changes on master or some other branch?

@jaliss

This comment has been minimized.

Show comment
Hide comment
@jaliss

jaliss Feb 14, 2014

Owner

Yes. Changes are in the master branch.

There is a new link method that does the account linking. The difference is how the UserService is implemented.

When you save an identity you should also create a new user (basically a an id) so you can group all the identities for a given user.

If you compare the new sample user service implementations with the old ones it should be clearer. In the previous samples an Identity is a user. In the new samples before you can save an identity you need to create the user that will own it.

Hope this clarifies a bit.

On 6 Feb 2014, at 00:46, Andrew Swan notifications@github.com wrote:

Please see the sample apps to see how that is being done

Jorge, could you please give a little more detail about which classes we should look at? I've looked in samples/java and samples/scala, but nothing jumps out at me. Are your changes on master or some other branch?


Reply to this email directly or view it on GitHub.

Owner

jaliss commented Feb 14, 2014

Yes. Changes are in the master branch.

There is a new link method that does the account linking. The difference is how the UserService is implemented.

When you save an identity you should also create a new user (basically a an id) so you can group all the identities for a given user.

If you compare the new sample user service implementations with the old ones it should be clearer. In the previous samples an Identity is a user. In the new samples before you can save an identity you need to create the user that will own it.

Hope this clarifies a bit.

On 6 Feb 2014, at 00:46, Andrew Swan notifications@github.com wrote:

Please see the sample apps to see how that is being done

Jorge, could you please give a little more detail about which classes we should look at? I've looked in samples/java and samples/scala, but nothing jumps out at me. Are your changes on master or some other branch?


Reply to this email directly or view it on GitHub.

@nelsonblaha

This comment has been minimized.

Show comment
Hide comment
@nelsonblaha

nelsonblaha Feb 23, 2014

Contributor

Source for link UserService example: 6c8f139

Contributor

nelsonblaha commented Feb 23, 2014

Source for link UserService example: 6c8f139

@abhishekShukla

This comment has been minimized.

Show comment
Hide comment
@abhishekShukla

abhishekShukla Mar 4, 2014

Hi,

I was looking at the sample code for linking two accounts together.

I was wondering what happens or what should happen in the following flow:

  1. I login using Facebook. (new user ID is created). I do my stuff and logout.
  2. I login using Twitter. (Again a new user ID is created). I do my stuff and logout.
  3. I login using Facebook. Now I try to link my Facebook and Twitter accounts.

def link(CURRENT: Identity, TO: Identity)

I guess you could do the following:

  1. Check if the TO Identity Id already exists. If it does, it will have its own application user ID.
  2. Change that application user ID to CURRENT's application user ID

Have I misunderstood anything?
Is this acceptable behavior? Is there a better way to do this?

Hi,

I was looking at the sample code for linking two accounts together.

I was wondering what happens or what should happen in the following flow:

  1. I login using Facebook. (new user ID is created). I do my stuff and logout.
  2. I login using Twitter. (Again a new user ID is created). I do my stuff and logout.
  3. I login using Facebook. Now I try to link my Facebook and Twitter accounts.

def link(CURRENT: Identity, TO: Identity)

I guess you could do the following:

  1. Check if the TO Identity Id already exists. If it does, it will have its own application user ID.
  2. Change that application user ID to CURRENT's application user ID

Have I misunderstood anything?
Is this acceptable behavior? Is there a better way to do this?

@peteoleary

This comment has been minimized.

Show comment
Hide comment
@peteoleary

peteoleary Nov 5, 2014

Here's what I did, seems to work fine for now:

  1. User authenticates through the SecureSocial login page using Github, Facebook, Google, whichever.
  2. New user gets a "base" User record in my DB. I generate a unique ID for this record which is not the provider ID.
  3. On an application page, user is invited to link other providers. When they authenticate, the link method is called with "current" and "to": I create a new User record with a "linkedTo" pointer back to the original login User record.
  4. Now the user has multiple DB records which all point back to the same original login record where I can fetch my internal unique ID.
  5. Anytime you see a save() or link(), always check first to see if the provider/id combination already exists. In your UI, you should give the user feedback whether they have already linked to a provider.

This scheme has the advantage that the user can now login in using any linked provider.

Here's what I did, seems to work fine for now:

  1. User authenticates through the SecureSocial login page using Github, Facebook, Google, whichever.
  2. New user gets a "base" User record in my DB. I generate a unique ID for this record which is not the provider ID.
  3. On an application page, user is invited to link other providers. When they authenticate, the link method is called with "current" and "to": I create a new User record with a "linkedTo" pointer back to the original login User record.
  4. Now the user has multiple DB records which all point back to the same original login record where I can fetch my internal unique ID.
  5. Anytime you see a save() or link(), always check first to see if the provider/id combination already exists. In your UI, you should give the user feedback whether they have already linked to a provider.

This scheme has the advantage that the user can now login in using any linked provider.

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