Budibase/budibase · error
Unexpected token payload
Error message
Unexpected token payload
What it means
jwt.verify normally returns either a string (when the JWT is an unsecured/unencoded payload or parsed as JWS with a string body) or a decoded object. This plain Error is thrown when jsonwebtoken returns a string payload instead of an object, meaning the token body is not the JSON object with claims (email, etc.) that embed SSO requires. The library only accepts object payloads because it reads claims like the configured emailClaim from the payload.
Source
Thrown at packages/server/src/sdk/workspace/embedSSO/index.ts:85
): string | undefined => {
const value = emailClaim
.split(".")
.reduce<any>((acc, part) => (acc == null ? acc : acc[part]), payload)
return typeof value === "string" ? value : undefined
}
const verifyToken = (
token: string,
config: EmbedSSOConfig
): Record<string, any> => {
const key = decodeSecret(config.key)
const options: jwt.VerifyOptions = { algorithms: [config.algorithm] }
if (config.issuer) {
options.issuer = config.issuer
}
const decoded = jwt.verify(token, key, options)
if (typeof decoded === "string") {
throw new Error("Unexpected token payload")
}
return decoded
}
/**
* Verify a signed token from the embedding host, map its identity to an
* existing Budibase user and, if found, establish a Budibase session by setting
* the auth cookie. Returns true if the user was authenticated.
*/
export async function authenticateEmbedUser(
ctx: Ctx,
config: EmbedSSOConfig,
token: string
): Promise<boolean> {
let payload: Record<string, any>
try {
payload = verifyToken(token, config)
} catch (err) {View on GitHub (pinned to a81a902e9a)
Solutions
- Fix the token producer to sign a JSON object payload containing at least the email claim, e.g. jwt.sign({ email: user.email }, key, { algorithm: config.algorithm })
- Verify the embedding host's signing code uses the same key and algorithm configured in the embed SSO config
- If tokens come from a third party that only produces string payloads, decode/transform them into an object before sending, or map a custom emailClaim path after signing an object
Example fix
// before (host side)
const token = jwt.sign(user.id, sharedSecret) // string payload
// after
const token = jwt.sign({ email: user.email }, sharedSecret, { algorithm: "HS256" }) Defensive patterns
Strategy: try-catch
Validate before calling
const headerB64 = token.split(".")[1]
const payload = JSON.parse(Buffer.from(headerB64, "base64url").toString())
if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
throw new Error("Token payload must be a JSON object")
} Type guard
const isJwtPayload = (v: unknown): v is Record<string, any> => typeof v === "object" && v !== null && !Array.isArray(v)
Try / catch
let payload: Record<string, any>
try {
payload = verifyToken(token, config)
} catch (err) {
if (err.message === "Unexpected token payload") {
// token was signed with a non-object payload — reject and re-authenticate
return false
}
throw err
} Prevention
- Sign JWTs with object payloads containing at least the email claim
- Keep the embedding host's signing library and algorithm aligned with the embed SSO config
- Validate token shape (decoded header/payload) in the host app before sending
When it happens
Trigger: Authenticating an embed user with a token whose decoded payload is a plain string — e.g. a token built with jsonwebtoken.sign("some-string", secret) rather than sign(payloadObject, secret), or a hand-crafted/opaque token that happens to pass signature verification.
Common situations: The embedding host signs a raw string or non-JSON payload instead of a claims object; an old or incompatible SDK on the host side emits string tokens; a proxy or test harness sends an arbitrary opaque token signed with the shared secret.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- A new verification key is required when changing the embed S
- Configuration invalid. Must contain google clientID and clie
- Error constructing google authentication strategy: ${err}
- Could not determine user email from profile ${JSON.stringify
- Error constructing OIDC authentication strategy - ${err}
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/bd6f9f1f21a712a9.
Report an issue: GitHub.