instructure/canvas-lms · error · OAuthValidationError
User is from unacceptable domain
Error message
User is from unacceptable domain %{domain}. What it means
AuthenticationProvider::Google#unique_id validates Google id_token hosted-domain (hd) claims for Google Apps/domain-restricted auth providers. If the provider's hosted_domain is not '*' and the token's hd is not in the allowed hosted_domains list, it raises OAuthValidationError 'User is from unacceptable domain "...".' This enforces that only members of the configured Google Workspace domain(s) can authenticate.
Solutions
- Add the user's actual domain to the provider's hosted_domains list (Account > Authentication > Google provider settings)
- Set hosted_domain to '*' if any Google account should be allowed
- Have the user sign in with an account on the allowed domain (Google account chooser)
- Ensure the Google OAuth client's hd parameter and Canvas config stay in sync after domain changes
Example fix
// before provider.hosted_domains # ['example.edu'] but user hd is 'alumni.example.edu' // after provider.update(hosted_domains: ['example.edu', 'alumni.example.edu']) # or '*'
Defensive patterns
Strategy: try-catch
Validate before calling
// before initiating SSO, confirm account domain
domain = userEmail.split('@')[1];
if (!allowedHostedDomains.includes(domain) && allowedHostedDomains[0] !== '*')
showError('Use your institution Google account'); Type guard
const isAllowedDomain = (hd, allowed) => allowed.includes('*') || allowed.includes(hd) Try / catch
begin unique_id = provider.unique_id(token) rescue OAuthValidationError => e render login_page_with_error: e.message end
Prevention
- Keep hosted_domains in the Google auth provider settings current (add new institutional domains)
- Set hosted_domain '*' only when personal accounts are acceptable
- Test SSO with both an in-domain and personal account before rollout
- Sync the Google OAuth client hd hint with Canvas provider config
When it happens
Trigger: A user authenticates with a personal gmail.com account (hd nil or 'gmail.com') against a provider configured with hosted_domain 'example.edu'; an alias/secondary Workspace domain not included in hosted_domains; user's domain changed in Google Admin after provider config; multiple hosted_domains configured but the token carries one not listed.
Common situations: Schools adding a new domain (students.school.edu) without updating the Canvas Google provider settings; SSO rollout where users use personal accounts; testing with non-domain accounts; typo in the configured hosted domain setting.
Related errors
- Failed to decode OpenID Connect id_token: #
- incorrect_client
- invalid_client_secret
- User is from unacceptable issuer
- User is from unacceptable tenant
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/8525eebb1f571d8d.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/authentication_provider/google.rb:82
email
family_name
given_name
locale
name
sub
].freeze
end
def unique_id(token)
id_token = claims(token)
if hosted_domain
if !id_token["hd"]
# didn't make a "nice" exception for this, cause it should never happen.
# either we got MITM'ed (on the server side), or Google's docs lied;
# this check is just an extra precaution
raise "Google Apps user not received, but required"
elsif hosted_domain != "*" && !hosted_domains.include?(id_token["hd"])
raise OAuthValidationError, t("User is from unacceptable domain %{domain}.", domain: id_token["hd"].inspect)
end
end
super
end
protected
def userinfo_endpoint
"https://www.googleapis.com/oauth2/v3/userinfo"
end
def client_options
super.merge(
auth_scheme: :basic_auth
)
end
def authorize_optionsView on GitHub (pinned to 1c9f0bb801)