{"record":{"id":"ba8a187136fd42ce","repo":"mastra-ai/mastra","slug":"google-user-is-not-in-an-allowed-hosted-domain","errorCode":null,"errorMessage":"Google user is not in an allowed hosted domain","messagePattern":"Google user is not in an allowed hosted domain","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"auth/google/src/auth-provider.ts","lineNumber":373,"sourceCode":"      issuer: GOOGLE_ISSUERS,\n      audience: this.clientId,\n    });\n\n    if (nonce && payload.nonce !== nonce) {\n      throw new Error('Invalid Google ID token nonce');\n    }\n\n    if (hasExpired(payload)) {\n      throw new Error('Google ID token has expired');\n    }\n\n    const user = mapGoogleClaimsToUser(payload);\n    if (!user.googleId) {\n      throw new Error('Google ID token is missing subject');\n    }\n\n    if (!this.isHostedDomainAllowed(user.hostedDomain)) {\n      throw new Error('Google user is not in an allowed hosted domain');\n    }\n\n    return user;\n  }\n\n  private isHostedDomainAllowed(hostedDomain: string | undefined): boolean {\n    if (this.allowedDomains.length === 0) return true;\n    const domain = normalizeDomain(hostedDomain);\n    if (!domain) return false;\n    return this.allowedDomains.includes(domain);\n  }\n\n  private extractBearerToken(request: Request): string | null {\n    const authHeader = request.headers.get('Authorization');\n    if (!authHeader) return null;\n    const token = authHeader.replace(/^Bearer\\s+/i, '').trim();\n    return token || null;\n  }","sourceCodeStart":355,"sourceCodeEnd":391,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/auth/google/src/auth-provider.ts#L355-L391","documentation":"Google Workspace accounts carry an hd (hosted domain) claim. When the provider is configured with allowedDomains and/or hostedDomain, verifyIdToken enforces that the authenticated user's hosted domain is allowed via isHostedDomainAllowed. If the claim is absent or doesn't match the configured domains, the login is rejected — this is the library's Workspace domain restriction feature.","triggerScenarios":"verifyIdToken succeeds on signature/nonce/expiry/subject but the mapped user's hostedDomain is undefined or not in the allowedDomains/hostedDomain configured on the provider — e.g. a personal @gmail.com account, or an account from a different Workspace tenant.","commonSituations":"allowing only @yourcompany.com but a tester logs in with a personal Gmail account; hostedDomain configured too narrowly after a company rename/alias change; users whose Workspace account doesn't expose hd because the domain isn't a Workspace domain; mismatch between GOOGLE_ALLOWED_DOMAINS spelling and the actual hd claim (case/suffix).","solutions":["Log in with an account whose hd matches the configured allowedDomains/hostedDomain.","Update GOOGLE_ALLOWED_DOMAINS or the hostedDomain option to include the correct domain(s) the org actually uses.","If any Google account should be allowed, remove the allowedDomains/hostedDomain restriction.","Remember the hd claim is only present for Workspace accounts; if you need to include such accounts, don't rely on hostedDomain matching alone."],"exampleFix":"// before\nGOOGLE_ALLOWED_DOMAINS=oldcompany.com\n\n// after\nGOOGLE_ALLOWED_DOMAINS=newcompany.com,oldcompany.com","handlingStrategy":"validation","validationCode":"const { payload } = decodeJwt(token);\nconst hd = (payload as { hd?: string }).hd;\nconst allowed = ['yourcompany.com'];\nif (allowed.length > 0 && (!hd || !allowed.includes(hd.toLowerCase()))) {\n  // show an org-account-required message before calling verifyIdToken\n}","typeGuard":null,"tryCatchPattern":"try {\n  const user = await provider.verifyIdToken(token, nonce);\n} catch (err) {\n  if (err instanceof Error && err.message === 'Google user is not in an allowed hosted domain') {\n    return res.status(403).send('Please sign in with your organization Google Workspace account.');\n  }\n  throw err;\n}","preventionTips":["Keep GOOGLE_ALLOWED_DOMAINS in sync with the domains your org actually uses (including aliases and post-rename domains).","Test with both a Workspace account and a personal Gmail account to confirm intended behavior.","Communicate the domain restriction on the login page so users pick the right account.","Remember hd is absent for consumer accounts; don't assume it's always present."],"tags":["oauth","google-workspace","hosted-domain","authorization","sso"],"backgroundTag":"domain-restriction-denied","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}