OAuth Buttons add-on

I created the
OAuth Buttons
add-on some time ago, and I just got around blogging about it:


OAuth with Vaadin - Login with Facebook/Twitter/…

I also added the sources to the code.google.com repository, which someone already asked for.

In short, OAuth Buttons makes "Login with [Facebook|Twitter|LinkedIn|…]
as easy as
new FacebookButton()
(well, almost), and then you get a callback when the user has been authenticated with the OAuth service in question.

Please go ahead and read the
blog post
, optionally
try it out
, then get back here to provide feedback!

Specifically I’d be interested in comments regarding the following (but don’t let that limit you):

  • Desired services (currently only Facebook/Twitter/LinkedIn but it’s easy to add more)
  • API (yeah, it springs from my use-case)
  • Need to do other stuff besides login (e.g service API access is possible, but not made obvious/easy)

As mentioned, this is mainly for login, but you should be able to access the services’ API by using the oauth-tokens you obtain when the user logs in (though I have not tried, please share your experiences with this). You can also use the Scribe library directly, of course.

Anyway, feel free to comment!

Best Regards,
Marc

Thanks for doing this project. It could prove very useful to outsource passwords, thereby relieving me of the main need to implement https/TLS/SSL for my Vaadin apps.

I have the simplest of questions:
→ How do I tie the user’s external login back to my app?

What exactly is the identifier so I know that user? What is it that I’m storing in my own “UserAccounts” data to tie that customer’s account with me to their various logins with Facebook, Twitter, or LinkedIn?

You mentioned that we can’t always retrieve the user’s email address from the Facebook/Twitter/LinkedIn service, so I guess I am not tracking the user by that.

Looking at your source code, I see a “User” interface with “getId” & “getScreenName” methods.

Am I supposed to call one of those methods on their first login and store the returned value as their UserID in my own UserAccounts database?

If so, which?

If so, does that mean I have to be able to store multiple values, one for each of the login services (Facebook, Twitter, Linkedin)?

The answer may be obvious, but I’m a newbie to external authentication.

Perhaps you could give the source code to a super-simple demo Vaadin app showing the process, such as your online demo app.

–Basil Bourque

Hi,

I just tried your implementation - works great!

However, for facebook it would be nice to be able to define the scope for a give user. Is there a way to do this with your API?

Cheers!

I, too, was wondering how people in general tie these external logins back to our app’s concept of a user. I know for low-security situations, like adding a comment to a blog, it may not really matter who they are other than they’ve done something to authenticate themselves for the individual “transaction.”

How would you model this for an app that has traditional user logins, but would like to allow these other forms of login if the user prefers it to remembering our app-specific username+password. Would you first force them to login traditionally, and then add these linkages in so we know that their login to Facebook/Twitter/LinkedIn is tied to it?

Has anybody evaluated the liability of outsourcing authentication since phishing scams are the like are more likely to target user logins to those big apps than they would be to our small app, so if someone gets hacked on Twitter (which is really quite rampant based on the number of “nasty rumors about you” type direct messages that all related to their accounts being compromised) they can then also login easily to your app.

Hi,

There are a number of ways to do this, but generally: you could use any data that the service in question guarantees is unique for each user, but the getId() is probably best unless you have specific reasons to use something else. I’d imagine twitter username could be used safely, though.
Please notice that getId() comes from the oauth service, and there is no guarantee it’s unique across oauth services, so you will probably have to use the service name in conjunction with the id.

If the user registers to your service using oauth, you might want to ask for additional information during the login-process, such as email.

Note that you might also want to tie multiple oauth accounts to one user account, for instance if you want your service to post messages to both twitter and facebook, or whatever. In this case I’d have the user log in first, then allow registering oauth accounts.

And yes, you have to store multiple id:s to be able to connect to multiple oauth services.

Note that if the user logs in with e.g twitter first, then facebook the next time, there is really not any way to know that it’s the same user, unless the user has previously tied both oauth accounts to his/her user account on your service. You might want to notify the user of this, or even provide a method of joining accounts, if the user accidentally created more than one.

So to sum up: 1. use getId() together with getService() to uniquely identify the user (“twitter:123456” or separately in your db) 2. ideally allow for multiple oauth accounts to be tied to one of your accounts 3. take measures to avoid users create multiple accounts by mistake by clicking different buttons each time 4. if you want to use oauth for more than login (i.e API access), you need to store the ‘secrets’ as well.

Did I answer everything? Did it make sense?

Best Regards,
Marc
(sorry for slow reply)

Hi,

FacebookButton has the following API:

    /**
     * If enabled, Facebook will be asked to return the users email address,
     * which will also be reflected in the dialog presented to the user by
     * Facebook.
     * 
     * @param enabled
     */
    public void setEmailScope(boolean enabled) {
        emailScope = enabled;
    }

