So far enjoying stytch so thank you for the hard work.
A few things I have noticed. The shape of the decoded JWT is not typed, cannot seem to find in the B2B lib. I decided to make my own, but running into some interface mismatches with what I would expect.
// stytch-decoded-session-jwt.ts
import { MemberSession } from "stytch";
export interface DecodedStytchSessionJwt {
// Custom Claims, Configured on https://stytch.com/dashboard/custom-claim-templates
memberId: string;
orgId: string;
emailAddress: string;
// Reserved
aud: string[];
exp: number;
iat: number;
iss: string;
nbf: number;
sub: string;
// Organization details
"https://stytch.com/organization": {
organization_id: string;
slug: string;
};
// Session Object
"https://stytch.com/session": MemberSession;
}
Here is an example decoded JWT that I received from a B2B auth.
{
"aud": [
"[REDACTED]"
],
"emailAddress": "[REDACTED]",
"exp": "[REDACTED]",
"https://stytch.com/organization": {
"organization_id": "[REDACTED]",
"slug": "[REDACTED]"
},
"https://stytch.com/session": {
"id": "[REDACTED]",
"started_at": "2025-01-18T20:52:01Z",
"last_accessed_at": "2025-01-18T20:52:01Z",
"expires_at": "2025-01-18T21:52:01Z",
"attributes": {
"user_agent": "[REDACTED]",
"ip_address": "[REDACTED]"
},
"authentication_factors": [
{
"type": "oauth",
"delivery_method": "oauth_google",
"last_authenticated_at": "2025-01-18T20:51:45Z",
"google_oauth_factor": {
"id": "[REDACTED]",
"email_id": "[REDACTED]",
"provider_subject": "[REDACTED]"
}
}
],
"roles": [
"stytch_member",
"stytch_admin"
]
},
"iat": "[REDACTED]",
"iss": "[REDACTED]",
"memberId": "[REDACTED]",
"nbf": "[REDACTED]",
"orgId": "[REDACTED]",
"orgSlug": null,
"sub": "[REDACTED]"
}
So far enjoying stytch so thank you for the hard work.
A few things I have noticed. The shape of the decoded JWT is not typed, cannot seem to find in the B2B lib. I decided to make my own, but running into some interface mismatches with what I would expect.
Here is an example decoded JWT that I received from a B2B auth.
Two issues:
custom_claimsfield on the session object, whereas in the actual decoded JWT, the custom claims are flattened onto the decoded JWT object. No preference on where they live, but given that the jwt actually encodes the member session my expectation given the MemberSession type would be that they would live on the session object.