An API driven Backend for Frontend (BFF)
for SPAs, in line with best practices for browser based apps.
A modern evolution of Backend for Frontend is used, called the Token Handler Pattern:
- The API implements OAuth work and calling the Authorization Server on behalf of the SPA
- This includes sending of OAuth requests, supplying secrets, then receiving tokens and storing them securely
- Only
SameSite=strict
cookies are issued to the SPA, so that recommended browser security is used
See the Curity OAuth for Web Home Page for further documentation.
The API has the role of an OAuth Agent
, and performs the intricate OAuth and cookie handling work:
See also the following resources:
- The Example SPA, which acts as a client to this API.
- The Example Gateway Plugin, which has the
OAuth Proxy
role.
Node and the Express framework are used to build the API, enabling you to deploy it to a host of your choice.
The API handles token responses from an Authorization Server, then saves encrypted tokens in http-only cookies.
The API is therefore stateless and easy to manage, and does not require a database.
The SPA can then use secure cookies to call business APIs, or to get userinfo from this API.
The API exposes the following endpoints to the SPA:
- POST
/login/start
- POST
/login/end
- GET
/userInfo
- POST
/logout
- POST
/refresh
This endpoint is used to initialize an authorization request. The API responds with a URL which the SPA should navigate to in order to start the authorization flow at the Authorization Server. The URL returned can contain query parameters or be a JAR or PAR URL. However, the format of the URL is irrelevant to the SPA, it should just redirect the user to that URL.
The API responds with a JSON containing the authorizationRequestUrl
field.
POST https://bff.example.com/login/start
Response:
{
"authorizationRequestUrl": "https://idsvr.example.com/oauth/authorize?client_id=bff_client&response_type=code&scope=openid%20read&redirect_uri=https://www.example.com/"
}
This endpoint should be be called by the SPA on any page load. The SPA sends the current URL to the API, which can either finish the authorization flow (if it was a response from the Authorization Server), or inform the SPA whether the user is logged in or not (basing on the presence of BFF cookies).
POST https://bff.example.com/login/end
pageUrl=http://www.example.com?code=abcdef&state=qwerty
The response will contain a few Set-Cookie
headers.
Endpoint which returns claims of the ID token contained in the session cookie.
GET https://bff.example.com
Cookie: myBFFSess=2558e7806c0523fd96d105...
Response
{
"exp":1626263589,
"nbf":1626259989,
"jti":"34e76304-0bc3-46ee-bc70-e21685eb5282",
"iss":"https://idsvr.example.com/oauth",
"aud":"bff-client",
"sub":"user",
"auth_time":1626259937,
"iat":1626259989
}
This endpoint can be called to get a logout URL. The SPA should navigate the user to that URL in order to perform a logout in the Authorization Server. The API also sets empty session cookies in the response.
This endpoint can be called to force the API to refresh the access token. If the API is able to perform the refresh new cookies will be set in the response (which is a 204 response), otherwise the API will respond with a 401 response (e.g. when the refresh token is expired) to inform the SPA that a new login is required.