Skip to main content

External auth with JWT

Instead of using the built-in authentication and authorization mechanisms, you can generate your own JWT tokens with the desired permissions for your already authenticated users and pass them to Windmill. This way, you control what permissions your users have without having to create them in Windmill.

We use the RS256 algorithm for the JWT tokens, so you will need to generate a public and private key pair. You public key in PEM format has to be passed to Windmill in the JWT_EXT_PUBLIC_KEY environment variable to all servers and workers. Then for each user, you will generate a JWT token and pass it to Windmill. You will have to prefix the token with jwt_ext_ to let Windmill know that this is an external token.

The payload has to contain the following fields:

  • username: the username of the user (for logs)
  • email: the email of the user (for logs)
  • is_admin: a boolean indicating if the user is an admin
  • is_operator: a boolean indicating if the user is an operator (run-only)
  • workspace_id: the workspace id
  • groups: an array of strings containing the groups the user belongs to, it should usually always contain all (group of all users)
  • folders: an array of arrays containing 3 values: the name of the folder, a boolean indicating whether the user can write to the folder and a boolean indicating whether the user is the owner of the folder (can manage folder permissions)

While you don't need to create the user on Windmill, you should create the workspaces, folders, and groups with the appropriate permissions in the Windmill UI.

Here's an example TypeScript code that generates the JWT token with the required payload and token prefix:

import { createSigner } from 'fast-jwt'

const YOUR_PRIVATE_KEY = `-----BEGIN RSA PRIVATE KEY-----...-----END RSA PRIVATE KEY-----`

async function generateJWT(
username: string,
email: string,
is_admin= false,
is_operator= false,
workspace_id: string,
groups: string[] = ["all"],
folders: [string, boolean, boolean][] = [],
) {
const signer = createSigner({
algorithm: 'RS256',
key: YOUR_PRIVATE_KEY,
expiresIn: '1h'
})

const token = await signer({
username,
email,
is_admin,
is_operator,
groups,
folders,
workspace_id
})

return "jwt_ext_" + token
}