RESTful web service - how to authenticate requests from other services?

Web ServicesAuthenticationRestRestful AuthenticationClient Certificates

Web Services Problem Overview


I am designing a RESTful web service that needs to be accessed by users, but also other web services and applications. All of the incoming requests need to be authenticated. All communication takes place over HTTPS. User authentication is going to work based on an authentication token, acquired by POSTing the username and password (over an SSL connection) to a /session resource provided by the service.

In the case of web service clients, there is no end user behind the client service. The requests are initiated by scheduled tasks, events or some other computer operations. The list of connecting services is known beforehand (obviously, I guess). How should I authenticate these requests coming from other (web) services? I want the authentication process to be as easy as possible to implement for those services, but not at the cost of security. What would be the standard and best practices for a scenario like this?

Options that I can think of (or have been suggested to me):

  1. Have the client services resort to having a "fake" username and password, and authenticate them in the same way as users. I do not like this option - it just doesn't feel right.

  2. Assign a permanent application id for the client service, possibly an application key as well. As far as I have understood this is just the same as having username + password. With this id and key, I can either authenticate each request, or create an authentication token to authenticate further requests. Either way, I do not like this option, because anyone who can get a hold of the application id and key can impersonate the client.

  3. I could add an IP address check to previous option. This would make it harder to perform fake requests.

  4. Client certificates. Set up my own certificate authority, create root certificate, and create client certificates for the client services. A couple of issues come to mind, though: a) how do I still allow the users to authenticate without certificates and b) how complicated is this scenario to implement from the client service point of view?

  5. Something else - there must be other solutions out there?

My service would be running on Java, but I deliberately left out information about what specific framework it would be built on, because I am more interested on the basic principles and not so much on the implementation details - I assume the best solution for this will be possible to implement regardless of the underlying framework. However, I am a bit inexperienced with this subject, so concrete tips and examples on the actual implementation (such as useful third party libraries, articles, etc.) will be much appreciated as well.

Web Services Solutions


Solution 1 - Web Services

After reading your question, I would say, generate special token to do request required. This token will live in specific time (lets say in one day).

Here is an example from to generate authentication token:

(day * 10) + (month * 100) + (year (last 2 digits) * 1000)

for example: 3 June 2011

(3 * 10) + (6 * 100) + (11 * 1000) = 
30 + 600 + 11000 = 11630

then concatenate with user password, example "my4wesomeP4ssword!"

11630my4wesomeP4ssword!

Then do MD5 of that string:

05a9d022d621b64096160683f3afe804

When do you call a request, always use this token,

https://mywebservice.com/?token=05a9d022d621b64096160683f3afe804&op=getdata

This token is always unique everyday, so I guess this kind of protection is more than sufficient to always protect ur service.

Hope helps

:)

Solution 2 - Web Services

Any solution to this problem boils down to a shared secret. I also don't like the hard-coded user-name and password option but it does have the benefit of being quite simple. The client certificate is also good but is it really much different? There's a cert on the server and one on the client. It's main advantage is that it's harder to brute force. Hopefully you've got other protections in place to protect against that though.

I don't think your point A for the client certificate solution is difficult to resolve. You just use a branch. if (client side certificat) { check it } else { http basic auth } I'm no java expert and I've never worked with it to do client side certificates. However a quick Google leads us to this tutorial which looks right up your alley.

Despite all of this "what's best" discussion, let me just point out that there is another philosophy that says, "less code, less cleverness is better." (I personally hold this philosophy). The client certificate solution sounds like a lot of code.

I know you expressed questions about OAuth, but the OAuth2 proposal does include a solution to your problem called "bearer tokens" which must be used in conjunction with SSL. I think, for the sake of simplicity, I'd choose either the hard-coded user/pass (one per app so that they can be revoked individually) or the very similar bearer tokens.

Solution 3 - Web Services

