190

I'm building an application which will be hosted on a server. I want to build an API for the application to facilitate interaction with from any platform (Web App, Mobile App). What I'm not understanding is that when using the REST API, how do we authenticate the user.

For example, when a user has logged in and then wants to create a forum topic. How will I know that the user is already logged in?

Henke
  • 4,445
  • 3
  • 31
  • 44
Noor
  • 19,638
  • 38
  • 136
  • 254
  • 4
    You probably should search for "REST authentication" here. It's been covered in many other questions. – Brian Kelly Nov 03 '11 at 20:06
  • 12
    In a nutshell, let the client send a username and password with every request using HTTP Basic Auth (over SSL!), or authenticate once so the client has an authenticated session which will expire after some period of inactivity (or however you choose to override your web framework's' session handling). Said session can then be stored in a cookie, or be a parameter passed with every request (e.g. JSESSIONID in Java land). – opyate Apr 14 '12 at 08:26
  • See also [How to control who uses my web widget](http://stackoverflow.com/questions/12998701/how-to-control-who-uses-my-web-widget/). – Arjan Dec 15 '12 at 00:21
  • 1
    @opyate from the security point of view, it's not really a good idea to have the session handled using cookies in a REST API case, since attackers can send requests without the user consent. It's better to include a session hash or token in a HTTP header (such as Authorization). – s3v3n Mar 26 '16 at 22:50
  • 1
    @s3v3n Correct me if I'm wrong, but both your and my suggestions are just different ways of using a header + local storage combo to effect the same thing. It's `Authorization` header + e.g. browser localStorage VS `Cookie` header + standard browser cookie storage. – opyate Apr 01 '16 at 10:55
  • It depends on which domain the cookie is stored. If the API send the `Set-Cookie` and it's saved on the API domain - this is the case I see as a potential vulnerability as any request will be authenticated (even malicious ones). If the application is served from a different domain (static website, CDN, etc) and the cookie is set on that domain - it should be fine. – s3v3n Apr 02 '16 at 14:41
  • My recommendation for those viewing this post - don't roll your own - use what is already out there – BenKoshy Feb 06 '18 at 03:18

6 Answers6

125

For e.g. when a user has login.Now lets say the user want to create a forum topic, How will I know that the user is already logged in?

Think about it - there must be some handshake that tells your "Create Forum" API that this current request is from an authenticated user. Since REST APIs are typically stateless, the state must be persisted somewhere. Your client consuming the REST APIs is responsible for maintaining that state. Usually, it is in the form of some token that gets passed around since the time the user was logged in. If the token is good, your request is good.

Check how Amazon AWS does authentications. That's a perfect example of "passing the buck" around from one API to another.

*I thought of adding some practical response to my previous answer. Try Apache Shiro (or any authentication/authorization library). Bottom line, try and avoid custom coding. Once you have integrated your favorite library (I use Apache Shiro, btw) you can then do the following:

  1. Create a Login/logout API like: /api/v1/login and api/v1/logout
  2. In these Login and Logout APIs, perform the authentication with your user store
  3. The outcome is a token (usually, JSESSIONID) that is sent back to the client (web, mobile, whatever)
  4. From this point onwards, all subsequent calls made by your client will include this token
  5. Let's say your next call is made to an API called /api/v1/findUser
  6. The first thing this API code will do is to check for the token ("is this user authenticated?")
  7. If the answer comes back as NO, then you throw a HTTP 401 Status back at the client. Let them handle it.
  8. If the answer is YES, then proceed to return the requested User
starball
  • 20,030
  • 7
  • 43
  • 238
Kingz
  • 5,086
  • 3
  • 36
  • 25
  • So what you're describing is essentially a session cookie, right? – LordOfThePigs Aug 29 '14 at 19:07
  • yes, but the session is "maintained" at 2 different places. One in the API server, another in the Browser. The JSON (or whatever) response back to browser post successful login should communicate the session id on the API server back to the browser. These sessions are independently managed by their respective agents. – Kingz Aug 29 '14 at 19:13
  • @Kingz: That sounds exactgly like session cookies to me. For session cookies the server maintains the session id and associates it with a given user. The browser then stores that id in a cookie that it sends to the server every time. I'm not sure what you mean by "maintained" at 2 different places unless you mean the token is stored. – Chris Oct 22 '14 at 08:31
  • 13
    I believe Kingz was trying to impart the idea that the mechanism for maintaining the session is intentionally vague. A session cookie is just one implementation of that mechanism. – justin.hughey Nov 03 '14 at 15:01
  • 2
    @Kingz , What about security in this solution, for example if any hacker sniff the link with session_id and start sending requests which content correct session_id ? We can solve it by add ssl to server connection, but what about clients? – Ahmad Samilo Apr 25 '15 at 08:01
  • @Kingz: So overall. If I implement it. then I need to use cookies as token? Basically the token you are saying is nothing but the cookie right? – Veer Shrivastav Apr 30 '15 at 03:35
  • Is `JSESSIONID` typically saved in the db after it is created? I mean, how does the server know if the sent `JSESSIONID` is the correct one? – hytromo Jul 28 '15 at 21:26
  • JSESSIONID is ephemeral and is encoded in the cookie and stored in your browser. On the server side, it lives as long as the session lives. Then goes away. When it does go away, an incoming cookie with that JSESSIONID will be treated as an invalid request and the user will be asked to login again. – Kingz Aug 05 '15 at 20:40
  • 1
    how to prevent session hijacking in man-in-the-middle-attrack case ? – m0z4rt Oct 01 '15 at 10:57
  • So anybody with the token is the authenticated user. So its easy to hijack the session and pretend to be like the legitimate user. – indianwebdevil Apr 19 '16 at 04:27
  • @Kingz, should the session token be sent in the request header, or should it be part of the son request? So something like `{header:{token:'JJ'},body:{ItemId:5}}`. My guess is that the latter would require everything to be posted rather than getted _(is that the right word, I have never thought about that before)_. Keeping the token the header keeps the request clean, whether getting or posting (or .putting, etc). I guess the answer could be ether, I am just wondering if there is a standard _"Best Practice"_. – JonathanPeel Jan 14 '17 at 06:58
75

You can use HTTP Basic or Digest Authentication. You can securely authenticate users using SSL on the top of it, however, it slows down the API a little bit.

  • Basic authentication - uses Base64 encoding on username and password
  • Digest authentication - hashes the username and password before sending them over the network.

OAuth is the best it can get. The advantages oAuth gives is a revokable or expirable token. Refer following on how to implement: Working Link from comments: https://www.ida.liu.se/~TDP024/labs/hmacarticle.pdf

ankitjaininfo
  • 11,961
  • 7
  • 52
  • 75
  • 5
    Please read [this question](http://stackoverflow.com/questions/454355/security-of-rest-authentication-schemes) and the answer provided by Les Hazelwood (author of Apache Shiro). – justin.hughey Nov 03 '14 at 15:11
  • 1
    Useful link how does Twitter protects it's REST API: [Twitter REST API Security](https://dev.twitter.com/oauth) – WildDev May 27 '16 at 06:33
  • As this is the accepted answer I feel it's important to mention that you can also use Digest authentication. Read about differences between Basic and Digest authentications here: https://stackoverflow.com/questions/9534602/what-is-the-difference-between-digest-and-basic-authentication – Rayee Roded Jan 30 '18 at 23:16
39
  1. Use HTTP Basic Auth to authenticate clients, but treat username/password only as temporary session token.

    The session token is just a header attached to every HTTP request, eg: Authorization: Basic Ym9ic2Vzc2lvbjE6czNjcmV0

    The string Ym9ic2Vzc2lvbjE6czNjcmV0 above is just the string "bobsession1:s3cret" (which is a username/password) encoded in Base64.

  2. To obtain the temporary session token above, provide an API function (eg: http://mycompany.com/apiv1/login) which takes master-username and master-password as an input, creates a temporary HTTP Basic Auth username / password on the server side, and returns the token (eg: Ym9ic2Vzc2lvbjE6czNjcmV0). This username / password should be temporary, it should expire after 20min or so.

  3. For added security ensure your REST service are served over HTTPS so that information are not transferred plaintext

If you're on Java, Spring Security library provides good support to implement above method

gerrytan
  • 40,313
  • 9
  • 84
  • 99
  • 1
    Why should it expire after 20 min? what if it's a website like facebook that the login is until the user logs out? – Dejell Feb 16 '15 at 17:22
  • 1
    @dejel I was under the assumption "session" is temporary in nature. It is common for it to expire if the user is idling – gerrytan Jul 29 '15 at 08:24
  • What's the Base64 for? You could just return the temporary password. In both cases, what really matters is that this temporary password is strong. Check https://security.stackexchange.com/a/19686/72945 – e18r Sep 14 '18 at 17:31
7

I think the best approach is to use OAuth2. Google it and you will find a lot of useful posts to help you set it up.

It will make easier to develop client applications for your API from a web app or a mobile one.

Hope it helps you.

franzlorenzon
  • 5,845
  • 6
  • 36
  • 58
Paulo Henrique
  • 1,025
  • 8
  • 12
  • 2
    Please read [this question](http://stackoverflow.com/questions/454355/security-of-rest-authentication-schemes) and the answer by Les Hazelwood (author of Apache Shiro). – justin.hughey Nov 03 '14 at 15:05
3

Here's a guided approach.

Your authentication service issues a JWT token that is signed using a secret that is also available in your API service. The reason they need to be there too is that you will need to verify the tokens received to make sure you created them. The nice thing about JWTs is that their payload can hold claims as to what the user is authorised to access should different users have different access control levels.

That architecture renders authentication stateless: No need to store any tokens in a database unless you would like to handle token blacklisting (think banning users). Being stateless is crucial if you ever need to scale. That also frees up your API service from having to call the authentication server at all as the information they need for both authentication and authorisation are in the issued token.

Flow (no refresh tokens):

  1. User authenticates with the authentication server (eg: POST /auth/login) and receives a JWT token generated and signed by the auth server.
  2. User uses that token to talk to your API and assuming user is authorised), gets and posts the necessary resources.

There are a couple of issues here. Namely, that auth token in the wrong hands provides unlimited access to a malicious user to pretend they are the affected user and call your APIs indefinitely. To handle that, tokens have an expiry date and clients are forced to request new tokens whenever expiry happens. That expiry is part of the token's payload. But if tokens are short-lived, do we require users to authenticate with their usernames and password every time? No. We do not want to ask a user for their password every 30min to an hour, and we do not want to persist that password anywhere in the client. To get around that issue, we introduce the concept of refresh tokens. They are longer lived tokens that serve one purpose: act as a user's password, authenticate them to get a new token. Downside is that with this architecture your authentication server needs to persist these refresh token in a database.

New flow (with refresh tokens):

  1. User authenticates with the authentication server (eg: POST /auth/login) and receives a JWT token generated and signed by the auth server, alongside a long lived (eg: 6 months) refresh token that they store securely
  2. Whenever the user needs to make an API request, the token's expiry is checked. Assuming it has not yet expired, user uses that token to talk to your API and assuming user is authorised), gets and posts the necessary resources.
  3. If the token has indeed expired, there is a need to refresh your token, user calls authentication server (EG: POST / auth/token) and passes the securely stored refresh token. Response is a new access token issued.
  4. Use that new token to talk to your API image servers.

OPTIONAL (banning users)

How do we ban users? Using that model there is no easy way to do so. Enhancement: Every persisted refresh token includes a blacklisted field and only issue new tokens if the refresh token isn't black listed.

Things to consider:

  • You may want to rotate refresh token. To do so, blacklist the refresh token each time your user needs a new access token. That way refresh tokens can only be used once. Downside you will end up with a lot more refresh tokens but that can easily be solved with a job that clears blacklisted refresh tokens (eg: once a day)
  • You may want to consider setting a maximum number of allowed refresh tokens issued per user (say 10 or 20) as you issue a new one every time they login (with username and password). This number depends on your flow, how many clients a user may use (web, mobile, etc) and other factors.
  • Logout endpoint in your authentication service may or may not blacklist refresh tokens. Something to think about.
Joe
  • 2,386
  • 1
  • 22
  • 33
0

I've been using the JWT authentication. Works just fine in my application.

There is an authentication method that will require the user credentials. This method validates the credentials and returns an access token in case of success.

This token must be sent to every other method in my Web API in the header of the request.

It's pretty easy to implement, and very easy to test.

Arturio
  • 418
  • 1
  • 7
  • 25