By looking at how that works you could easily extend and use other scopes. I should probably change that API to support other scopes, though…

Best Regards,
Marc

I just touched on this topic in my previous answer that I just wrote, but no harm in attacking the problem one more time, I think:

One possible (more complete) solution is something like this:

  • if the oauth user id is found in your db, just go ahead an log in the user

  • if the oauthuser is actually registering (oauth id not in db), ask if the user has previously registered

    • if yes, ask him/her to do a login, which then ties the new oauth to the old account
    • if no, ask for whatever you need (desired username, email, …)
  • additionally, allow the user to tie oauth services to the account while logged in

There are quite many sites using facebook and twitter for login these days, so I’m sure there should be some good examples online (note to self: go find some).

First a disclaimer: You should evaluate this for each of your services yourself. But here are some thoughts:

Yes, by using federated login like this, you’re essentially putting all eggs in one basket - if a hacker manages to login as a user on the other service, he can also log in on your service.

However, once the other service (e.g facebook) notices this, and the situation is corrected, the hacker should no longer be able to log in to your service either. In effect the user only has to make sure the one service is secured, instead of changing passwords on all services separately. Which also brings forward another point: I think users are more likely to choose stupid/same passwords etc if they are forced to invent one for each service.

You might also want to consider the level of security the other service provides, and how much is at stake there. Does the user realize your service will be hacked if the other service is? If not, is the other account equally important to the user, as your account. What I’m getting at: if the other service is the users bank, he’s probably likely to try to keep it secure, while that might not be the case for some less important service. Conversely, please don’t use facebook login if you’re running a bank :wink:

In the end, federated login is excellent for the usability (sites that have measured show much improved conversion rates using e.g facebook login), but introduces security implications that might not be clear to the users. Something needs to be done about passwords - it’s a p-i-t-a right now, which makes users do stupid things. OAuth might not be the ultimate solution, but it certainly is a step in a good direction - at least for certain types of services.

IMHO the ultimate might be use an authority that the user absolutely trusts (has to trust, too much at stake), such as his bank, then work on making the login to that service as painless as possible while maintaining security (“Login with your bank” instead of “Login with Facebook”). But I digress.

I’m by no means an expert on OAuth or federated login as a whole, but hopefully this cleared some things up - or at least provides as a starting point for discussion.

Best Regards,
Marc

Important notice:

This does (IMO) not really relieve you of making use of SSL for your applications. Yes, the passwords will not be transferred plain-text over http, but other stuff the user submits still is, and the application is
still vulnerable to session-hijacking
.

Generally, please move to SSL if at all possible - all services should, and I hope we get there in the near future.

(Having said that, I do know this is an acceptable tradeoff for some services, as might be the case with yours. Please still consider using SSL, though - if only to lead the way :wink:

Best Regards,
Marc

This add-on is great and I can authorise against Twitter and Facebook, no problem.

However, with Facebook I am struggling to post a status update to user’s walls. My app has permission and if I use my own account it works fine, but not for other people.

I’ve set up a test user and notice when they are prompted to authorise that it doesn’t mention updating their wall.

Is there something I need to do to get that to work? Anyone got any ideas?

(Worked fine on Twitter)

Thanks in advance.

Cheers,
Chris

OK - I think this is to do with the scopes passed in the URL. Will subclass FacebookButton, test and report back.

Yep, it was the scope. If you want your Vaadin app to be able to do status updates, etc on Facebook you need to add the scope to the authentication request, regardless of what permissions your app is configured with. Anyway, here’s the code, should be obvious how to use it:

public class ScopedFacebookButton extends FacebookButton {

	private List<String> scopes = new LinkedList<String>();

	public ScopedFacebookButton(String apiKey, String apiSecret, OAuthListener authListener) {
		super(apiKey, apiSecret, authListener);
	}

	public ScopedFacebookButton(String caption, String apiKey, String apiSecret, OAuthListener authListener) {
		super(caption, apiKey, apiSecret, authListener);
	}

	@Override
	protected String getAuthUrl() {
		String url = getService().getAuthorizationUrl(null);

		if (isAskForEmail()) {
			scopes.add("email");
		}

		if (!scopes.isEmpty()) {
			url += "&scope=" + StringUtils.join(scopes, ",");
		}

		return url;
	}

	public void addScope(String scope) {
		scopes.add(scope);
	}

}

Loved the ideia, but it does not work with Vaadin 7. Any plans on updating this code ?
Been looking at the code and basically only OAuthButton needs to be updated. I’m trying to do it myself, but knowing almost nothing of Vaadin 6 and Vaadin 7 … or any Vaadin for that matter, I don’t quite which new classes substitute the old ones, etc.