Step 3 - Protected pages
Protecting pages
Now that we have the FusionAuth and the example app running, let’s take a look at how the application protects pages that require a user to be logged in.
This process is also known as access control. It is the process of controlling the users that have access to specific pages and functions of an application. The first step is to protect a page such that only users that have logged in can view it.
Open the file src/index.mts
and find the route named /account
. It should look something like this:
app.get("/account", async (req, res) => {
if (!await sdk.userHasAccess(req, res, ["admin", "user"])) {
res.redirect(302, '/login');
return;
}
res.sendFile(path.join(__dirname, '../templates/account.html'));
});
The first check is where the application determines if the user is logged in or not. This is done by calling the SDK function userHasAccess
(you can ignore the last parameter, which we will cover in the next section). This function returns true if the user has access, which implicitly verifies that they are logged in. Let’s take a look at this function.
Open the file src/sdk.ts
and find the function named userHasAccess
. One note is that this application uses jose
which is a Node library used for validating and parsing JSON Web Tokens (JWTs). If you want to learn more about jose
, you can visit the project at Github here: https://github.com/panva/jose
The userHasAccess
method begins by loading the user from the request. This is accomplished by calling the getUser
function of the SDK, which returns the user’s access token. If the access token comes back null
, that means the user is not logged in and this function returns false.
async userHasAccess(req: Request, res: Response, roles: Array<string>): Promise<boolean> {
const jwt = await this.getUser(req, res);
// @ts-ignore
if (!jwt || !jwt.roles || jwt.roles.length === 0) {
return false;
}
// @ts-ignore
return jwt.roles.some(role => roles.includes(role));
}
Let’s take a quick peek at the getUser
function of the SDK. This function looks like this:
async getUser(req: Request, res: Response): Promise<JWTPayload> {
let accessToken = req.cookies[this.configuration.accessTokenCookieName];
if (!accessToken) {
return null;
}
let payload: JWTPayload;
try {
payload = (await jose.jwtVerify(accessToken, this.JWKS, {
issuer: this.configuration.oauthIssuer,
audience: this.configuration.applicationId,
})).payload;
} catch (e) {
payload = await this.handleJWTException(req, res, e);
}
return payload;
}
The first part of this function loads a cookie from the request that contains the user’s access token. The access token is part of the OAuth specification, and is generated after the user successfully logs in. FusionAuth uses JWTs for access tokens.
The second part of this function verifies that the JWT is valid using jose
’s jwtVerify
function. This function validates the cryptographic signature of the JWT and also ensures that the JWT is not expired and all of the claims are valid.
We won’t go into detail about JWTs and all of the claims here, but you can read our OAuth token documentation to learn more.
If the getUser
function returns null
, this means that the user is not logged in. The user is then redirected to the login page.
You can also take a look at the /make-change
route and you’ll find that it uses this same approach to ensure that the user is logged in.
In the next step, we’ll expand on this and cover role-based access controls.
Next steps
< Go back to step 2 - Login button Ready for the next step? Step 4 - Role-based access controls >