JWT Authenticator

The JWT authentication is a generic, secure method for applications running on various platforms to authenticate to Conjur using a unique identity token or a third-party machine identity signed by a JWT provider.

Applications are represented in Conjur as application identities, or app IDs. For JWT authentication, the Conjur app ID can be established with varying granularity, allowing for a collection of resources to be identified to Conjur as one, or for individual workloads to be uniquely identified.

 
  • The JWT authentication assumes your application works with a JWT, and that the JWT is signed.

  • Because of the variability of JWT structure and claims, it is likely you will need to create a JWT Authenticator endpoint that is unique for each Conjur app ID.

 

For a use-case scenario with GitLab, see A use-case: Conjur-GitLab Integration with JWT Authentication.

How it works

  1. An application requests an identity token from a JWT provider.

  2. The JWT provider sends a JWT back to the application.

  3. The application sends an authentication request to Conjur using the JWT Authenticator REST API.

  4. Conjur fetches a public key from the JWT provider.

  5. Conjur attempts to authenticate and authorize the request. If successful, Conjur sends a short-lived access token back to the application.

  6. The application can use the Conjur token to retrieve secrets stored in Conjur.

JSON Web Token (JWT)

A JWT is provided by a JWT provider, and MUST be signed.

  • The following algorithms are supported by the JWT Authenticator.

    Signing method Algorithm

    RSA

    • RS256 - RSA using SHA-256 hash algorithm

    • RS384 - RSA using SHA-384 hash algorithm

    • RS512 - RSA using SHA-512 hash algorithm

    The algorithm is defined in the JWT's header.

  • The JWT must have a valid expiration date (exp claim). If there is no exp claim, or if it is invalid, the JWT authentication fails.

  • If any of the iat, nbf, or iss claims exists in the JWT, it must be valid, otherwise the JWT authentication fails.

  • Nested claims and arrays are currently not supported.

Important guidelines for configuring JWT authentication

When configuring JWT authentication, you want to configure the JWT Authenticator and define a Conjur app ID for the application in such a way that a 1:1 relationship exists between the application that needs to authenticate to Conjur and the app ID in Conjur. Because of the variability of JWT structure and claims, it is likely you will need to create a JWT Authenticator endpoint that is unique for each Conjur app ID. This is a bit different from other Conjur authenticators that can authenticate multiple app IDs.

We recommend basing JWT authentication on identifying claims in the JWT, such as the name, the user, the application's identity in the JWT, and so on, as described below in Define relationship in policy.

Where this is not possible, we recommend defining a Conjur app ID as uniquely as possible so that Conjur can properly distinguish between valid and invalid app IDs.

Let's explore the various options for defining a relationship between the application and Conjur.

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 define variables in the JWT Authenticator policy that map JWT claims to the Conjur app ID. We recommend using as many variables in the JWT Authenticator to control the number of entities that authenticate to Conjur, especially the token-app-property variable.

For example, let's say the JWT includes the following claims:

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

When you configure the JWT Authenticator, you can associate the token-app-property variable with any of these claims. The effect would be as follows:

Claim

Level of authorization

project-id

Enables all entities and all namespaces in project 12345 to authenticate to Conjur

namespace-id

Enables entities in project 12345's namespace 98765 to authenticate to Conjur

app-id

Enables only those entities whose app-id = app-1 to authenticate to Conjur

To narrow down the 1:1 relationship further, you include the identity-path variable, which is the Conjur policy path to the app ID (host) definition in Conjur policy. For example, if the policy with your host definition is:

 
- !policy
  id: jwt-apps
  body:
  - !host my-host

and that policy is loaded at root, the identity-path would be /jwt-apps, to direct the JWT Authenticator to the correct Conjur app ID (host) definition.

In addition, it is always recommended to include in the app ID policy as many defining details as possible/required about the application, including claims from the JWT. This can be achieved using annotations in the Conjur app ID policy.

Send app ID in authentication request

When you define the authentication, you define a Conjur app ID to represent the application in Conjur. Instead of creating a 1:1 relationship in policy, as described in Define relationship in policy, you can send the app ID path directly in the authentication request.

For production environments, you would need to make sure the app ID path is as unique as possible.

Configure JWT authentication

 

This task is performed by the Conjur admin.

This section describes how to set up a JWT Authenticator in Conjur so that your application can use a JWT to authenticate to Conjur.

 

It must be noted that there are many ways to define the JWT authentication, as described in Important guidelines for configuring JWT authentication above. The example JWT authentication configured in this section uses the token-app-property variable.

