Foal Framework 2.0: What’s New? | Hacker Noon

Loïc Poullain Hacker Noon profile picture

@loicpoullainLoïc Poullain

Fullstack developper. Creator of FoalTS.

This article presents some improvements introduced in version 2 of FoalTS:

  • the JWT utilities to manage secrets and RSA keys,
  • the JWT utilities to manage cookies,
  • and the new stateless CSRF protection.

JWT – Accessing config secrets and public/private keys

Starting from version 2, there is a standardized way to provide and retrieve JWT secrets and RSA public/private keys: the functions

getSecretOrPublicKey

and

getSecretOrPrivateKey

.

Using secrets

In this example, a base64-encoded secret is provided in the configuration.

JWT_SECRET="Ak0WcVcGuOoFuZ4oqF1tgqbW6dIAeSacIN6h7qEyJM8="
settings:
  jwt:
    secret: "env(JWT_SECRET)"
    secretEncoding: base64

Both

getSecretOrPublicKey

and

getSecretOrPrivateKey 

functions will return the secret.

In the case a

secretEncoding

value is provided, the functions return a buffer which is the secret decoded with the provided encoding.

Using public and private keys

const { Env } = require('@foal/core');
const { readFileSync } = require('fs');

module.exports = {
  settings: {
    jwt: {
      privateKey: Env.get('RSA_PRIVATE_KEY') || readFileSync('./id_rsa', 'utf8'),
      publicKey: Env.get('RSA_PUBLIC_KEY') || readFileSync('./id_rsa.pub', 'utf8'),
    }
  }
}

In this case,

getSecretOrPublicKey

and

getSecretOrPrivateKey

return the keys from the environment variables

RSA_PUBLIC_KEY

and

RSA_PRIVATE_KEY

if they are defined or from the files

id_rsa

and

id_rsa.pub

otherwise.

Managing JWT with cookies

In version 2, Foal provides two dedicated functions to manage JWT with cookies. Using these functions instead of manually setting the cookie has three benefits:

  • they include a CSRF protection (see section below),
  • the function
    setAuthCookie

    automatically sets the cookie expiration based on the token expiration,

  • and cookie options can be provided through the configuration.

api.controller.ts

import { JWTRequired } from '@foal/jwt';

@JWTRequired({ cookie: true })
export class ApiController {
  // ...
}

auth.controller.ts

export class AuthController {

  @Post('/login')
  async login(ctx: Context) {
    // ...

    const response = new HttpResponseNoContent();
    // Do not forget the "await" keyword.
    await setAuthCookie(response, token);
    return response;
  }

  @Post('/logout')
  logout(ctx: Context) {
    // ...

    const response = new HttpResponseNoContent();
    removeAuthCookie(response);
    return response;
  }

}

Configuration file

settings:
  jwt:
    cookie:
      name: mycookiename # Default: auth
      domain: example.com
      httpOnly: true # Warning: unlike session tokens, the httpOnly directive has no default value.
      path: /foo # Default: /
      sameSite: strict # Default: lax if settings.jwt.csrf.enabled is true.
      secure: true

Stateless CSRF protection simplified

In version 1, providing a CSRF protection was quite complex. We needed to provide another secret, generate a stateless token, manage the CSRF cookie (expiration, etc), use an additional hook, etc.

Starting from version 2, the CSRF protection is all managed by

@JWTRequired

,

setAuthCookie

and

removeAuthCookie

.

The only thing that you have to do it to enable it through the configuration:

settings:
  jwt:
    csrf:
      enabled: true

When it is enabled, an additional

XSRF-TOKEN

cookie is sent to the client at the same time as the auth cookie (containing your JWT). It contains a stateless CSRF token which is signed and has the same expiration date as your JWT.

When a request is made to the server, the

@JWTRequired

hooks expects you to include its value in the

XSRF-TOKEN

header.

Loïc Poullain Hacker Noon profile picture

Tags

Join Hacker Noon

Create your free account to unlock your custom reading experience.

read original article here