class OmniAuth::Strategies::AzureActiveDirectoryDavevanfleet

A strategy for authentication against Azure Active Directory.

Constants

DEFAULT_RESPONSE_MODE
DEFAULT_RESPONSE_TYPE

Public Instance Methods

callback_phase() click to toggle source

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.

Calls superclass method
# File lib/omniauth/strategies/azure_activedirectory_davevanfleet.rb, line 86
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
request_phase() click to toggle source

Overridden method from OmniAuth::Strategy. This is the first step in the authentication process.

# File lib/omniauth/strategies/azure_activedirectory_davevanfleet.rb, line 78
def request_phase
  redirect authorize_endpoint_url
end
unknown() click to toggle source

The client id (key) and tenant must be configured when the OmniAuth middleware is installed. Example:

require 'omniauth'
require 'omniauth-azure-activedirectory'

use OmniAuth::Builder do
  provider :azure_activedirectory, ENV['AAD_KEY'], ENV['AAD_TENANT']
end
# File lib/omniauth/strategies/azure_activedirectory_davevanfleet.rb, line 48
args [:client_id, :tenant]

Private Instance Methods

authorize_endpoint_url() click to toggle source

Constructs a one-time-use authorize_endpoint. This method will use a new nonce on each invocation.

@return String

# File lib/omniauth/strategies/azure_activedirectory_davevanfleet.rb, line 104
def authorize_endpoint_url
  uri = URI(openid_config['authorization_endpoint'])
  uri.query = URI.encode_www_form(client_id: client_id,
                                  redirect_uri: callback_url,
                                  response_mode: response_mode,
                                  response_type: response_type,
                                  nonce: new_nonce)
  uri.to_s
end
client_id() click to toggle source

The client id of the calling application. This must be configured where AzureAD is installed as an OmniAuth strategy.

@return String

# File lib/omniauth/strategies/azure_activedirectory_davevanfleet.rb, line 119
def client_id
  return options.client_id if options.client_id
  fail StandardError, 'No client_id specified in AzureAD configuration.'
end
fetch_openid_config() click to toggle source

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/azure_activedirectory_davevanfleet.rb, line 168
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
fetch_signing_keys() click to toggle source

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/azure_activedirectory_davevanfleet.rb, line 140
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
issuer() click to toggle source

The expected id token issuer taken from the discovery endpoint.

@return String

# File lib/omniauth/strategies/azure_activedirectory_davevanfleet.rb, line 128
def issuer
  openid_config['issuer']
end
new_nonce() click to toggle source

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/azure_activedirectory_davevanfleet.rb, line 181
def new_nonce
  session['omniauth-azure-activedirectory.nonce'] = SecureRandom.uuid
end
openid_config() click to toggle source

A memoized version of fetch_openid_config.

@return Hash

# File lib/omniauth/strategies/azure_activedirectory_davevanfleet.rb, line 189
def openid_config
  @openid_config ||= fetch_openid_config
end
openid_config_url() click to toggle source

The location of the OpenID configuration for the tenant.

@return String

# File lib/omniauth/strategies/azure_activedirectory_davevanfleet.rb, line 197
def openid_config_url
  "https://login.windows.net/#{tenant}/.well-known/openid-configuration"
end
read_nonce() click to toggle source

Returns the most recent nonce for the session and deletes it from the session.

@return String

# File lib/omniauth/strategies/azure_activedirectory_davevanfleet.rb, line 206
def read_nonce
  session.delete('omniauth-azure-activedirectory.nonce')
end
response_mode() click to toggle source

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/azure_activedirectory_davevanfleet.rb, line 226
def response_mode
  options[:response_mode] || DEFAULT_RESPONSE_MODE
end
response_type() click to toggle source

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/azure_activedirectory_davevanfleet.rb, line 216
def response_type
  options[:response_type] || DEFAULT_RESPONSE_TYPE
end
signing_keys() click to toggle source

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/azure_activedirectory_davevanfleet.rb, line 235
def signing_keys
  @signing_keys ||= fetch_signing_keys
end
signing_keys_url() click to toggle source

The location of the public keys of the token signer. This is parsed from the OpenId config response.

@return String

# File lib/omniauth/strategies/azure_activedirectory_davevanfleet.rb, line 244
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
tenant() click to toggle source

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/azure_activedirectory_davevanfleet.rb, line 254
def tenant
  return options.tenant if options.tenant
  fail StandardError, 'No tenant specified in AzureAD configuration.'
end
validate_and_parse_id_token(id_token) click to toggle source

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/azure_activedirectory_davevanfleet.rb, line 266
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
verify_options() click to toggle source

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/azure_activedirectory_davevanfleet.rb, line 316
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