Budibase/budibase · error

Could not determine user email from profile ${JSON.stringify

Error message

Could not determine user email from profile ${JSON.stringify(profile)} and claims ${JSON.stringify(jwtClaims)}

What it means

getEmail resolves the user's email during OIDC SSO verification from the profile, then jwtClaims.email, then jwtClaims.preferred_username (if it is a valid email). If none of these yield a usable email it throws this error because Budibase requires an email identity to save/link the SSO user.

Source

Thrown at packages/backend-core/src/middleware/passport/sso/oidc.ts:150

 */
function getEmail(profile: SSOProfile, jwtClaims: JwtClaims) {
  // profile not guaranteed to contain email e.g. github connected azure ad account
  if (profile._json.email) {
    return profile._json.email.toLowerCase()
  }

  // fallback to id token email
  if (jwtClaims.email) {
    return jwtClaims.email.toLowerCase()
  }

  // fallback to id token preferred username
  const username = jwtClaims.preferred_username
  if (username && validEmail(username)) {
    return username.toLowerCase()
  }

  throw new Error(
    `Could not determine user email from profile ${JSON.stringify(
      profile
    )} and claims ${JSON.stringify(jwtClaims)}`
  )
}

/**
 * Determines whether the identity provider has verified the email that
 * getEmail resolved. Mirrors getEmail's source precedence so the returned flag
 * describes the same claim. An absent email_verified is treated as unverified
 * (OIDC Core §5.7). A preferred_username used as an email is never considered
 * verified.
 * @param profile The structured profile created by passport using the user info endpoint
 * @param jwtClaims The claims returned in the id token
 */
function getEmailVerified(profile: SSOProfile, jwtClaims: JwtClaims): boolean {
  if (profile._json.email) {
    return profile._json.email_verified === true

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Configure the OIDC provider to release the email scope and the email claim in the ID token/userinfo
  2. Set preferred_username to a valid email address on the IdP, or map a claim containing the user's email into preferred_username
  3. Check the profile JSON in the message to see exactly which claims the IdP is returning and adjust claim mapping
  4. If the IdP cannot provide emails, use a different authentication method or a connector that synthesizes emails

Example fix

// before (IdP returns preferred_username: "jdoe")
// after: configure claim mapping so preferred_username = "jdoe@example.com" (or add scope "email")
Defensive patterns

Strategy: validation

Validate before calling

function hasUsableEmail(profile, jwtClaims) {
  const candidates = [profile?.email, jwtClaims?.email, jwtClaims?.preferred_username]
  return candidates.some(c => typeof c === "string" && /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(c))
}

Type guard

function isEmail(value) {
  return typeof value === "string" && /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(value)
}

Try / catch

try {
  await strategy.authenticate(req)
} catch (err) {
  if (String(err.message).startsWith("Could not determine user email")) {
    // surface a friendly 'your identity provider does not share your email' message
  }
}

Prevention

When it happens

Trigger: An OIDC IdP completes authentication but returns a profile and ID token with no email claim, no valid email-formatted claim values, and no preferred_username that parses as an email.

Common situations: IdPs that only return a subject ID or opaque username (e.g. username 'jdoe' with no @domain); email scope not requested/granted in the OIDC client config; IdP admin has not populated emails for users; custom enterprise IdPs with nonstandard claims.

Related errors


AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29). Data as JSON: /api/errors/4a1875495c5382bc. Report an issue: GitHub.