Distributed Systems – Authentication Options for Distributed Systems

authenticationauthorizationoauth2

I am in the process of designing 3 components that will work in symphony with one another:

  • A RESTful web service which requires BasicAuth over HTTPS on all calls, and which is what actually does all the heavy lifting for my system (does the work)
  • A web UI that translates end user actions into API calls to the above-mentioned web service; hence the UI is "backed by" the WS
  • A command-line interface (CLI) tool that developers can install and run locally, that also translates commands into API calls to the WS (hence it too is "backed by" the WS)

One of the first hurdles I'm trying to cross is with respect to authentication and authorization.

Let's pretend that the WS uses a LDAP/directory service (like AD or perhaps Apache DS) as its authentication realm. Meaning, when an API call come in over the wire (say, an HTTPS GET for some resource), the BasicAuth credentials are extracted from the request, and forwarded on to the LDAP service to determine whether this is a valid user or not. If they are authenticated, then let's say that a separate authorization realm, perhaps a database, is used for determining whether or not the identified user can do what they are attempting in the HTTPS request. So far, so good.

In the case of the CLI tool, the user will have to authenticate prior to running any commands, and so this model works just fine, as a single user will only ever be operating the same CLI instance at a given time.

The problem comes when we try integrating the web app (UI) with the WS, because many people could be logged on to the app at the same time, all with different permissions dictating which underlying API calls they're allowed to make.

As far as I see it, it looks like I have but 4 options here:

  • Cached Credentials: After logging into the app, the credentials are somehow, somewhere cached (such that the app has access to them), and the app doesn't enforce any kind of authorization policy itself. When users attempt to do things that generate API calls under the hood, their credentials are looked up from the cache, and forwarded on with the API calls. If the WS determines they aren't authorized, it sends back an error.
  • Service-Level Accounts: The app and the WS both use the same authentication/authorization realms, except the web UI now enforces authorization on what users can actually see and do inside the app. If they're allowed to do something that generates an underlying API call, the app sends service account credentials (e.g. myapp-admin-user) with each API call on the user's behalf.
  • OAuthv2: I have no idea what OAuth is or if its applicable for this scenario, but feel it may be a solution here somehow.
  • Token Servers: Use a token server such as CAS or maybe Kerberos to vouch for users, in a similar way as the Service-Level Account option behaves. Here, when a user logs into the app successfully, the token server sends the app back a session UUID, and also registers that UUID with the WS. Every time the app generates an API call, it tacks the UUID on to the request, which is then validates on the WS side.

The "Cached Credentials" option just feels like an aberration of everything that is good and wholesome in security-land. It just feels wrong to cache credentials anywhere, ever.

The "Token Server" option seems valid for an SSO type setup, but not in this particular case and feels awkward to me. I also think there's no good way to use the session UUID concept and BasicAuth/HTTPS at the same time.

So this leaves OAuthv2, which I know nothing about, and "Service-Level Account (SLA)*" as the only remaining options. The SLA option seems OK, but has a few following drawbacks:

  • It requires the service account to basically have "god privileges" over the WS. In other words, once the app deems a user is allowed to click a button or do something in the UI, that translates to an unconditional API call by the service account being used by the UI. This just feels bad, mkay?
  • It occurs to me that maintaining two sets of permissions (the permission set for each user of the app, and then the permission set for the service account used by the app against the WS) can result with the permissions getting out of synch with each other somehow

So it appears that I don't really have any good options here. Surely I can't be the first dev to run into this, but asking the Google Gods have not helped me much here. Any ideas?

Best Answer

There are many reasons not to use basic authentication scheme to protect Web API services.

In order to use the service, the client needs to keep the password somewhere in clear text to send it along with each request.

The verification of a password should be very slow (to counter brute force attacks), which would hamper scalability of your service. On the other hand, security token validation can be quick (digital signature verification).

OAuth2 does offer solutions for each of your use cases. Your web application can use the code grant, which gives it an access token it can use to talk to your API.

Your web application will redirect the user's browser to the authorization server. It will prompt the user for credentials (or smart card, or two-factor auth code) and return a code to the browser, which the client (your web application) can use to get an access token from the authorization server.

Your application will also get back a refresh token with which it can get a new access token if the current token expires.

Your CLI application can use the resource owner credentials grant. You will prompt the user for credentials and submit them to the authorization server to acquire an access and refresh token. Once your CLI application has the token, you can discard the user's password in memory.

Both clients (web app and command-line client) need to be registered up front with the authorization server.

Your authorization server may well talk to a LDAP/directory service (identity provider or IdP) to do the actual authentication.

Your web API service only needs to verify the incoming JWT token and establish what the user is allowed to do (authorization).

If you are the victim of a man-in-the-middle attack and you lose your access token, the attacker only has a limited time (token lifetime) to use it. A password is typically valid for much longer. Refresh tokens can be revoked in case they get lost.

Related Topic