0

I work for an ecommerce site and we are looking to expose much of our core functionality via a set of APIs. We plan on re-writing some of our own public facing websites (e.g. the main shop website) to call these new APIs also. Currently a lot of the core code is found directly in the main web app (making it difficult to provide this functionality to third parties).

I am looking at authentication options for the APIs we are going to write. JWT looks like a good fit, each API consumer (main website, some mobile app, some third-party) will request a token and then use that in requests to endpoints like /products, /offers etc. etc.

However, I am confused as to how this API can work with our public facing website's user accounts system - e.g. we have hundreds of end users who log in to the current website via their username and password that they have registered with.

How would authentication work for example, for an endpoint like users/1/orders. The JWT token would handle actual access to the API (for the consumer, such as an app or third-party) but how do we make sure the actual end user has authenticated? How should that information be passed into the main API? Would there be a 'login' endpoint that takes the username/pass in plain text? Would there be another token at this point to determine the user who has authenticated?

I don't understand how you track the fact the end user has logged in, giving the API consumer access to their 'orders' endpoint, for example.

Any advice much appreciated!

2 Answers 2

1

First off, I want to clear up that conceptually it's not the client application that asks for the token for itself - it's the user which requests the token for themselves, and the application only manages it.

So, for example, if you have a web application as your client, then the JavaScript code will take care of getting the client's credentials from a login form, handing them off to the API, receiving a token from it and saving it in cookies or other sort of local storage. But that token will have the user's identity written into it, and will belong to that user, not to the application. The fact that 1000 other users are logging in at the same time does not matter - each of them gets a different token based on their credentials and keeps it in their own browser.

So your authentication flow would look something like this:

  • the user types in their login and password into the website and hits "Submit"
  • the JavaScript code running in the browser picks them up and sends a POST request to /login
  • the API returns a token for those credentials (or some sort of error if the authentication failed)
  • the JavaScript code saves that token in a global variable
  • when requesting GET /users/1/orders, the JavaScript code grabs the same token, puts it in the request's header, and sends it to the API
  • the API checks the token to see which user owns it, and either returns the data or an error

Things get just a little trickier if you're not using JavaScript to query the API directly from the client, but are dealing with eg. a legacy WebForms application which needs the data itself to then process it and render the view. In this case you have two options:

  • share a secret key between the WebForms application and the API and have the API unconditionally accept all requests with that secret key. The assumption is that you as a user have already logged into the WebForms app, so now it's that app's responsibility to not let you see the data you aren't allowed to see
  • have the WebForms application act essentially like the JS application above, forwarding your token to the API. This might be as simple as copying the authentication headers from your request and including it in the request to the API, or might require the application to authenticate to the API on your behalf and keep that token in memory under your name.

Also, in your question you mention JWT as a possible solution - personally I think it's a bit of an overkill. The main benefit of JWT is to enable a completely separate service to manage authentication and authorization, and have your API validate that the token (which is just a username, a set of permissions and a signature) was indeed issued by that service.

In your case this seems unnecessary - you don't need to verify your signature, you can just remember what you signed. For that, a simple random string stored in the database, associated with the user and his permissions and invalidated on logout should work just fine.

Additionally, a common pattern for publicly available APIs is to let users create persistent API keys and manage them - for example, if I were writing a bot for monitoring orders, I could create a key separate from the token I normally get when using the web application, assign it only the permission to read orders and set it to never expire. Then I can just make requests with that key without going through the login endpoint at all, merely saving it in the configuration file for my bot.

1
  • I would add that the token should be associated with a soft and hard expiry. A soft expiry would require the user to re-authenticate, but otherwise the session is still good (ie. shopping cart is remembered). The hard expiry would wipe the token out entirely so that not even the user's identity is associated with the random bit string.
    – Kain0_0
    Commented Dec 13, 2018 at 6:29
1

How would authentication work for example, for an endpoint like /users/1/orders. The JWT token would handle actual access to the API (for the consumers, such as an app or third-party) but how do we make sure the actual end user has authenticated?

Because the end-user have had to go through an authentication process. Such a process, among other things, gave a fresh JWT to the client (user). The token is the proof you need. Basically, because you trust in whatever issued that token. Usually, it's your own API who does it. And also it's who check it later.

Think of tokens as movie tickets. When you go to the cinema you buy a ticket and later the ticket is checked before going to the hall. The ticket (JWT) is your proof, it's what grants you access. Of course, in this example, your "authentication" was not a user/password, it was more like a credit card or cash.

How should that information be passed into the main API?

If we are to use HTTP as the communication protocol, it's convenient to send JWT as an HTTP header. More precisely

Authorization: Bearer my_jwt_token_here

The header must be sent in every single request that might require authentication and/or identification.

Would there be a 'login' endpoint that takes the username/pass in plain text?

There will be an endpoint that takes credentials in some format. They can be user/pass, email/pass, whatever you need. The format can be plain text, JSON, XML, ... Whatever you need.

Would there be another token at this point to determine the user who has authenticated?

There could be several tokens. As many as your API needs. Usually, JWT contains all the info (claims) required for identification and authorization. This makes one JWT be enough.

I don't understand how you track the fact the end user has logged in, giving the API consumer access to their 'orders' endpoint, for example.

Because in every request it's sending a valid JWT

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