There are several different approaches you can take.

  1. The RESTful purists will want you to use BASIC authentication, and send credentials on every request. Their rationale is that no one is storing any state.

  2. The client service could store a cookie, which maintains a session ID. I don't personally find this as offensive as some of the purists I hear from - it can be expensive to authenticate over and over again. It sounds like you're not too fond of this idea, though.

  3. From your description, it really sounds like you might be interested in OAuth2 My experience so far, from what I've seen, is that it's kind of confusing, and kind of bleeding edge. There are implementations out there, but they're few and far between. In Java, I understand that it has been integrated into Spring3's security modules. (Their tutorial is nicely written.) I've been waiting to see if there will be an extension in Restlet, but so far, although it's been proposed, and may be in the incubator, it's still not been fully incorporated.

Solution 4 - Web Services

I believe the approach:

  1. First request, client sends id/passcode
  2. Exchange id/pass for unique token
  3. Validate token on each subsequent request until it expires

is pretty standard, regardless of how you implement and other specific technical details.

If you really want to push the envelope, perhaps you could regard the client's https key in a temporarily invalid state until the credentials are validated, limit information if they never are, and grant access when they are validated, based again on expiration.

Hope this helps

Solution 5 - Web Services

As far as the client certificate approach goes, it would not be terribly difficult to implement while still allowing the users without client certificates in.

If you did in fact create your own self-signed Certification Authority, and issued client certs to each client service, you would have an easy way of authenticating those services.

Depending on the web server you are using, there should be a method to specify client authentication that will accept a client cert, but does not require one. For example, in Tomcat when specifying your https connector, you can set 'clientAuth=want', instead of 'true' or 'false'. You would then make sure to add your self signed CA certificate to your truststore (by default the cacerts file in the JRE you are using, unless you specified another file in your webserver configuration), so the only trusted certificates would be those issued off of your self signed CA.

On the server side, you would only allow access to the services you wish to protect if you are able to retrieve a client certificate from the request (not null), and passes any DN checks if you prefer any extra security. For the users without client certs, they would still be able to access your services, but will simply have no certificates present in the request.

In my opinion this is the most 'secure' way, but it certainly has its learning curve and overhead, so may not necessarily be the best solution for your needs.

Solution 6 - Web Services

5. Something else - there must be other solutions out there?

You're right, there is! And it is called JWT (JSON Web Tokens).

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA.

I highly recommend looking into JWTs. They're a much simpler solution to the problem when compared against alternative solutions.

https://jwt.io/introduction/

Solution 7 - Web Services

You can create Session on server and share sessionId in between client and server with each REST call.

  1. First authenticate REST request: /authenticate. Returns response (as per your client format) with sessionId: ABCDXXXXXXXXXXXXXX;

  2. Store this sessionId in Map with actual session. Map.put(sessionid, session) or use SessionListener to create and destroy keys for you;

    public void sessionCreated(HttpSessionEvent arg0) {
      // add session to a static Map 
    }
    
    public void sessionDestroyed(HttpSessionEvent arg0) {
      // Remove session from static map
    }
    
  3. Get sessionid with every REST call, like URL?jsessionid=ABCDXXXXXXXXXXXXXX (or other way);

  4. Retrive HttpSession from map using sessionId;

  5. Validate request for that session if session is active;

  6. Send back response or error message.

Solution 8 - Web Services

I would use have an application redirect a user to your site with an application id parameter, once the user approves the request generate a unique token that is used by the other app for authentication. This way the other applications are not handling user credentials and other applications can be added, removed and managed by users. Foursquare and a few other sites authenticate this way and its very easy to implement as the other application.

Solution 9 - Web Services

Besides authentication, I suggest you think about the big picture. Consider make your backend RESTful service without any authentication; then put some very simple authentication required middle layer service between the end user and the backend service.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTommiView Question on Stackoverflow
Solution 1 - Web ServiceskororoView Answer on Stackoverflow
Solution 2 - Web Servicesnewz2000View Answer on Stackoverflow
Solution 3 - Web ServicesjwismarView Answer on Stackoverflow
Solution 4 - Web ServicesDynrepsysView Answer on Stackoverflow
Solution 5 - Web Servicesbobz32View Answer on Stackoverflow
Solution 6 - Web Servicesjustin.hugheyView Answer on Stackoverflow
Solution 7 - Web ServicesarviaryaView Answer on Stackoverflow
Solution 8 - Web ServicesDevin MView Answer on Stackoverflow
Solution 9 - Web ServicesDagangView Answer on Stackoverflow