SaaS Identity
Understanding tenant isolation through JWT claims
SaaS Identity is a core concept in Agentic Team OS that provides secure tenant isolation using JWT (JSON Web Token) claims.
This pattern ensures that each request is properly authenticated and scoped to the correct tenant.
Overview
The SaaS Identity pattern uses JWT tokens with custom claims to:
- Scope access to resources
- Track user credits
- Rate limit requests
- Maintain security boundaries
The diagram above illustrates the flow of a request through the SaaS Identity system:
- The API receives and parses the incoming event with a JWT token
- The token is validated with the SaaS Identity Provider(s)
- The claims within the token are validated
- Finally, the usecase is executed with the validated identity context
Let’s break down how the SaaS identity system works in the agentic-api-template:
Core Components
- SaaSIdentityVendingMachine Class (
/packages/utils/src/tools/saas-identity.ts
):
While initializing dependencies in the constructor violates the Dependency Inversion Principle, it’s done here intentionally for simplicity. Since we know we’ll only use Clerk as our JWT service, creating the instance directly reduces boilerplate and makes the code more straightforward to use - just instantiate and go! You are free to reverse this pattern or swap in a different JWT service like Okta or Auth0 if you need to. It will just need to implement the IJwtService interface.
- JWT Service (
/packages/utils/src/vendors/jwt-vendor.ts
):
Metadata
You can modify how a user is validated and what data is extracted from the JWT token.
For example, here is the schema for a valid user:
The keyId is from Unkey, and helps downstream services identity the users remaining credits for different requests.
The users keyId is added to the metadata of the JWT token on signup via a webhook.
When working with Clerk you can modify the claims on the JWT token to include this keyID.
This keyId is used to track the users remaining credits for different requests.
- JWT Schema (
/packages/utils/src/metadata/jwt.schema.ts
):
Authentication Flow
-
Token Validation:
- When a request comes in, the
getValidUser
method is called - It first tries to validate via auth header using
getValidUserFromAuthHeader
- If successful, returns a ValidUser object
- If not, throws an “Unauthorized” error
- When a request comes in, the
-
JWT Processing:
- The ClerkService handles JWT token validation
- Extracts token from Authorization header
- Validates token using Clerk’s verification system
- Decodes token payload for user information
Usage in Primary Adapters
Below is an example of a Lambda Primary Adapter in the orchestrator that uses the SaaSIdentityVendingMachine to validate a user.
Creating the SaaS Identity
1. Adding the KeyId to the JWT Token
When a user signs up, a webhook is handled by the control plane of the system.
Inside of the user module, the registerUserAdapter
is called.
Here you will likely want to add any additional validation needed and in the usecase handler onboarding your user.
/control-plane/user/adapters/primary/register-user.adapter.ts
Go to the page on Clerk for more information on how to configure the webhook for this flow.
2. Updating User’s KeyID Properties
When a user makes a purchase, a webhook is handled by the control plane of the system.
Inside of the billing module, the checkoutSessionWebhookAdapter
is called.
Here you will likely want to add any additional validation needed and in the usecase handler updating the users keyId properties.
This is where you can add credits to the users API key and update the keyId properties based on the purchase.
/control-plane/billing/adapters/primary/checkout-session-completed.adapter.ts
Go to the page Setup Webhooks for more information on how to configure the webhooks to enable this flow.
Was this page helpful?