Important guidelines for configuring JWT authentication

This section describes important guidelines for setting up the JWT Authenticator.

Overview

When configuring an application to authenticate to Conjur using a JWT, you configure a JWT Authenticator endpoint and define an app ID in Conjur for the application authenticating to Conjur. You create a 1:1 relationship between the application authenticating to Conjur and its app ID.

Because of the variability of the JWT structure and its claims, you may want to create a JWT Authenticator endpoint that is unique for each Conjur app ID.

We recommend basing JWT authentication on identifying claims in the JWT such as the name, the user, and/or the application's identity in the JWT. For more information, see Define relationship in policy.

If authentication cannot be based on identifying claims in the JWT, we recommend defining an app ID in Conjur as uniquely as possible so that Conjur can properly distinguish between valid and invalid app IDs.

In the following sections, we explore the various options for defining a relationship between the application and its app ID in Conjur.

JWT Authenticator policy variables

The JWT Authenticator is defined in a Conjur policy. In this policy, you define several variables based on the requirements of the endpoint being defined. After loading the policy to Conjur, you populate the policy variables with relevant values.

Use the following diagram to understand the relationships between the JWT Authenticator policy variables:

Signing key provider variables

The following variables define the signing key provider.

The policy must include either the jwks-uri or public-keys variable (not both).

Signing key provider

Description

jwks-uri

The resource for a set of JSON-encoded public keys, one of which corresponds to the key used to digitally sign the JWT. The keys must be encoded as a JWK Set (RFC7517).

Value: A JWKS URI, beginning with https://

JWKS URI examples:

  • Google - https://www.googleapis.com/oauth2/v3/certs

  • Microsoft - https://login.microsoftonline.com/common/discovery/v2.0/keys

  • GitLab - https://gitlab.com/-/jwks

public-keys

When Conjur is unable to reach a remote JWKS URI endpoint, you can use this variable to provide a static JWKS to the JWT Authenticator.

When using this variable, it is assumed that the JWT includes an iss claim, and you must also define the issuer variable (see optional variables below).

Value: A JSON object (jwks.json) containing two fields: type and value, where type is always set to jwks (string value), and value is set to the JWKS value defined by the JWT provider.

{
  "type": "jwks",
  "value": <JWKS VALUE AS DEFINED BY THE IDENTITY PROVIDER>
}

Optional variables

These variables are optional, unless otherwise stated.

Variable

Description

ca-cert

When the JWKS provider server uses a self-signed certificate or the certificate is signed by third-party CA, use this variable to establish a TLS connection and validate server identity.

Certificates from this variable (can be one or many certificates) override default operating system CA certificate bundles when fetching the JWKS from the remote URI.

Value: The X.509 public key certificate or certificate bundle. Each certificate in the bundle should be in PEM (RFC7468) format.

Required: No

token-app-property

Use token-app-property to create a 1:1 mapping between the JWT Authenticator and the authenticating application.

You populate token-app-property with the name of a claim in the JWT, for example "app-id".

When you define an app ID for the authenticating application, its host id must match the JWT claim's value.

For example, say the JWT has the following claim: "app-id": "app530"

  1. You populate the token-app-property with app-id

  2. You define the app ID as follows:

    - !host app530.

The JWT Authenticator uses this mapping to validate the identity of the authenticating application.

  • You can populate this variable with one claim only.

    For nested claims, specify the path to the nested claim: <claim_root>/<claim_name>

  • Claims names that contain a slash (/) or a colon (:) are not supported.

Required: No, but highly recommended

identity-path

Further to defining the token-app-property variable, you can use the identity-path variable to define the app ID's path in Conjur, thereby completing the full app ID name.

The JWT Authenticator narrows validation of the authenticating application by the app ID, and in the defined identity path.

identity-path has no purpose when standing alone. It should be used only if token-app-property is defined.

Required: No

issuer

JWT Authenticator validates the issuer of the JWT.

The issuer variable value must be identical to the iss claim value.

Required:

  • When signing keys are static, that is, when the public-keys variable (see signing key provider variables) is defined in the policy, this variable is required

  • When the sign keys come from the JWKS URI, this variable is optional. In this case, if you do not define this variable, the issuer is taken from the jwks-uri.

enforced-claims

You can define a list of enforced claims in the JWT Authenticator.

All Conjur app IDs that work with the JWT Authenticator endpoint must include these enforced claims in their host annotations.

When this variable is defined in the JWT Authenticator, it must be populated with at least one claim. Separate multiple claims with commas:

custom_claim1,custom_claim2.

