JWT Authorizer

The JWT Authorizer is used to authorize signature requests based on the provided JSON Web Token (JWT) included in the request.

AUTHTYPE=org.signserver.server.jwtauth.JwtAuthorizer

Introduction

JSON Web Token (JWT) is an internet standard for JSON-based token authentication. JWTs allow you to digitally sign information (referred to as claims) with a signature and can be verified at a later time with the public key of the issuer. JWT claims are typically used to pass the identity of authenticated users between an authorization server and a resource server. The JWT Authorizer allows having the authorization server separate from the SignServer application.

Use Case Example

There are several authorization servers available and this implementation has been tested with Keycloak. The following use case example outlines authenticating with Keycloak (as the authorization server) to obtain a signed token, then used in the request sent from the client to SignServer (the resource server). The client in the following overview could, for example, be an app using OpenID Connect (OIDC).

  1. Public key trusted: The worker in SignServer is configured to trust the authorization server's public key. Authorization rules matching claims from the tokens are also configured.
  2. Credentials: The client authenticates toward the authorization server using its credentials.
  3. Token: The authorization server creates and signs a token using its private key and returns it to the client.
  4. Request with token: The client sends its request to SignServer and includes the token.
  5. Response: SignServer verifies the token (including its signature and validity etcetera) and matches its claims against the configured authorization rules. If a rule matches, the request is processed and the response is returned to the client.

Configuration

Several trusted authorization servers can be set up using indexed worker properties starting with an AUTHSERVER prefix.

Currently, the algorithm types RSA and ECDSA are supported and public keys use RSA by default. To use ECDSA, set the key algorithm to EC or ECDSA (AUTHSERVERn.KEYALG=ECDSA). It is also allowed to explicitly set the key algorithm to RSA, but not needed as this is the default.

The issuer field needs to be matched to the value provided as the Issuer Claim (iss) in the tokens, according to the following example. For more information on Issuer Claim (iss), refer to RFC 7519, section 4.1.1. "iss" (Issuer) Claim.

  • AUTHSERVER1.ISSUER=<issuer>
  • AUTHSERVER1.PUBLICKEY=<base 64-encoded public key of issuer>
  • AUTHSERVERn.ISSUER=<issuer>
  • AUTHSERVERn.PUBLICKEY=<base 64-encoded public key of issuer>
  • AUTHSERVERn.KEYALG=ECDSA

Each issuer needs a matching rule for matching on the claims in the token:

  • AUTHJWT1.ISSUER=<issuer>
  • AUTHJWT1.CLAIM.NAME=groups
  • AUTHJWT1.CLAIM.VALUE=SignServer-users

This allows access to the request which provides a token of type (typ) JWT, with a valid (and not expired) signature that:

  • Can be verified by the public key configured with that issuer name/URI.
  • Contains a claim named "groups, that either has the value SignServer-users or contains a list of values including that value.

It is also possible to configure a list of accepted audiences that are checked against the intended audiences from the claim of the token (if present). If the token has an audience, it must match a configured audience. If the audiences do not match, the request is not authorized.

  • ACCEPTED_AUDIENCES=<comma-separated list of audience names>

Examples

The following displays an example JWT token:

Base 64-encoded

eyJraWQiOiJqd3Qua2V5IiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJ1c2VyMSIsInVwbiI6ImR1a2UiLCJhdXRoX3RpbWUiOjE1ODM4MzAwMzcsImlzcyI6Im15LWF1dGgtc2VydmVyIiwiZ3JvdXBzIjpbInN0YWZmIiwiU2lnblNlcnZlci11c2VycyIsInJlbGVhc2UtbWFuYWdlcnMiLCJtYWlsdXNlcnMiXSwiZXhwIjoxNTgzODMxMDM3LCJpYXQiOjE1ODM4MzAwMzcsImp0aSI6IjQyIn0.Tzy6hoLKwmiQ4C7exBaEUVjH_TK6qiY6KUJUu2QLC-52QxJRXKdBXR1t6l2JqbhWm20o_yKcgp6d4n03AyX8IUGOVul5xY5nWP4Uyn_SfWznuANCXKIf9y8a99ucO4yTEtsrAw2Hiv88LSpia768m1epUXe8_fgoFxfZr8adtRkJ2mT5evHtFwbWtUTT2r3-okuQPvmUfhBrECVKYrBwrV3JlXgXGTjSz4j3XwFfdh516EhKXY8dSn4PMG4hmcnmLNJkz59sUOSTgpwgtp8JzqGBLqtsehJGSYVFDueIDCEbljEAXNgfIbUpT7PuE1IY8VyTm792RB_u_Dq5f03TEQ

The following displays the header and payload for the above token:

Header

{
 "kid":"jwt.key",
 "typ":"JWT",
 "alg":"RS256"
}

Payload

{
 "sub":"user1",
 "upn":"duke",
 "auth_time":1583830037,
 "iss":"my-auth-server",
 "groups": [
    "staff",
    "SignServer-users",
    "release-managers",
    "mailusers"
  ],
 "exp":1583831037,
 "iat":1583830037,
 "jti":"42"
}

The following displays an example (using curl) that sends a request authenticating using JWT:

$ curl -H "Authorization: Bearer eyJraWQiOiJqd3Qua2V5IiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJ1c2VyMSIsInVwbiI6ImR1a2UiLCJhdXRoX3RpbWUiOjE1ODM4MzAwMzcsImlzcyI6Im15LWF1dGgtc2VydmVyIiwiZ3JvdXBzIjpbInN0YWZmIiwiU2lnblNlcnZlci11c2VycyIsInJlbGVhc2UtbWFuYWdlcnMiLCJtYWlsdXNlcnMiXSwiZXhwIjoxNTgzODMxMDM3LCJpYXQiOjE1ODM4MzAwMzcsImp0aSI6IjQyIn0.Tzy6hoLKwmiQ4C7exBaEUVjH_TK6qiY6KUJUu2QLC-52QxJRXKdBXR1t6l2JqbhWm20o_yKcgp6d4n03AyX8IUGOVul5xY5nWP4Uyn_SfWznuANCXKIf9y8a99ucO4yTEtsrAw2Hiv88LSpia768m1epUXe8_fgoFxfZr8adtRkJ2mT5evHtFwbWtUTT2r3-okuQPvmUfhBrECVKYrBwrV3JlXgXGTjSz4j3XwFfdh516EhKXY8dSn4PMG4hmcnmLNJkz59sUOSTgpwgtp8JzqGBLqtsehJGSYVFDueIDCEbljEAXNgfIbUpT7PuE1IY8VyTm792RB_u_Dq5f03TEQ" --data "data=<document/>" https://sign.example.com/signserver/worker/XMLSigner

Logging

If the request is allowed, the provided subject claim (sub) is logged in the AUTHORIZED_USERNAME field.