RESTful APIs are a great way to build reliable and decoupled server/client protocols. Nearly all application require some type of login and/or session capabilities and there are numerous systems that allow users to share logins across multiple systems. Therefore, building a RESTful authentication system which can handle third party authorizations and allow a different third party access to the API is highly desirable. However, a recent search of the web and questions on StackOverflow did not provide a modern, standard, RESTful solution. Hopefully, with feedback, this outline can cover the basics of a modern, RESTful, login system.
In todays modern, connected world with multiple systems sharing login credentials and information it is sometimes difficult to discern which system has a particular responsibility. An example of building a Todo App will be used to augment the discussion. This process will involve the following systems:
- MyTodoApp is the name of the actual application used by the user. It may be implemented in iOS, Andriod, a JS SPA, an in vehicle proprietary system, and/or something else. The MyTodoApp developers are concerned with building a great user interface and providing new ways to work with todo lists. There are not interested in new ways of storing or drawing conclusions from stored todo lists. As a side effect of not doing “big data” things, MyTodoApp also does not want to focus on keeping track of users.
- BigCorpTodoServer will be the system implementing the RESTful, JWT based, login process. BigCorp is a data science shop and wants to find better ways for todo list to predict market trends, find new ways for people to share and be social with todos, and other data focused research operations. Therefore, BigCorp has build the BigCorpTodosServer. The server may be implemented in ASP.NET MVC API, RoR, Nitrogen, and/or something else. It stores todo lists from different people and provides todo list services. Unfortunately for BigCorp, few users have the BigCorp flagship social media application installed on their devices. Therefore, BigCorp has to support a variety of third party authentication methods.
- SocialSystemsInc supports third party authentication and claims via OAuth. They are the current leader in social media and their extensive collection of users enjoy the convenience of having a single sign-on via the SocialSystemsInc API.
The most critical component of a RESTful API is the resources. The resources have to accurately, and individually, represent the nouns in the story. A single resource representing two nouns works if Jack and Jill go up the hill but if there is a future version of the story where Jack stays and Jill goes there will be volatility in the resource definitions. Therefore, for the login system resources will be created for the session, user, authorization, and credentials. Please note that to the best of my knowledge there is no true standard for this process and therefore no standard resources types.
SessionResourcerepresents a relationship between the application and the server. The session does not represent the actual device, application, or person. Also important to note is that the
SessionResourcedoes not represent a relationship between the server and a person using an application. The session relationship begins when an application is first launched on a device or after a new install of the application or after a logout has occurred. The session ends when the application is uninstalled or the user logs out. The
SessionResourceis defined, in JSON for example purposes, as follows:
The session resource has a unique id which servers to identify this particular session resource. The session ID should be generated on the client. However, responsibility for security is a server concern. Therefore, it should be obvious this is not, and need not be a “secure UUID.” Further to this point is awareness that the session ID is used anyplace a reference to this session needs to be created and therefore may be stored in numerous locations. Ultimately, the takeaway is that no security measure should rely on this ID being kept secret and there is no onus to keep this ID secret.
As stated and obviously, the responsibility for a servers security and any details of the security implementation are the responsibility of the server. While the client does have to know on some level how the server wishes to authenticate the session, the more knowledge required by the client the greater the coupling between the server and the client. Therefore, this shared knowledge is kept to a minimum. For this implementation, the client will need to know that the server wants to authenticate the session based on an HTTP header. The
auth-headerproperty specifies which header to use for authentication and to what value the aforementioned header is set.
The session determines to what resources the client has access. To expand the client’s nexus of accessible resources the session will need additional endorsements. The system has little understanding of the possible endorsements that may come in the future. Therefore, this component is loosely coupled and will be parsed with a dictionary approach. The type of endorsement is the part that is dictionary matched. The value is then used in a domain specific manner post the type match. The authority identifies who is providing the endorsement. The signature is proof that the aforementioned authority is providing said endorsement.
UserResourcerepresents the user as they are known to BigCorpTodosServer. The UserResource has to be able to tie multiple sessions together and extends the list of resources to which a session has access. Since the user ties multiple sessions together it is also the keeper of how sessions are permitted to connected to the user. Finally, the UserResource also stores information about the user. It is important to note that the UserResource does not store any authentication information.
UserResourceis defined, in JSON for example purposes, as follows:
Other than the ID the first property of the UserResource is the metadata. This is just information about the user that may be of interest to the client. This data is stored in a key value pair format because it is volatile and therefore should be parsed with a dictionary as to avoid volatility in the resource and the accommodate a variety of client usages.
metadata-sourcesproperty allows other systems to store and modify user data. The
UserResourcethen becomes the aggregator of the user metadata. The order in
metadata-sourcesmay be pertinent since sources listed higher in the order would have priority items listed lower in the list if providing the same metadata. The metadata link is also dictionary parsed, with the knowledge that the token is the secure component, due to the potential variety of alternate sources and user metadata.
Authorizations simply provides a list of accounts on authorization providers that are allowed to connect a session to a user. This is highly structured data since it need only identify the provider and user within the provider. All the other complexity associated with the relationship is stored with the provider.
The credentials resource is the challenge information provided to the authorization server. The
CredentialsResourceis defined, in JSON for example purposes, as follows:
Given there are a vast array of credential types, it is most logical to use a credentials resource that is dictionary parsed. The supported / provided challenge types are listed under
authorityproperty is concerned with providing information that allows other systems know that credentials were accepted.
AuthorizationResourceconnects a session to its associated claims by optionally connecting credentials to sessions. The
AuthorizationResourceis defined, in JSON for example purposes, as follows:
Given a valid value for the
authorityproperty (expectantly derived from a valid credentials resource), the
tokensproperty contains a valid token which states the
claimsare valid. This token is signed by the authorization server which does not have to be the credentials server.
Now let’s examine the steps to create a user and login to the system.
1.) After the user first opens MyTodoApp, the app needs to create a session on the server.
1.a) Immediately, in the background the application generates an ID to represent its session, connects to the server, and performs a POST to
http://api.mytodoapp.com/Sessions with the follow body:
Please note this session ID was generated on the client and may not be and does not have to be a “secure UUID.” The session ID is used anyplace a reference to this session needs to be created and therefore may be stored in numerous locations.
1.b) The server replies with the same resource and populates some additional values:
auth-header tells the client what HTTP header value to set for future communications. This information is will be used for securely identifying the session and should be treated appropriately.
Likely, the session create is the only permitted activity without a session id. With the session header value set, the device is capable of interacting with more services on the server. However, the
user-id value is not set. This indicates that no login has occurred or, put differently, there is no relationship between a person and the server for this session. However, certain functionality of the application may require the server to have a relationship with the user, not just the device.
2.) A prerequisite for connecting the session to a user is that a user must exist in the system. A user could already exist on the BigCorpTodosServer in which case all of step two can be skipped. If user creation is required, two common user creation workflows are illustrated here.
2.a.1) The user opens MyTodoApp and choses to create an account using SocialSystemsInc. This either opens the SocialSystemsInc app or SocialSystemsInc website to perform an OAuth2 authentication. The result is an unique id for the user in the SocialSystemsInc system and a token for claims to the users data stored with SocialSystemsInc.
The MyTodoApp then creates a new user on the BigCorpTodosServer by POST with the following body:
2.a.2) The BigCorpTodosServer uses the claim provided in the resource to connect to SocialSystemsInc and populates the remainder of the user information.
2.b) The user is created directly on the BigCorpTodosServer.
2.b.1) The user visits the BigCorpTodosServer website and POSTs the following form:
which creates an
AuthorizationResource on BigCorpTodosServer.
2.b.2) The user then posts a form with required information and a
UserResource is created on BigCorpTodosServer. Hidden in this form as a linked account is the connection to the
AuthorizationResource created earlier.
Since this creates an account directly with BigCorpTodosServer, future verification will not include SocialSystemsInc.
2.c) The user creates an account with BigCorpTodosServer directly from MyTodoApp. This is done in three steps. First, credentials need to be created on the authorization server.
2.c.1) The app fetches the create
CredentialsResource from the BigCorpTodos authorization server and populates it.
This step is not necessary and only needs to be performed once. It is useful for identifying how the server wants passwords to be hashed.
2.c.2) The app creates (POSTs) the
CredentialsResource on BigCorpTodos authorization server.
2.c.3) The app POSTs the create user resource with a link to the authorization.
There is not a linked account in the metadata so the metadata must be populated directly. The account is created directly with BigCorpTodosServer so future verification will not include SocialSystemsInc.
3.) The client obtains a JWT token for the session. This could be done in may ways. Two common approaches are outline here.
3.a.1) The client opens the SocialSystemsInc app to perform an OAuth2 authentication. The SocialSystemsInc app returns a token which is valid for claims to SocialSystemsInc data about the user.
3.a.2) The client then creates an authorization resource on the server using the token from SocialSystemsInc.
3.a.3) The BigCorpTodosServer then uses the claim to connect to SocialSystemsInc and validate the claim and fetch the necessary information to complete the response (see step 4).
Alternative workflow using an account previously created directly on BigCorpTodosServer:
3.b.1) MyTodoApp prompts the user to enter their username and password and then performs a query for an authentication object associated with the provided username:
Note that the query is necessary to fetch the appropriate salt for that username. For security, specifically anti-phishing practices, it may be best for the server to always reply even if a valid username does not exist.
3.b.2) MyTodoApp then computes the appropriate password hash, populates the session id field, and performs a PUT to update the auth resource:
4) In both flows, the BigCorpTodosServer server deletes the credentials field from the resource and populates the
auth-header field with the JWT and the appropriate HTTP header name, populates the
user-id field with the ID of the authenticated user, populates the expires field, and the resource is returned.
Now the client only needs to connect the session to the user id by updating the
SessionResource via a PUT:
The BigCorpTodosServer then uses the JWT sent in the
token HTTP header to validate that the session is authorized to be connected to the specified user. The serve also ensures that the account that signed the JWT is a linked account for the user.