This is the third part of my Authentication and security for REST API in the context of Web Apps series of posts. In the previous post, we discussed how to store and provide tokens to the server. In this post, we will try to design an API to authenticate users and manage tokens.
As mentioned in the introduction, the focus will be on a JSON-LD, Hydra and JWT-based implementation. However, this should be easily transposable to any REST implementation using an alternative for some or all of them.
Unfortunately, not all the Linked-Data vocabulary we will need is
already available. We will, sometimes, have to define our own vocabulary
terms. In these cases, we will use http://ld.lemoinem.name/ns/rest-auth# as
the base IRI for these new terms and
rest-auth: as their corresponding prefix.
These IRIs are not, currently, populated with linked-data description. In
particular, they are not HTTP dereferenceable, as of today. However, we intend
to provide such documents once the vocabulary has stabilized and is clear
Since the HATEOAS principle’s goal is discoverability, it is a given
that the login endpoint/operation IRI MUST be included in the public API
descriptions (such as a
referenced by a
The endpoint SHOULD be documented as a
@type appropriate for
a JWE JWT. This
hydra:Resource, or whatever is
used to represent the authentication endpoint in the API documentation, SHOULD
be given a
wrds:describedby property with
This endpoint’s supported operations MUST include:
POSToperations on the resource itself, used to create a new explicitly authenticated token,
GEToperation, on the resource itself, used to refresh a token.
Since tokens are intended to be opaque and stateless with respect to the server,
DELETE operations MUST NOT be supported. Token are
credentials and, therefore, intrinsically sensitive resources. They require
specific cache handling, which MUST be enforced by the server and respected by
any cache proxy:
Cache-controlHeader with the
Cache-controlHeader with either:
max-agedirective with a value less than the the token remaining lifetime,
Cache-controlHeader with the
VaryHeader including “Authentication” (or any other HTTP Header used to provide a token to the server) and “Cookie”.
This restrictions are intended as a strict minimum. If an implementation chooses not to follow these recommendations, it MUST provide a stricter cache policy.
The restriction on the
Vary header is mostly important for unauthenticated or
anonymous resources. This one aside, the same restrictions SHOULD be applied on
any response including an access-restricted authenticated-clients-only resource.
GET operation (i.e. presented without a token or with an
expired token) on the token resource endpoint MUST return an anonymously
An anonymously authenticated token is a token with a NULL
sub claim. All other
relevant claims MUST be filled as with any other token. In particular,
jti are always relevant claims and MUST be filled for all
tokens. For reference, a
NULL claim MAY be removed from the token.
GET operation on the token resource endpoint, whose
authentication token has reached half its lifetime MUST send a renewed
token. The renewed token MUST have identical
iss claims. The
renewed token MUST have a level of authentication identical to or lower than the
original one (for reference, an explicitly authenticated token SHOULD be renewed
as a remember me token). This renewed token MUST have
rest-auth:use-cookie claim equivalent to the old
one. This renewed token MUST have a different
jti claim than the old one.
This renewed token’s
jti claim SHOULD be different from any other
included in any previously issued token (and in particular, for the same
user). A cached token revalidation by the server MUST be consistent with this
POST operation on the token resource endpoint MUST provide enough
information to start a credentials validation process. Once the process is
completed successfully, a fresh explicitly authenticated token MUST be returned
in the appropriate format (and transit mode). The response including this new
token SHOULD include a
Content-Location header containing the IRI of the token
endpoint itself. This MUST be considered a state modifying operation in the
context of CSRF protection to prevent Login CSRF attacks.
Once a user agent or a Web App has been through the authentication process and has been provided a new token, it SHOULD revalidate the hydra:ApiDocumentation resource as provided in the hydra:apiDocumentation link. This is to ensure API discoverability in the case where the Web App has been hiding its authenticated API.
POST operations on the token resources MUST support the following inputs
to achieve the following features. Each input SHOULD be tagged (in the API
documentations) with the appropriate value for
wrds:describedby property. These inputs MAY be
specified as query-string parameters.
rest-auth:use-cookie: This input’s goal is to generate a cookie stored token. This MUST NOT be used by an agent who support more RESTful appropriate token storage technology. The token generated by this authentication process MUST include an encrypted
rest-auth:use-cookieclaim set to
true. It MUST be provided to the user agent through a cookie including the
httpOnlyflag. This cookie SHOULD include the
secureflag as well. If the API supports non-HTTPS requests, which it SHOULD NOT, the
secureflag MAY be omitted.
rest-auth:remember-me: This input’s goal is to generate a long-term Remember Me token. The token generated by this authentication process MUST NOT have an higher authenticated level than the “Remember Me” level. The user agent SHOULD request a short-term token (which SHOULD be marked as explicitly authenticated) as soon as possible after receiving a long-term token.
Since the token based authentication is stateless for the server, no logout endpoint is required. Logging out of the API simply means dropping the token and starting anew.
In order to prevent CSRF vulnerabilities, the API SHOULD reject with a
“401 Unauthorized” response any unauthenticated (i.e. not including any token)
state modifying request. The
WWW-Authenticate header MUST include a
challenge whose realm parameter MAY be set to the IRI of the token
Most services nowadays do not offer state modifying request to anonymous users (an exception could be a completely open and free platform such as Wikipedia).
In particular, the API MUST NOT generate an automatically managed out-of-band (e.g., in a cookie) non-anonymous authentication token in response to an unauthenticated (i.e. not including any token) request.
It is very important to consider that some authentication schemes (e.g., the ones relying on a trusted third party) could come with their own vulnerabilities to login CSRF even for manually managed authentication tokens.
The risk of login CSRF is greatly reduced for Web App using a manually managed token. However, the API must protect Web App using cookies (or another automatically managed out-of-band method) by ensuring they won’t issue an unrequested authentication token.
WWW-Authenticate header is not the preferred method of discoverability
because it is very loosely defined. User agents SHOULD rely on HATEOAS
discoverability (e.g. using Hydra) rather than the
header until it is better integrated to the HATEOAS principle.
In the context of our API, a token is valid if:
nbfclaim, if any,
rest-auth:use-cookieclaim set to
trueMUST have been provided as an
httpOnlycookie. The cookie MUST have the
secureflag unless the API allows non-HTTPS requests.
rest-auth:use-cookieclaim or with such a claim set to
NULLMUST have been provided as a
Bearertoken through the
Additional properties of the token MAY be verified, such as CSRF
protection check or validating the credentials with the token’s issuer (when
there is a non-
In order to mitigate CSRF vulnerabilities, several checks SHOULD be done before accepting a token.
The protections against CSRF detailed here are based on the following assumptions:
“consistently publicize the origin” does not mean a user-agent must send a
Origin Header to the server. It has complete and total freedom on
this point. However, it MUST apply this choice consistently during a single
In particular, the API MUST include the user-agent’s
Origin as the value for
aud claim of any fresh token unless the user-agent’s
NULL. Renewed tokens MUST carry forward the
aud claim of their older token.
CSRF protection is provided by matching the the user-agent’s
with the the token’s
In order to protect browser user-agent, it is expected that user-agents not
requiring CSRF protection (such as a command-line API client), will not
Origin or will craft and consistently provide an appropriate
Origin that couldn’t be generated by a browser user-agent.
This is not a protection against token theft since, an attacker able of stealing
a token will usually be able to determine the token’s original user-agent’s
First, the server MUST determine the user-agent’s
Origin using this algorithm:
OriginHeader in its request, the value of this Header is the user-agent’s
RefereeHeader in its request and the value of this Header is an IRI, the
Origin’s components of its value (usually the scheme, host and port parts of the IRI).
Origin is the value of its
For security and privacy reasons, an API MAY wish to hide its discoverable API to unauthenticated user agents.
In this case, any unauthenticated or anonymously authenticated request to the
API MUST provide a
hydra:apiDocumentation link to a
hydra:ApiDocumentation. This unauthenticated
API Documentation MAY use the same IRI as the resource for the authenticated
one. In this case, the server MUST include a
Vary Header with “Authentication”
(or any other HTTP Header used to provide a token to the server) and “Cookie” in
This is to ensure caches will not be confused and the authenticated API remains discoverable easily.
If the authenticated and
hydra:ApiDocumentation’s IRIs are
different, the authenticated API Documentation MUST include at least the same
information as the unauthenticated one. Moreover, any response to an
authenticated request MUST reference the authenticated ApiDocumentation.
MAY be provided this way, each for a different level of authentication. However,
this might end up confusing both users and user agents. Therefore, API SHOULD
refrain from doing so.
In the next part, we will focus on adapting several user authentication schemes to this API.