Send signed metadata with JWT

Last updated:

Important: The ability to install using signed metadata with JWT is a paid feature. Contact Pendo Support to request access.

With a typical Pendo installation, your application passes metadata to Pendo as a JSON object over SSL. Some industries require additional assurance that this metadata hasn't been tampered with in transit. Signed metadata adds a JSON Web Token (JWT), an open standard (RFC 7519), that verifies metadata originated from your application and wasn't modified before it reached Pendo.


JWT doesn't encrypt your metadata, it signs it. The signature confirms the message hasn't changed, but the contents are still readable by other parties. Standard SSL encryption handles confidentiality separately.
This feature adds complexity and can cause data loss if implemented incorrectly. It's unnecessary for most installations.

This article covers the process for installing Pendo using signed metadata with JWT. Broadly, this involves the following steps:

  1. Activate signed metadata with JWT
  2. Generate a key to sign your metadata
  3. Start sending signed metadata
  4. Verify that you're sending signed metadata
  5. Enforce signed metadata only

Prerequisites

You must have the following to install using signed metadata using JWT:

  • Signed metadata access for your subscription. This feature is available with all paid plans but is turned off by default. Contact Pendo Support to request access. The Advanced Security settings in your application's Install Settings page won't appear until access is turned on
  • Admin access to your Pendo subscription.
  • An engineering resource to modify your Pendo installation code, generate JSON web tokens, and send them. 

We recommend installing Pendo without signed metadata first to validate your setup before enabling JWT.

Step 1. Activate signed metadata with JWT

For your application to accept data with JWT, you must first activate Use signed metadata in your application's Install Settings page, under Advanced Security. If this setting isn't activated and you attempt to send signed metadata, all data from these sessions is dropped.

  1. Go to Settings > Subscription Settings.
  2. Open the Applications tab.
  3. Find and open the relevant app from your Applications list.
  4. Open the Install Settings tab.
  5. Select Enable under Advanced Security to accept the signed metadata.
    • For web implementation, this is found under Customize your snippet.

      EnableSM.png
    • For mobile implementation, this is found under Customize your SDK.

      Screen_Shot_2022-02-17_at_12.42.52.png

Note: The Advanced Security settings won't appear until Pendo Support has activated signed metadata for your subscription. For more information, see Prerequisites.

After you've turned on Use signed metadata, another option appears: Only allow signed metadata. This is grayed out until you generate at least one key to sign your metadata. After creating a key, don't activate Only allow signed metadata until you verify that you're sending events with signed metadata, as outlined in Step 4. Verify that you're sending events with signed metadata

Step 2. Generate a key to sign your metadata

JWT installation requires all requests to be signed using your JWT shared secret, otherwise data is dropped. 

After enabling signed metadata with JWT for your subscription, you need a key to sign your metadata. This key is different from the API key that appears in App Details > Basic settings. The API key connects Pendo to your application.

One active key is typically sufficient, but each application can have up to five active keys at a time.

  1. From Install Settings, go to the Active Keys table and select + Generate Key.
  2. Enter a meaningful description for your key.
  3. Select Create. The key is now available in the Active Keys table.

Keys are hidden by default. Select Show to see the key.

Step 3. Start sending signed metadata

Sending signed metadata to Pendo requires technical or engineering resource to create JWTs that hold your metadata and to send them to Pendo. If you haven't already, we recommend that you first install Pendo without metadata before continuing.

You mustn't create or sign JWTs directly in your application code. To keep the secret key safe, known only to you and Pendo, you must create the signed JWT on your server-side and pass it to your application, together with the signing-key name, before calling the initialize (for web) or startSession (for mobile) method.

Generate signed JWTs

