class OmniAuth::MicrosoftGraph::DomainVerifier
Attributes
access_token[R]
email_domain[R]
id_token[R]
permitted_domains[R]
skip_verification[R]
upn_domain[R]
Public Class Methods
new(auth_hash, access_token, options)
click to toggle source
# File lib/omniauth/microsoft_graph/domain_verifier.rb, line 21 def initialize(auth_hash, access_token, options) @email_domain = auth_hash['info']['email']&.split('@')&.last @upn_domain = auth_hash['extra']['raw_info']['userPrincipalName']&.split('@')&.last @access_token = access_token @id_token = access_token.params['id_token'] @skip_verification = options[:skip_domain_verification] end
verify!(auth_hash, access_token, options)
click to toggle source
# File lib/omniauth/microsoft_graph/domain_verifier.rb, line 17 def self.verify!(auth_hash, access_token, options) new(auth_hash, access_token, options).verify! end
Public Instance Methods
verify!()
click to toggle source
# File lib/omniauth/microsoft_graph/domain_verifier.rb, line 29 def verify! # The userPrincipalName property is mutable, but must always contain a # verified domain: # # "The general format is alias@domain, where domain must be present in # the tenant's collection of verified domains." # https://learn.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0 # # This means while it's not suitable for consistently identifying a user # (the domain might change), it is suitable for verifying membership in # a given domain. return true if email_domain == upn_domain || skip_verification == true || (skip_verification.is_a?(Array) && skip_verification.include?(email_domain)) || domain_verified_jwt_claim raise DomainVerificationError, verification_error_message end
Private Instance Methods
domain_verified_jwt_claim()
click to toggle source
learn.microsoft.com/en-us/entra/identity-platform/optional-claims-reference Microsoft offers an optional claim ‘xms_edov` that will indicate whether the user’s email domain is part of the organization’s verified domains. This has to be explicitly configured in the app registration.
To get to it, we need to decode the ID token with the key material from Microsoft’s OIDC configuration endpoint, and inspect it for the claim in question.
# File lib/omniauth/microsoft_graph/domain_verifier.rb, line 63 def domain_verified_jwt_claim oidc_config = access_token.get(OIDC_CONFIG_URL).parsed algorithms = oidc_config['id_token_signing_alg_values_supported'] jwks = get_jwks(oidc_config) decoded_token = JWT.decode(id_token, nil, true, algorithms: algorithms, jwks: jwks) xms_edov_valid?(decoded_token) rescue JWT::VerificationError, ::OAuth2::Error false end
get_jwks(oidc_config)
click to toggle source
# File lib/omniauth/microsoft_graph/domain_verifier.rb, line 79 def get_jwks(oidc_config) # Depending on the tenant, the JWKS endpoint might be different. We need to # consider both the JWKS from the OIDC configuration and the common JWKS endpoint. oidc_config_jwk_keys = access_token.get(oidc_config['jwks_uri']).parsed[:keys] common_jwk_keys = access_token.get(COMMON_JWKS_URL).parsed[:keys] JWT::JWK::Set.new(oidc_config_jwk_keys + common_jwk_keys) end
verification_error_message()
click to toggle source
# File lib/omniauth/microsoft_graph/domain_verifier.rb, line 87 def verification_error_message <<~MSG The email domain '#{email_domain}' is not a verified domain for this Azure AD account. You can either: * Update the user's email to match the principal domain '#{upn_domain}' * Skip verification on the '#{email_domain}' domain (not recommended) * Disable verification with `skip_domain_verification: true` (NOT RECOMMENDED!) Refer to the README for more details. MSG end
xms_edov_valid?(decoded_token)
click to toggle source
# File lib/omniauth/microsoft_graph/domain_verifier.rb, line 73 def xms_edov_valid?(decoded_token) # https://github.com/MicrosoftDocs/azure-docs/issues/111425#issuecomment-1761043378 # Comments seemed to indicate the value is not consistent ['1', 1, 'true', true].include?(decoded_token.first['xms_edov']) end