For nested claims, specify the path to the nested claim: <claim_root>/<claim_name>

 
  • You cannot enforce reserved claims (iss, exp, nbf, iat, aud, jti).

  • When a claim is enforced, application identities that do not include this claim as a host annotation will no longer be able to authenticate to Conjur and must be deleted and redefined..

Required: No

claim-aliases

Claim names in JWTs do not always represent their essence. For example ref might actually indicate a namespace ID, or a branch.

You can map claim names to more user-friendly aliases in your app ID's host annotations, instead of the actual claim name.

Following on from the example above, you can map ref to namespace, and then use namespace in the host annotation. So the host annotation would look as follows:

authn-jwt/<service-id>/namespace: theNamespace

Because of this mapping, the JWT Authenticator knows to check the JWT's ref value.

When this variable is defined in the JWT Authenticator, it must be populated with at least one mapping statement, in the following format:

<target-annotation-name>:<source-claim-name>

For nested claims, specify the path to the nested source claim:

<target-annotation-name>:<source_claim_root>/<source_claim_name>

Separate multiple mapping statements with commas:

<target-annotation-name_1>:<source-claim-name_1>,<target-annotation-name_2>:<source-claim-name_2>

 
  • You cannot map reserved claims to aliases (iss, exp, nbf, iat, aud, jti).

  • Application identities (hosts) that were defined prior to mapping the claim to an alias, and whose host annotations include the mapped claim, must be redefined to use alias.

Required: No

audience

If the JWT has an audience claim (aud), you can include this variable to authenticate the aud claim. The audience variable accepts a single value and must match the aud claim value.

If the aud claim contains an array, the array must include the audience variable value.

Required: No

Define relationship in policy

 

We recommend using this option for production environments.

To create a 1:1 relationship between the application and a concrete Conjur app ID, you create mappings between the JWT claims and the Conjur app ID. This is achieved by defining variables in the JWT Authenticator policy.

We recommend using as many variables in the JWT Authenticator policy as possible to limit the number of entities that can authenticate to Conjur.

In particular, we highly recommend defining the token-app-property variable.

For example, let's say that the JWT includes the project-id, namespace-id, and app-id claims, representing a project-id/namespace-id/app-id hierarchy:

 
"project-id":12345
"namespace-id":98765
"app-id":"app-1"

When you configure the JWT Authenticator policy, you could associate the token-app-property variable with any of these claims. It is important to note that in a hierachy situation, the using each of these claims in the token-app-property variable will have the following effects:

Claim

Effects on identification

project-id

12345 is used for the name of the app ID. All namespaces and apps in project 12345 are represented by the 12345 app ID, and in turn will have access to the same secrets as the project itself

namespace-id

98765 is used for the name of the app ID. All apps in namespace 98765 are represented by the 98765 app ID, and in turn will have access to the same secrets as the namespace itself

app-id

Defines the most specific identity level. Each application needing acces to secrets in Conjur requires its only app ID. Each app ID can have its own set of permissions, and will have access to its own set of secrets.

At the same time, there might be an app-1 application in multiple namespaces, and in multiple projects. To prevent app IDs from impersonating each other, narrow down the app ID definition as much as you can by including as many defining details about the application as possible/required, including claims from the JWT. This can be achieved by defining annotations in the app ID policy.

For example, you could define the app ID as follows:

- !host
  id: app-1
  annotations:
    authn-jwt/<service-id>/namespace-id: 98765
    authn-jwt/<service-id>/project-id: 12345

Send app ID path in authentication request

Applications can authenticate to Conjur by sending the app ID path directly in the URL of an authentication request.

For example:

curl -k --request POST 'https://myorg.example.com/authn-jwt/myVendor/cucumber/host%2Fjwt-apps%2Fmyapp/authenticate' --header 'Content-Type: application/x-www-form-urlencoded' --header "Accept-Encoding: base64" --data-urlencode 'jwt=eyJ0e......jjjkl'

For details, see JWT Authenticator.

  • For production environments, make sure the app ID path is as unique as possible.

  • We strongly recommend rather using the token-app-property variable method to authenticate applications to Conjur, as described in Define relationship in policy above.

Limitations

  • If your JWT provider revokes or changes a signing key used to sign a JWT, and Conjur gets a JWT that was signed with old signing key, this JWT will still be validated since Conjur has the old signing key in its memory.

    When Conjur gets a token signed by the new signing key, the signing key is replaced in Conjur's memory.

    To actively rotate the signing keys in Conjur's memory, restart all Conjur Servers (Leader/Followers) where JWT authentication is enabled.

  • Conjur accepts tokens signed by static signing keys presented in the public-keys variable. If the static signing keys change, you must manually populate the public-keys value with the new keys. Until this update is made, Conjur continues to accept tokens signed by the original static signing keys.