There are many libraries you can use to generate and sign JWTs. When you generate signed JWTs required to start sessions with Pendo, there are a few considerations:

  • JWT must be signed with the generated key corresponding to the key ID. 
  • The JWT payload must contain both the visitor and account elements, each with an id property and value. 
  • Visitor and Account IDs must be passed as strings for JWT installs. To generate an anonymous visitor, use an empty string as the value of the id property of the visitor element. The account ID property may be set to an empty string as well. Additional visitor and account properties are optional.
  • A nonce property must be included in the claims. The nonce is a string randomly generated by the client and must be at the top level in the JSON as a sibling property to a visitor or account, not contained within one of those sections. The value isn't explicitly validated, but a value is required. Including a random nonce helps ensure that tokens always appear different even if they contain the same claims.
  • The HMAC SHA-256 algorithm should be used when signing the JWT. If the JWT isn't signed with this algorithm, Pendo can't verify the signature, and the events aren't processed.
  • Any additional configuration you'd like can be included inside the JWT metadata.
  • Historical metadata values must be included in every JWT.

See the following pseudocode for generating a JWT to start a visitor session with Pendo:

jwt = JWT.sign({

    visitor:{
        id:             'VISITOR-UNIQUE-ID' // Required if user is logged in; must be a string. Pass an empty string for an anonymous visitor.
        // email:       // Recommended if using Pendo Feedback, or NPS Email
        // full_name:   // Recommended if using Pendo Feedback
        // role:        // Optional
        // You can add any additional visitor level key-values here,
        // as long as it's not one of the above reserved names.
    },

    account: {
        id:           'ACCOUNT-UNIQUE-ID' // Required if using Pendo Feedback; must be a string. Pass an empty string for an anonymous visitor.
        // name:         // Optional
        // is_paying:    // Recommended if using Pendo Feedback
        // monthly_value:// Recommended if using Pendo Feedback
        // planLevel:    // Optional
        // planPrice:    // Optional
        // creationDate: // Optional
        // You can add any additional account level key-values here,
        // as long as it's not one of the above reserved names.
    },

    nonce: 'randomly generated value'  // It should be different every time it is used
},
'JWT-SECRET-KEY' // Generate this key below
)

Warning: There should be no signing-key value in the code of your application. The JWTs that you're creating shouldn't be created on the application. They should be sent to the application from your server. Your application should be collected from the server, where the JWT is already signed.

Signed metadata with anonymous visitors

Anonymous visitors are automatically assigned a random string as a Visitor ID when pendo.initialize() is called without metadata assigned to the Visitor ID. This method of generating anonymous Visitor IDs is the same for a conventional or signed metadata Pendo implementation.

With signed metadata, anonymous visitors still use JWT when they pass event data to Pendo.

Example of the payload used to initialize anonymous visitors with JWT:

let payload = {
     nonce: "abcdefg78910xyz",
     visitor: { // notice there is no "id" field
          otherVisitorField: "hi"
     },
     account: {
          otherAccountField: "hello world"
     }
}
const jwt = JWT.sign(payload, 'SECRET KEY');
pendo.initialize({
     jwt: jwt,
     signingKeyName: 'SIGNING KEY NAME'
})

Update your application installation code

To send signed metadata, your application must fetch your JWT and signing key name from your server and pass them on to Pendo when starting a session.

Web implementation

For a web implementation, you can send the JWT and key ID by including them in the Pendo install script for your application as part of the initialize method. The JSON object passed to initialize must have both jwt and signingKeyName properties.

Mobile implementation

For a mobile implementation, you can send the JWT and key ID by including them in your mobile SDK integration for your application as part of the startSession method. You must directly pass both jwt and signingKeyName properties to the startSession method.

Example of an Android mobile device sending the JWT to Pendo (Java):

Pendo.setup(this, pendoAppKey, null, null);

String jwt = “JWT-SIGNED-TOKEN”; // obtained from your server
String signingKeyName = “SECRET-KEY-ID” // obtained from your server
Pendo.jwt.startSession(jwt, signingKeyName);

Example of an iOS mobile device sending the JWT to Pendo (Swift):
 

PendoManager.shared().setup(pendoAppKey, nil);

