Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free.

For my job we have a nice RESTful webservice we've built out that we use to drive a couple websites we have. Basically the webservice lets you create and work with support tickets, and the website is responsible for the front end. Any webservice requests use an auth header which we use to validate the user and their password for each call.

This year we're looking to expand our login options so that users on the website can log in via Google, Twitter, and Facebook (possibly others). However I'm having a lot of trouble figure out how to architect this so the webservice can use the 3rd party authentication providers to ensure the users is who they say they are. Is there any best practices out there for how to do this?

Currently we're thinking of having the website handle authenticating the users themselves, and then use a new setSessionId call that registers their current session with the webservice back end. Each additional request to the webservice will pass along that sessionId and will validate it. These seems okay, but I have that feeling in the back of my head that I'm not thinking this through and all my forum browsing and reading oauth and openid specs is just confusing me more. Any tips for how to tackle this?

share|improve this question
    
nothing really wrong with what you're suggesting. i find it easier to look at openid integration example code than the actual oauth and openid specs... –  spaceman Aug 14 '13 at 14:46
    
What language / platform are you using? You don't need to reinvent the wheel, as there are frameworks to substantially help you. :) –  RobM Aug 19 '13 at 17:18
    
@Rob The webservice is hosted on Salesforce.com, but is accessed via a proxy which as of this writing is Node.js. That said it feels like the general question would apply to all platforms. And yes, I'd hope to use a framework rather than reinventing the wheel, just not sure what wheel to use. –  Ralph Callaway Aug 20 '13 at 3:19
    
@Ralph Yep, the general question is irrespective of platform, but it has practical implications, as the platform will generally narrow down your framework options quite a bit. So, you've a custom built front-end app using node.js to a salesforce-hosted webservice & data repository? Do you need to store user/identity information in the backend, or just authentication and authorization on actions in the front-end? –  RobM Aug 20 '13 at 4:16
    
@RobM yes, we'll want to store user info on the backend, i.e. email, firstname, lastname, plus whatever is needed to validate future calls from the webservice consumers once they've been authenticated. –  Ralph Callaway Aug 20 '13 at 4:53

4 Answers 4

up vote 11 down vote accepted
+100

It sounds like there are two goals:

  1. Easy for end-users to authenticate with their existing social accounts
  2. Easy for developers using your webservice

Authorizing people to use resources on your site makes OAuth2 a preferred mechanism due to the popularity and availability of client libraries.

1. Easy for end-users to authenticate with their existing social accounts

The end-user visits a site that uses your API and chooses to login. They are sent to your OAuth login page. Your login page shows a normal username and password prompt for accounts managed on your site and a set of social auth buttons where they can click to login via a site like Facebook. When the user chooses Facebook you redirect them to Facebook to approve the choice (starting the facebook auth flow). When the end-user completes the login at Facebook they are redirected back to your site.

When the user is redirected back to your site from Facebook you save that user's information into a user record in your database and then generate a new session for that user. You immediately redirect the end-user to their original downstream site with the oauth access_token, completing the original oauth flow.

2. Easy for developers using your webservice

If you are the provider of authorization then you should create a simple interface for developers to depend upon which does not change every time you add a new upstream auth provider which doesn't implement oauth perfectly. This is why I believe you should implement an OAuth2 provider site and that site should be a consumer of the social auth sites.

To the developer using your nice rest API, they will not be aware of the Facebook interaction unless you choose to give them a hint through the session capture post (for example).

TL;DR

Make consumers of your APIs like you by implementing OAuth2 and hiding social auth complexities. You can, during your oauth flow for your downstream sites, trigger an additional oauth flow with facebook.

Image because picture == words * 1000:

enter image description here

Can we call this oauth2-piggy-back?

Step by step flow

  1. End user visits site that uses your API
  2. End user is sent to your site to authorize or signup (oauth2)
  3. End user picks social auth, clicks facebook login button
  4. Your site sets a cookie or sets the state in the facebook oauth to know where the user came from
  5. End user redirected to Facebook and accepts connection at facebook site
  6. End user redirected to your site to complete facebook auth process
  7. You lookup or create the user in your database
  8. You create a new session on your server
  9. You redirect the user back to their original site with your session token
share|improve this answer

How to make it extensible

First you should notice all these api's use the same mechanism for logging in. They all use OAuth for their authentication. This you need to leverage by starting with a general OAuth library. Don't use their own libraries for authentication, these will be unusable for other providers. If you get the hang of OAuth2 it is quite easy to add more providers.

You need unfortunately two of them, because twitter still hasn't jumped the OAuth2 bandwagon.

OAuth needs you to create an interface for the authenticating party. The tokens will be exchanged server to server. Create one entry point, which can handle all the communication.

The token should be stored in a separate table from your account, this is because their can be multiple tokens and multiple linked profiles. Some services give you two tokens, one of them is a refresh token.

Now you design an interface, which encapsulates the other functionality you need. I would personally setup a separate REST-service for this. This way you can easily extend the authentication to other places.
Some services use JSON to communicate, other go for XML etc. For the front user you need to unify them all. This is a pretty painful process, but it is possible to derive some common grounds here.

Another problem here, is that not all the services provide the same functionality. This can mean, that your services cannot provide the full API as you specified. You need to have a strategy here, which let the application gracefully downgrade.

This all will ensure you can easily add new 3rd party providers.

Token problems

The tokens are limited in time, thus you need a couple of cron jobs, which can check if the token is still usable, otherwise you have to delete it. You can also refresh a token by this mechanism.

It sometimes happens, that an user retracts the token. Be ready for this.

Data storage

If you have this design, you need to think about the data you need. This follows partly from your just created interface. Design some tables for this and look if the data is actually retrievable. Some services don't let you grab a lot of data. You should also take into account, that the more data you need, the heftier the privacy messages become. So be modest in your needs, otherwise users won't use it.

For extra verification, you can store the profiles in a separate, but linked table to your users. This will provide you with a lot more information about someone.

Also check your local laws, for some data you need extra precautions.

Last thing Don't make the fault where you don't create an account on your own services. If the user gets banned from facebook, he will effectively be unable to login on your service. This is a situation you don't want to create. This is often overlooked.

share|improve this answer

I would definitely go with the solution it sounds like you've already figured out: implementing the 3rd party authentication on your client-facing website and then associating those 3rd party auth tokens with your website user accounts, and then finally trigger your setSessionID call on login.

Depending on your website architecture, you may find using a library like EveryAuth or Passport to be very helpful.

share|improve this answer

My two cents: I've never done anything like this before nor do I know how the FB, Twitter or Google login mechanisms work, but a few issues popped up in my head as soon as I read your question:

  • Multiple logins: What happens if I log in with my Facebook account one day and my Google account the next? Or simultaneously? Do you treat these two accounts as unique, separate accounts or should you have some method to associate the two and allow me to access my tickets either way?
  • Relying on external identifiers: What happens when Facebook or Twitter decide to change the way their account identifiers look? To illustrate, if BazLogin represents a unique account using the code {4382-af56} but decides that from now on accounts will have 12 digits because 8 were not enough, how do you tell {1234-4382-af56} from {0000-4382-af56}?

We can deal with these two issues by choosing to associate the external accounts with an internal account, not just a session ID. Then, an external login could be just a gateway to the creation of an internal account. Should I authenticate with more than one login method, you could tell me I'm already logged in. If an external auth provider changes something we rely on, we could ask the user to provide the name and password of their internal account and create a new association for future logins.

I'm not sure I addressed the issues you had in mind, but you didn't really mention any concrete concerns. I hope my answer was helpful, either way.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.