Transitioning to JWT-Based authentication for better security

A technical guide for migrating user authentication from HMAC-based identity verification to JWT tokens, covering implementation steps, security benefits, and code examples for enhanced user session protection.

Written By Bruno from Featurebase

Last updated 4 months ago

Why Migrate from Identity Verification to JWTs?

Featurebase's traditional Identity Verification uses HMAC-SHA256 hashes to verify user authenticity. While this prevents basic impersonation attacks, JWTs offer significant improvements:

Enhanced Security Features

  • Protect all attributes: Current solution provides limited control over data security, only the customer identifier is verified (e.g., user email/id or company id), while other attributes (e.g., customer plan) can not be verified. With JWTs, you can verify every attribute needed.

  • Time-based validity: Unlike static user hashes, JWT tokens automatically expire after a specified duration

  • Protection against replay attacks: Time-limited tokens reduce vulnerability windows

Operational Benefits

  • Selective data protection: Specify which user attributes require secure transmission

  • Better scalability: More suitable for distributed systems and modern architectures

  • Session management flexibility (Coming soon): Define custom expiration periods based on your security requirements

Technical Comparison

Current Identity Verification (HMAC Hashing)

  • Uses static userHash and optional companyHash

  • Generated using HMAC-SHA256 with your secret key

  • No built-in expiration mechanism

  • Suitable for basic verification needs

JWT Approach

  • Granular control over protected attributes

  • Dynamic token generation with embedded claims

  • Configurable expiration timestamps (Coming soon)

  • Aligned with current security standards

  • A single token contains all authentication data

Migration Process

Step 1: Copy Your JWT Secret Key

Navigate to Settings → Security in your Featurebase dashboard and copy your JWT secret value.

Step 2: Update Backend Token Generation

Replace your current HMAC hash generation with JWT creation:

Before (Identity Verification):

Example
import crypto from "crypto"; // Your identity verification secret const secretKey = "iv_your-secret-key"; // Use email or user identifier const userIdentifier = currentUser.email; // Generate HMAC hash const userHash = crypto .createHmac('sha256', secretKey) .update(userIdentifier) .digest('hex');

After (JWT Authentication):

Example
const jwt = require("jsonwebtoken"); // Configure token payload const tokenData = { name: currentUser.name, // Both email and userId should be provided when possible // At minimum, either email or userId must be present email: currentUser.email, userId: currentUser.userId, profilePicture: "https://example.com/images/yourcustomer.png", // Add any optional custom attributes - must be configured from settings to work title: "Product Manager", plan: "Premium", number: "123", // locale: "en", // optional, provide expected language for user // Optional fields companies: [ { id: "987654321", // required name: "Business Inc. 23", // required monthlySpend: 500, // optional createdAt: "2023-05-19T15:35:49.915Z", // optional // Add any optional custom attributes - must be configured from settings to work industry: "Fintech", location: "Canada", }, ], }; // Use environment variable for security const signingKey = process.env.FEATUREBASE_JWT_SECRET; // Generate signed token const authToken = jwt.sign(tokenData, signingKey, { algorithm: "HS256" });

Step 3: Update Frontend Implementation

Modify your Featurebase identify calls to use JWT instead of userHash:

Before (Identity Verification):

Example
Featurebase("boot", { appId: "yourAppId", email: "user@example.com", name: "User Name", userId: "123456", userHash: "user-hash-for-this-specific-user", createdAt: "2025-05-06T12:00:00Z", // ... other fields companies: [{ id: "123", companyHash: "company-hash-created-from-company-id", // ... other fields }] });

After (JWT Authentication):

Example
Featurebase("boot", { appId: "yourAppId", featurebaseJwt: "generated-jwt-here", // Required for secure auth - JWT generated on your server nonSensitiveAttr1: "non-sensitive-value", // Optional nonSensitiveAttr2: "non-sensitive-value", // Optional nonSensitiveAttr3: "non-sensitive-value", // Optional });

For non-sensitive attributes, you may still include them directly in the code snippet outside of the JWT payload.

Step 4: Configure Secure Attribute Updates

In your Featurebase dashboard:

  1. Navigate to Settings → Data → User Attributes

  2. For each sensitive attribute included in your JWT payload, edit it and enable "Require secure updates"

  3. This ensures these attributes can only be modified through authenticated requests

  4. Repeat for Company custom attributes if needed

Step 5: Testing Your Implementation

Before enforcing JWT authentication:

  • Use the hash checker at Settings → Security to validate your JWT generation

  • Test with non-admin accounts (admin auto-login is disabled for security)

  • Check browser console for any authentication errors

  • Verify that user data syncs correctly

  • Test token expiration and refresh flows

Step 6: Cleanup

Once testing is complete:

  • Remove userHash generation from your backend code

  • Clean up any client-side code sending userHash parameters

Migration Considerations

Backward Compatibility

Featurebase will accept both valid user hashes and JWTs. This allows for gradual migration without service disruption.

Data Attributes in JWTs

All user and company data should now be included in the JWT payload rather than sent separately. This includes:

  • User identification (email, userId, name)

  • Company associations

  • Custom fields and attributes

  • Monthly spend or other metrics

Security Best Practices

Token Management

  • Store JWT signing keys securely (environment variables, key vaults)

  • Never expose keys in client-side code or repositories

  • Use different keys for development and production environments

Troubleshooting Common Issues

Invalid JWT Errors

If you see 400 Bad Request errors:

  1. Verify you're using the correct signing key

  2. Check that the required fields (userId or email) are included

  3. Ensure the token hasn't expired

  4. Validate your JWT structure using the dashboard tools

User Not Identified

  • Confirm JWT is being passed in the identify or messenger boot call

  • Check that the organization name matches your Featurebase subdomain

  • Verify token signature using the correct algorithm (HS256)

Data Not Syncing

  • Ensure all user attributes are included in the JWT payload

  • Check that "Require secure updates" is configured correctly

  • Verify company data structure matches expected format

FAQ

Getting Help

For additional support during migration:

  • Contact Featurebase support with any error messages

  • Include your organization name and example JWT

Conclusion

Migrating from Identity Verification to JWT-based authentication strengthens your Featurebase security posture while providing better control over user sessions and data. The transition can be done gradually without disrupting existing users, making it a low-risk, high-reward security upgrade for your feedback and support platform.