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

  1. Add the user's actual domain to the provider's hosted_domains list (Account > Authentication > Google provider settings)
  2. Set hosted_domain to '*' if any Google account should be allowed
  3. Have the user sign in with an account on the allowed domain (Google account chooser)
  4. 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

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


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_options

View on GitHub (pinned to 1c9f0bb801)