SPA
The basic premise of this JWT authentication pattern is that the front-end authenticates via a GraphQL API with a valid JWT on each request.
Attributes of this pattern include:
A valid JWT will act as the currency for an authenticated user.
The first step is to authenticate the user and issue a valid JWT. This step is required in both browser and non-browser contexts.
Now that the client has a valid JWT, it will need to be sent with each request to the GraphQL API.
Storing JWTs in a SPA is notoriously tricky. Most of the options present a security risk as the entire JWT ends up being available to JavaScript. As such, retrieval of the JWT might be possible with common exploits such as cross-site scripting.
To avoid these scenarios, we'll split the JWT content ({header}.{payload}.{signature}) across two cookies.
The first cookie will contain the {header}.{payload} content from the JWT with the following attributes:
The second cookie will contain the {signature} content from the JWT with the following attributes:
Splitting the JWT across two cookies provides the following benefits:
payload portion of the JWT.signature cookie is HTTPOnly so it is not available to JavaScript and helps to protect against XSS.SameSite Lax cookies will provide some protection against CSRF.Secure cookies will help to mitigate man-in-the-middle attacks.Each request to the GraphQL API will need to be authenticated with a valid JWT.
Browsers will supply the JWT via two cookies, and API clients will provide the JWT via an Authorization header.
The GraphQL API will need to respond to all requests, regardless if they're coming from a browser or not.
In the context of an API client, an Authorization header containing a JWT will be supplied.
In the context of the browser, the server will be responsible for creating an Authorization header by reconstructing a JWT from the content within the two cookies. Constructing the Authorization header will ensure that all future processing of the request is identical for browser-based and non-browser-based applications alike.
Each request should be inspected for cookies and if found, their content to be used in reconstructing the JWT and creating anAuthorization header.
While using cookies to store the JWT (rather than storing it in local storage or session storage) protects applications from some type of attacks, it makes them vulnerable to others, including CSRF.
Using the Same Origin Policy (SOP), we can mitigate CSRF by requiring that a custom header exists if supplying the JWT via cookies. This is described as a CSRF prevention technique on OWASP.
Processing of the cookies should only take place if a custom header with the correct information is supplied.
The GraphQL API should require a valid JWT in the Authorization header.
When validating the JWT, ensuring the following are checked:
At this point, if the JWT validated and the user roles have access to the GraphQL API queries being requested, continue processing the request using your GraphQL framework.