let jwt = “JWT-SIGNED-TOKEN”; // obtained from your server
let signingKeyName = “SECRET-KEY-ID” // obtained from your server
PendoManager.shared().jwt.startSession(jwt, signingKeyName);

Step 4. Verify that you're sending signed metadata

The following steps require you to know which visitor you're testing with. Typically, this involves signing in as a particular visitor to your application.

  1. Go to Settings > Subscription Settings > Applications.
  2. Find and open your application.
  3. Open the Raw Events tab.
  4. Check for new data flowing into Pendo from the visitor that you're testing with.

Step 5. Enforce signed metadata only

When your install script is updated and transmitting signed metadata, you can enforce it to ensure that data from sessions without secure metadata is dropped going forward. Up until this step, only Use signed metadata is activated, which means that we accept metadata with or without JWT.

When you activate Only allow signed metadata, we only process events that have a verified signed JWT. To activate this setting:

  1. From your application settings, open the Install Settings tab.
  2. Activate Only allow signed metadata under Advanced Security.
    • For web implementation, this is found under Customize your snippet.
    • For mobile implementation, this is found under Customize your SDK.
  3. Confirm that you want to only allow signed metadata by reviewing the checklist and entering "I understand" before selecting Enable Signed Metadata.

This drops all events without signed metadata, as intended. After the signed JWT is generated and obtained by the client or mobile device, the JWT and key ID are sent to Pendo's servers. Pendo checks the JWT against the secret key, known only to your server and Pendo's server, to verify that it hasn't been tampered with. Pendo doesn't process any events that are sent with an invalid JWT. Dropped data can't be recovered.

Important: You could experience data loss if your public application is using Pendo without secure metadata or if mobile visitors haven't had time to update to an application version using JWTs and you attempt to enforce signed metadata using the instructions in this section.

Replay attacks 

Signed metadata using JWT doesn't support the “jti claim" (a unique identifier assigned to each JWT), typically used to prevent replay attacks. Instead, Pendo drops any duplicate events it receives, preventing malicious users from replaying the same event. Additionally, you can exchange and revoke the JWT secret key as a way of mitigating replay attacks

Exchange and revoke the JWT secret key

A signed metadata JWT remains valid as long as the signature is valid. Exchanging and revoking tokens on a regular basis is the best way to prevent long-lived JWTs from being exploited if compromised. This is relevant to replay attacks.

To exchange a previously used JWT secret key, generate a new key in the Install Settings page. For instructions, see Step 2. Generate a key to sign your metadata. You must start using the new key to sign your JWTs, replacing the key that you want to revoke. Before you can revoke the old key, you must update the key ID sent along to the JWT to the application. You can then revoke the old key.

Warning: If an active key is revoked before your JWT signing is updated with the new key, data isn't processed until the JWT sent to Pendo is signed with the new key.

To revoke a key, hover over it in the Active Keys table and select the Revoke Key icon, then accept the confirmation to permanently revoke the key. This can't be undone.

Screen_Shot_2020-04-06_at_3.09.51_PM.png

You can find a log of Revoked Keys underneath the Active Keys section. Screen_Shot_2020-04-06_at_3.02.58_PM.png

Turn off signed metadata

If you need to turn off signed metadata for your application, you can do so from the Install Settings page. Turning off signed metadata revokes all active keys and stops Pendo from processing events that include a signed JWT.

Warning: Turning off signed metadata causes data loss for any sessions currently sending signed JWTs. Dropped data can't be recovered. Before turning off, make sure your application is ready to send unsigned metadata.

  1. Go to Settings > Subscription Settings.
  2. Go to the Applications tab.
  3. Find and open the relevant app.
  4. Open the Install Settings tab.
  5. Under Advanced Security, turn off Use signed metadata.
  6. Confirm your selection.

All active keys are revoked immediately. Events sent with a signed JWT are no longer processed after this setting is turned off.

 

Was this article helpful?
4 out of 7 found this helpful