Skip to content

v0.30.0

Choose a tag to compare

@gonzalogarciajaubert gonzalogarciajaubert released this 11 Jul 13:37
· 813 commits to main since this release

New TokenVerifer interface and default implementations

Now, it is possible for users to provide custom implementations as well as use the framework's implementations with sensible defaults. The TokenVerifierConfig interface has been replaced by a new TokenVerifier interface:

interface TokenVerifier {
  /** Returns an UserEnvelope object if the token is valid. Raises an exception if not valid */
  verify(token: string): Promise<UserEnvelope> 
}

Booster now provides two TokenVerifier implementations with sensible defaults that check a token against a jwksUri or a public key. These implementations can be instantiated and provided to the tokenVerifiers config option as follows:

import { Booster } from '@boostercloud/framework-core'
import { BoosterConfig } from '@boostercloud/framework-types'

Booster.configure('production', (config: BoosterConfig): void => {
  config.appName = 'demoapp'
  config.providerPackage = '@boostercloud/framework-provider-aws'
  config.tokenVerifiers = [
    new JwskUriTokenVerifier(
      'https://securetoken.google.com/demoapp',
      'https://demoapp.firebase.com/.well-known/jwks.json',
      'firebase:groups'
    ),
    new PublicKeyTokenVerifier(
      'custom-key-pair',
      getCustomKey(),
      'custom:roles'
    )
  ]
})

With this design, you can easily create a new class implementing a fully custom TokenVerifier implementation from scratch or reuse one of the default verifier classes by extending one of the provided classes.

Token utilities

You can now use any library to parse and validate the tokens in any way you want, like using the jwt node package in your own way, but Booster also exposes the following functions as building blocks if you want to build a custom implementation but want to reuse Booster's sensible defaults:

/**
 * Initializes a jwksRSA client that can be used to get the public key of a JWKS URI using the
 * `getKeyWithClient` function.
 */
export function getJwksClient(jwksUri: string) {
  ...
}

/**
 * Initializes a function that can be used to get the public key from a JWKS URI with the signature
 * required by the `verifyJWT` function. You can create a client using the `getJwksClient` function.
 */
export function getKeyWithClient(
  client: jwksRSA.JwksClient,
  header: jwt.JwtHeader,
  callback: jwt.SigningKeyCallback
): void {
  ...
}

/**
 * Verifies a JWT token using a key or key resolver function and returns a Booster UserEnvelope.
 */
export async function verifyJWT(
  token: string,
  issuer: string,
  key: jwt.Secret | jwt.GetPublicKeyOrSecret,
  rolesClaim?: string
): Promise<UserEnvelope> {
 ...
}

New Authorizer functions

Now you can set an authorizer function in any command's or read model's authorize decorator parameter or in an event's authorizeReadEvent decorator parameter to perform custom authorization:

@Command({
  authorize: async (currentUser) => {
    if (!currentUser.claims['permissions'].includes('Permission-To-Rock')) {
      throw new Error(`User ${currentUser.username} should not be rocking!`)
    }
  }
})
export class PerformIncredibleGuitarSolo {
  ...
}

See the documentation changes for more details on how this feature can be used in "Read models" and entities event streams.

What's Changed

  • Token Verifiers and Authorizers Refactor to allow custom logic by @tpellegrin in #1083

Full Changelog: v0.29.7...v0.30.0