Step 1: Before you begin

  • This section assumes your application uses a JWT for authentication, and that the JWT is signed. For details, see JSON Web Token (JWT).

  • Make sure the time on the Conjur machine and the JWT provider time are synced. You can use the secured NTP service to synchronize the times.

  • Important! Make sure you fully understand the Important guidelines for configuring JWT authentication.

  • The examples in this section use the following information:

    • App ID path: host/jwt-apps

    • JWT Provider: https://vendorHost.example.com

    • Example JWT

       
      {
        "ver": "2.0",
        "iss": "https://login.example.com",
        "sub": "AAAAAAAAAAAAAAAIkzqFVrSaSaFHy782bbtaQ",
        "aud": ["service1","service2","conjur"],
        "exp": 1537361411,
        "iat": 1537274711,
        "nbf": 1537274711,
        "workspace": "production_emp",
        "group_name": "mygroup",
        "group_id": "group21",
        "team_name": "myteam",
        "team_id": "team76",
        "app_name": "myapp",
        "app_id": "app530",
        "oid": "00000000-0000-0000-66f3-3332eca7ea91",
        "tid": "9122040d-6c67-4c5b-b112-36a304b66dad",
        "nonce": "123456",
        "client_id": "s6BhgRkqt3",
        "state": "af0ifjslrkj",
        "aio": "Df2UXL1ix!lv8q5!eGbIDaky5mnOreY"
      }
  • Decide which claims in the JWT you want to use to set up the JWT authentication.

    Based on the example JWT above, let's use the workspace, group_id, and app_name claims to define the authentication:

     
    {
      "ver": "2.0",
      "iss": "https://login.example.com",
      "sub": "AAAAAAAAAAAAAAAIkzqFVrSaSaFHy782bbtaQ",
      "aud": ["service1","service2","conjur"],
      "exp": 1537361411,
      "iat": 1537274711,
      "nbf": 1537274711,
      "workspace": "production_emp",
      "group_name": "mygroup",
      "group_id": "group21",
      "team_name": "myteam",
      "team_id": "team76",
      "app_name": "myapp",
      "app_id": "app530",
      "oid": "00000000-0000-0000-66f3-3332eca7ea91",
      "tid": "9122040d-6c67-4c5b-b112-36a304b66dad",
      "nonce": "123456",
      "client_id": "s6BhgRkqt3",
      "state": "af0ifjslrkj",
      "aio": "Df2UXL1ix!lv8q5!eGbIDaky5mnOreY"
    }

Step 2: Configure JWT authentication

This step describes how to configure JWT authentication in Conjur

Step 3: Allowlist the JWT Authenticator

In this step, you add the JWT Authenticator to the list of allowed authenticators in Conjur.

Set the CONJUR_AUTHENTICATORS variable as an environment variable: CONJUR_AUTHENTICATORS=<authenticator-name>

 

The value for this variable must be identical to the name given to the JWT Authenticator policy ID above, excluding the conjur/ prefix.

For example:

 
CONJUR_AUTHENTICATORS=authn-jwt/myVendor

Send a JWT authentication request

Use the following URI to send a JWT authentication request to Conjur:

 
POST https://<Conjur-server-hostname>/authn-jwt/<service-id>/<account>/[<host-id>]/authenticate

For example, using the following details:

Parameter

Example

Conjur-server-hostname

myorg.example.com

service-id

myVendor

account

cucumber

host-id

Following the example JWT authentication we configured above, which uses the token-app-property to create a 1:1 relationship between the JWT Authenticator and app ID (host), we do not include the host-id in the request.

the resulting request looks as follows:

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

For more details about sending authentication requests, see the JWT Authenticator REST API.

JWT Authenticator modifications

Certain modifications to a JWT Authenticator's configuration require updating all the application identities associated with the modified JWT Authenticator.

This section describes these JWT Authenticator modifications and the required app ID updates.

 

Application identities that are not updated accordingly cannot be used to the authenticate to Conjur due to mismatched configurations.

Claim mappings

If you add or modify the mapping-claims variable definition in the JWT Authenticator, application identities that include this claim mapping in its annotations must be modified accordingly.

Take, for example, the JWT Authenticator defined above in Configure JWT authentication.

Say you add the mapping-claims variable to the JWT Authenticator policy, and populate it as follows:

 
conjur variable set -i conjur/authn-jwt/myVendor/mapping-claims -v environment: workspace
 
conjur variable values add conjur/authn-jwt/myVendor/mapping-claims environment: workspace

In the app ID (see Define an app ID (host)), the authn-jwt/myVendor/workspace: production_emp annotation must be adjusted accordingly to authn-jwt/myVendor/environment: production_emp:

 
- !policy
  id: jwt-apps
  body:
    - !group

    - &hosts
        - !host
            id: myapp
            annotations:
                authn-jwt/myVendor/environment: production_emp
                authn-jwt/myVendor/group_id: group21

    - !grant 
        role: !group
        members: *hosts

- !grant
    role: !group conjur/authn-jwt/myVendor/apps
    member: !group jwt-apps

Enforced claims

If you add or modify the enforced-claims variable definition in the JWT Authenticator, application identities that do not include this enforced claim in its annotations must be modified to include it.

Take, for example, the JWT Authenticator defined above in Configure JWT authentication.

Say you want to enforce the team_id claim in the JWT Authenticator policy. Modify (repopulate) the enforced-claims variable as follows:

 
conjur variable set -i conjur/authn-jwt/myVendor/enforced-claims -v group_id, team_id
 
conjur variable values add conjur/authn-jwt/myVendor/enforced-claims group_id,team_id

The app ID (see Define an app ID (host)) must now include annotations for both the group_id and team_id claims:

 
- !policy
  id: jwt-apps
  body:
    - !group

    - &hosts
        - !host
            id: myapp
            annotations:
                authn-jwt/myVendor/environment: production_emp
                authn-jwt/myVendor/group_id: group21
                authn-jwt/myVendor/team_id: team76

    - !grant
        role: !group
        members: *hosts

- !grant
    role: !group conjur/authn-jwt/myVendor/apps
    member: !group jwt-apps

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 (Master/Followers) where JWT authentication is enabled.

Troubleshooting