Skip to content

Commit

Permalink
Account for internal tenant ID of 'common' during JWT validation
Browse files Browse the repository at this point in the history
  • Loading branch information
pond committed Oct 21, 2024
1 parent 4f45fbc commit 16c55b0
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 17 deletions.
17 changes: 11 additions & 6 deletions lib/omniauth/strategies/entra_id.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ class EntraId < OmniAuth::Strategies::OAuth2
option :tenant_provider, nil
option :jwt_leeway, 60

DEFAULT_SCOPE = 'openid profile email'
DEFAULT_SCOPE = 'openid profile email'
COMMON_TENANT_ID = 'common'

# The tenant_provider must return client_id, client_secret and,
# optionally, tenant_id and base_url.
Expand Down Expand Up @@ -43,7 +44,7 @@ def client
options.tenant_id = if provider.respond_to?(:tenant_id)
provider.tenant_id
else
'common'
COMMON_TENANT_ID
end

options.base_url = if provider.respond_to?(:base_url )
Expand Down Expand Up @@ -72,14 +73,14 @@ def client
'oauth2/v2.0'
end

base_url = if options.custom_policy && options.tenant_name
tenanted_endpoint_base_url = if options.custom_policy && options.tenant_name
"https://#{options.tenant_name}.b2clogin.com/#{options.tenant_name}.onmicrosoft.com/#{options.custom_policy}"
else
"#{options.base_url}/#{options.tenant_id}"
end

options.client_options.authorize_url = "#{base_url}/#{oauth2}/authorize"
options.client_options.token_url = "#{base_url}/#{oauth2}/token"
options.client_options.authorize_url = "#{tenanted_endpoint_base_url}/#{oauth2}/authorize"
options.client_options.token_url = "#{tenanted_endpoint_base_url}/#{oauth2}/token"

super
end
Expand Down Expand Up @@ -136,7 +137,11 @@ def raw_info
# sense to verify the token issuer, because the value of 'iss' in the
# token depends on the 'tid' in the token itself.
#
issuer = options.tenant_id.nil? ? nil : "#{options.base_url}/#{options.tenant_id}/v2.0"
issuer = if options.tenant_id.nil? || options.tenant_id == COMMON_TENANT_ID
nil
else
"#{options.base_url || BASE_URL}/#{options.tenant_id}/v2.0"
end

# https://learn.microsoft.com/en-us/entra/identity-platform/id-tokens#validate-tokens
#
Expand Down
61 changes: 50 additions & 11 deletions spec/omniauth/strategies/entra_id_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ def adfs?
end # "describe '#client' do"
end # "describe 'dynamic configuration with on premise ADFS' do"

describe 'raw_info' do
describe 'raw_info and validation' do
let(:issued_at ) { Time.now.utc.to_i }
let(:expires_at) { (Time.now.utc + 3600).to_i }

Expand All @@ -476,7 +476,6 @@ def adfs?
end

let(:id_token_info) do

{
ver: '2.0',
iss: 'https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0',
Expand Down Expand Up @@ -603,15 +602,55 @@ def adfs?
end
end # "context 'with an invalid audience' do"

context 'with an invalid issuer' do
subject do
OmniAuth::Strategies::EntraId.new(app, {client_id: 'id', client_secret: 'secret', tenant_id: 'test-tenant'})
end
context 'issuers' do
context 'when valid' do
subject do
OmniAuth::Strategies::EntraId.new(app, {client_id: 'id', client_secret: 'secret', tenant_id: '9188040d-6c67-4c5b-b112-36a304b66dad'})
end

it 'fails validation' do
expect { subject.info }.to raise_error(JWT::InvalidIssuerError)
end
end # "context 'with an invalid issuer' do"
it 'passes validation' do
expect { subject.info }.to_not raise_error()
end
end # "context 'when valid' do"

context 'when invalid' do
subject do
OmniAuth::Strategies::EntraId.new(app, {client_id: 'id', client_secret: 'secret', tenant_id: 'a-mismatched-tenant-id'})
end

it 'fails validation' do
expect { subject.info }.to raise_error(JWT::InvalidIssuerError)
end
end # "context 'when invalid' do"

context 'multi-tenant' do
let(:id_token_info) do
hash = super()
hash['iss'] = 'invalid issuer that should be ignored'
hash
end

context 'no tenant specified' do
subject do
OmniAuth::Strategies::EntraId.new(app, {client_id: 'id', client_secret: 'secret', tenant_id: nil})
end

it 'skips issuer validation since tenant ID is unknown' do
expect { subject.info }.to_not raise_error()
end
end # "context 'no tenant specified' do"

context '"common" tenant specified' do
subject do
OmniAuth::Strategies::EntraId.new(app, {client_id: 'id', client_secret: 'secret', tenant_id: OmniAuth::Strategies::EntraId::COMMON_TENANT_ID})
end

it 'skips issuer validation since tenant ID is unknown' do
expect { subject.info }.to_not raise_error()
end
end # "context '"common" tenant specified' do"
end # "context 'multi-tenant' do"
end # "context 'issuers' do"

context 'with an invalid not_before' do
let(:issued_at) { (Time.now.utc + 70).to_i } # Invalid because leeway is 60 seconds
Expand Down Expand Up @@ -667,7 +706,7 @@ def adfs?
expect { subject.info }.to raise_error(JWT::ExpiredSignature)
end
end # "context 'with an expired token' do"
end # "describe 'raw_info' do"
end # "describe 'raw_info and validation' do"

describe 'callback_url' do
subject do
Expand Down

0 comments on commit 16c55b0

Please sign in to comment.