class OmniAuth::Strategies::ActiveDirectory
A strategy for authentication against Azure Active Directory.
Constants
- DEFAULT_RESPONSE_MODE
- DEFAULT_RESPONSE_TYPE
Public Instance Methods
Overridden method from OmniAuth::Strategy. This is the second step in the authentication process. It is called after the user enters credentials at the authorization endpoint.
# File lib/omniauth/strategies/activedirectory.rb, line 64 def callback_phase error = request.params['error_reason'] || request.params['error'] fail(OAuthError, error) if error @session_state = request.params['session_state'] @id_token = request.params['id_token'] @code = request.params['code'] @claims, @header = validate_and_parse_id_token(@id_token) # validate_chash(@code, @claims, @header) super end
Overridden method from OmniAuth::Strategy. This is the first step in the authentication process.
# File lib/omniauth/strategies/activedirectory.rb, line 56 def request_phase redirect authorize_endpoint_url end
The client id (key) and tenant must be configured when the OmniAuth
middleware is installed. Example:
require 'omniauth' require 'omniauth-activedirectory' use OmniAuth::Builder do provider :activedirectory, ENV['AAD_KEY'], ENV['AAD_TENANT'] end
# File lib/omniauth/strategies/activedirectory.rb, line 26 args [:client_id, :tenant]
Private Instance Methods
The client id of the calling application. This must be configured where AD is installed as an OmniAuth
strategy.
@return String
# File lib/omniauth/strategies/activedirectory.rb, line 97 def client_id return options.client_id if options.client_id fail StandardError, 'No client_id specified in AzureAD configuration.' end
Fetches the OpenId Connect configuration for the AzureAD tenant. This contains several import values, including:
authorization_endpoint token_endpoint token_endpoint_auth_methods_supported jwks_uri response_types_supported response_modes_supported subject_types_supported id_token_signing_alg_values_supported scopes_supported issuer claims_supported microsoft_multi_refresh_token check_session_iframe end_session_endpoint userinfo_endpoint
@return Hash
# File lib/omniauth/strategies/activedirectory.rb, line 146 def fetch_openid_config JSON.parse(Net::HTTP.get(URI(openid_config_url))) rescue JSON::ParserError raise StandardError, 'Unable to fetch OpenId configuration for ' \ 'AzureAD tenant.' end
Fetches the current signing keys for Azure AD. Note that there should always two available, and that they have a 6 week rollover.
Each key is a hash with the following fields:
kty, use, kid, x5t, n, e, x5c
@return Array
# File lib/omniauth/strategies/activedirectory.rb, line 118 def fetch_signing_keys response = JSON.parse(Net::HTTP.get(URI(signing_keys_url))) response['keys'] rescue JSON::ParserError raise StandardError, 'Unable to fetch AzureAD signing keys.' end
The expected id token issuer taken from the discovery endpoint.
@return String
# File lib/omniauth/strategies/activedirectory.rb, line 106 def issuer openid_config['issuer'] end
Generates a new nonce for one time use. Stores it in the session so multiple users don't share nonces. All nonces should be generated by this method.
@return String
# File lib/omniauth/strategies/activedirectory.rb, line 159 def new_nonce session['omniauth-azure-activedirectory.nonce'] = SecureRandom.uuid end
A memoized version of fetch_openid_config
.
@return Hash
# File lib/omniauth/strategies/activedirectory.rb, line 167 def openid_config @openid_config ||= fetch_openid_config end
The location of the OpenID configuration for the tenant.
@return String
# File lib/omniauth/strategies/activedirectory.rb, line 175 def openid_config_url "https://login.windows.net/#{tenant}/.well-known/openid-configuration" end
Returns the most recent nonce for the session and deletes it from the session.
@return String
# File lib/omniauth/strategies/activedirectory.rb, line 184 def read_nonce session.delete('omniauth-azure-activedirectory.nonce') end
The response_mode
that will be set in the authorization request query parameters. Can be overridden by the client, but it shouldn't need to be.
@return String
# File lib/omniauth/strategies/activedirectory.rb, line 204 def response_mode options[:response_mode] || DEFAULT_RESPONSE_MODE end
The response_type
that will be set in the authorization request query parameters. Can be overridden by the client, but it shouldn't need to be.
@return String
# File lib/omniauth/strategies/activedirectory.rb, line 194 def response_type options[:response_type] || DEFAULT_RESPONSE_TYPE end
The keys used to sign the id token JWTs. This is just a memoized version of fetch_signing_keys
.
@return Array
# File lib/omniauth/strategies/activedirectory.rb, line 213 def signing_keys @signing_keys ||= fetch_signing_keys end
The location of the public keys of the token signer. This is parsed from the OpenId config response.
@return String
# File lib/omniauth/strategies/activedirectory.rb, line 222 def signing_keys_url return openid_config['jwks_uri'] if openid_config.include? 'jwks_uri' fail StandardError, 'No jwks_uri in OpenId config response.' end
The tenant of the calling application. Note that this must be explicitly configured when installing the AzureAD OmniAuth
strategy.
@return String
# File lib/omniauth/strategies/activedirectory.rb, line 232 def tenant return options.tenant if options.tenant fail StandardError, 'No tenant specified in AzureAD configuration.' end
Verifies the signature of the id token as well as the exp, nbf, iat, iss, and aud fields.
See OpenId Connect Core 3.1.3.7 and 3.2.2.11.
@return Claims, Header
# File lib/omniauth/strategies/activedirectory.rb, line 244 def validate_and_parse_id_token(id_token) # The second parameter is the public key to verify the signature. # However, that key is overridden by the value of the executed block # if one is present. # # If you're thinking that this looks ugly with the raw nil and boolean, # see https://github.com/jwt/ruby-jwt/issues/59. jwt_claims, jwt_header = JWT.decode(id_token, nil, false, verify_options) do |header| # There should always be one key from the discovery endpoint that # matches the id in the JWT header. x5c = (signing_keys.find do |key| key['kid'] == header['kid'] end || {})['x5c'] if x5c.nil? || x5c.empty? fail JWT::VerificationError, 'No keys from key endpoint match the id token' end # The key also contains other fields, such as n and e, that are # redundant. x5c is sufficient to verify the id token. OpenSSL::X509::Certificate.new(JWT.base64url_decode(x5c.first)).public_key end return jwt_claims, jwt_header if jwt_claims['nonce'] == read_nonce fail JWT::DecodeError, 'Returned nonce did not match.' end
The options passed to the Ruby JWT library to verify the id token. Note that these are not all the checks we perform. Some (like nonce) are not handled by the JWT API and are checked manually in validate_and_parse_id_token
.
@return Hash
# File lib/omniauth/strategies/activedirectory.rb, line 294 def verify_options { verify_expiration: true, verify_not_before: true, verify_iat: true, verify_iss: true, 'iss' => issuer, verify_aud: true, 'aud' => client_id } end