-2

I'm trying to expose RESTful API endpoints for creating user accounts to access my API. So, this would be one of the first things my consumers would use. I'm trying to figure out if this is how I should implement the endpoints, so any feedback / guidance (particularly in complying with REST principles) is highly appreciated.

My current thoughts:

POST /users with parameters name, email, and password to register.

PUT /users/{id} with parameters name, email, and/or password to update user account.

DEL /users/{id} to delete account.

I think the above 3 endpoints handles the create, update, and delete part of the user accounts. Now, I'm trying to figure out how to "log in" (essentially, the user needs to submit email and password to get an access_token) and "log out" (where the `access_token" is nullified).

LOG IN

Calls to various other endpoints require Bearer access token. In order to get that access token, I was considering:

GET /users with parameters email and password. The endpoint would return name, email, access_token. For subsequent calls, I'm unsure if I should change the value of access_token.

GET /users/{id} with same parameters as GET /users. This means the {id} in the path is ignored and useless. This feels weird.

LOG OUT

PUT /users/{id} with parameter access_token and the value null. Essentially, this would nullify the access token, and any further use of the access token in other endpoints would result in a 401 Unauthorized.

UPDATE

If someone can produce an OpenAPI document showing how this might be implemented, maybe that'll be easier to understand?

6

It's easier to handle when you have different APIs for different matters.

Authentication

POST /auth or POST /login doesn't matter. Use the one you like most for authentication. As for the response, the token you mentioned is OK.

GET /users with parameters email and password. The endpoint would return name, email, access_token.

Since we are speaking about REST, we'll assume the API to be stateless. So, clients send users' credentials and the server respond with the auth token (200 OK) or redirects the client to a new address to follow up with the auth protocol (302 redirect).

For subsequent calls, I'm unsure if I should change the value of access_token.

Not necessarily. Tokens can live forever ( although it's not recommended), so that they can be reused over and over. However, the longer they live, the longer is the API exposed to threats, hence vulnerable. The common agreement is then to implement short-living tokens. In 30 seconds or 40 days is up to you, but they should expire eventually. When they expire, clients must ask the server for a new one. A sort of re-login.

Note that, this introduces a new feature: the token life cycle management.

My advice is, don't reinvent the wheel. There's a lot written about the subject. A good site to start with is OWASP: REST Security

Logout

POST /logout. The bearer token is already communicating what system user is involved and what token must be invalidated (expired). This service can be idempotent, doesn't matter if the token was already expired or it doesn't exist, the client only needs to know that it went ok (200 OK)

Users and accounts

For simplicity, don't mix security management and users management in a single interface.

For example:

  • /users
  • /accounts

Each endpoint manages its resource (User and Account respectively).

There's indeed a 1-to-1 relationship between one user and one account. We could say that both are the same thing but observed from different standpoints. When we look at the system user from the security standpoint, we "see" Accounts (privileges, credentials, tokens, permissions, etc). When we look at the very same system user, from the person who is it, we "see" a human being with personal attributes (name, surname, age, etc). In both cases, both things are essentially the same. A system user.

This said

  • /users handles the user data and this sort of stuff
  • /accounts handles the user credentials, grants, roles or/and availability.

The reason for us to do this is segregation of concerns. This way we can attend security without compromising the user' management and vice-versa. It's also easier to reason about each subject when we don't mix them up.

| improve this answer | |
  • Is it RESTful to be using verbs as the endpoints? – ProgrammerNewbie Sep 21 at 8:08
  • 1
    RESTfulness has nothing to be with the grammar of the endpoints. The "rule" of not using verbs is addressed to don't transfer any meaning to the URIs others can get attached to. URIs are meaningless. In other words, you don't want others inspecting your URIs in order to know what to do. The action is informed via the HTTP method. However, you will find that these HTTP methods fall short as the API growth in uses cases and once there, simplicity is way more important than any the facto rule we don't fully grasp. Overall if it's, somewhat, preventing us from getting things done. – Laiv Sep 21 at 8:22
  • I don't have a concept of "users" and "accounts" in my API. Do I really need to separate these two? – ProgrammerNewbie Sep 21 at 8:41
  • You can mix everything in a single API, but then the "weird" cases like the one detected in your login and logout will keep appearing. The solution will be segmenting the code with "!if/else" blocks addressed to detect which use cases is handling the endpoint. Or even worse, implementing weird URIs with very little difference among them and doing precisely what you were asking in the first comment. – Laiv Sep 21 at 8:47
  • 1
    When you log in, you effectively create a new session linked to a specific user, identified by a token. /user only models static user data, while /accounts (or /auth, or whatever) models those (active) session tokens. Login and Logout translate to "create session" and "destroy session" respectively, so those actions should fall neatly in your /accounts endpoint. This is true even if you don't use actual sessions on the server side, as it's primarily an abstraction. – Duroth Sep 24 at 7:57
1

I propose the following:

  • Have a dedicated /login endpoint. GET /users is more appropriate for obtaining a list of all registered users of your service; GET /users/{id} is more appropriate for obtaining the (public) profile of that user.
  • Use POST to obtain an access token, because the login action should not be idempotent or cacheable (both of which a GET would imply). A user should get a new token for any login action so that they can log-in from multiple devices independently; unless that's explicitly forbidden of course in which case they still would get a new token on every login, the old token would just be invalidated at the same time.
  • Similarly: Use POST /logout instead. PUT also implies idempotence, i.e. you should get the same response with the same input when you do it twice. Logout in principle can be idempotent, but only if you can distinguish "this is an old token that once was valid, but is now logged out" from "this is a token that never was valid; someone is trying funny business". The former can get the same successful response every time /logout is accessed, but the latter should always get a 401.
| improve this answer | |
  • Is it RESTful to be using verbs as the endpoints? – ProgrammerNewbie Sep 21 at 8:11
  • Maybe in my context, GET /users is to obtain a list of all users the API caller has access to, which will always be one (the caller's user resource). Wouldn't that be a semantic definition of GET /users? – ProgrammerNewbie Sep 21 at 8:12
  • Isn't POST to create a new resource? What new resource is being created when the user logs in, or logs out? – ProgrammerNewbie Sep 21 at 8:14
  • 1
    POST can be used for creating new resources, but that's not the only way POST is used in REST APIs. Most of the other verbs ought be to be used in idempotent operations, so POST is often the catch-all verb for every action that's non-idempotent. – Johannes Hahn Sep 21 at 10:45
  • And no, simply using different HTTP verbs on some URL does not make the API RESTful. At least because a single resource isn't nearly enough to represent any even moderately complex API. You'll basically always use multiple different resources in the same API, some of which will use many different verbs, some will use only a few, some may even use a single one. – Johannes Hahn Sep 21 at 10:50
0

If you expect relying parties to use a browser based OAuth login procedure, it is not particularly difficult to add user creation to the login workflow: the RP doesn't need to know who it is logging in as until the exchange is complete and it has and it has an access token. This gives you flexibility for expansion in the future without breaking RPs:

  • change what information you collect when users register.
  • 2FA support
  • CAPTCHAs on account creation/login.

If the RP needs additional information about the user, a simple option is to offer a fixed GET endpoint that will return information about the user identified by the access token used to authenticate the request. A PUT or PATCH request on the same endpoint could be used to update the user's profile.

If you need a way to logout a the current user, I would again suggest using a fixed endpoint that uses the access token to identify which user to expire the tokens of. Adding a user ID to the URI just complicates things.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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