diff --git a/README.md b/README.md index 3313dba..97e1bd1 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,25 @@ use OmniAuth::Builder do end ``` +## Azure v2 Endpoints + +The Azure `v1` endpoints are used by default. To use the `v2` endpoints set the `v2` option to `true`. Ex: + +```ruby +use OmniAuth::Builder do + provider :azure_oauth2, + { + client_id: ENV['AZURE_CLIENT_ID'], + client_secret: ENV['AZURE_CLIENT_SECRET'], + tenant_id: ENV['AZURE_TENANT_ID'], + v2: true + } +end +``` + +A scope must be set for the `v2` endpoints to work properly. The default scope is `User.Read`, which is automatically set. +To use different scopes, set the scope option in the config. It currently isn't designed to be dynamic. + ## Contributing 1. Fork it diff --git a/lib/omniauth/strategies/azure_oauth2.rb b/lib/omniauth/strategies/azure_oauth2.rb index 879d39f..1d74176 100644 --- a/lib/omniauth/strategies/azure_oauth2.rb +++ b/lib/omniauth/strategies/azure_oauth2.rb @@ -13,7 +13,10 @@ class AzureOauth2 < OmniAuth::Strategies::OAuth2 # AD resource identifier option :resource, '00000002-0000-0000-c000-000000000000' - # tenant_provider must return client_id, client_secret and optionally tenant_id and base_azure_url + # AD default scope + option :scope, 'User.Read' + + # tenant_provider must return client_id, client_secret and optionally tenant_id, base_azure_url, v2, and scope args [:tenant_provider] def client @@ -29,17 +32,19 @@ def client provider.respond_to?(:tenant_id) ? provider.tenant_id : 'common' options.base_azure_url = provider.respond_to?(:base_azure_url) ? provider.base_azure_url : BASE_AZURE_URL + options.v2 = provider.respond_to?(:v2) && provider.v2 ? '/v2.0' : '' + options.uid_claim = provider.respond_to?(:uid_claim) ? provider.uid_claim : 'sub' options.authorize_params = provider.authorize_params if provider.respond_to?(:authorize_params) options.authorize_params.domain_hint = provider.domain_hint if provider.respond_to?(:domain_hint) && provider.domain_hint options.authorize_params.prompt = request.params['prompt'] if defined? request && request.params['prompt'] - options.client_options.authorize_url = "#{options.base_azure_url}/#{options.tenant_id}/oauth2/authorize" - options.client_options.token_url = "#{options.base_azure_url}/#{options.tenant_id}/oauth2/token" + options.client_options.authorize_url = "#{options.base_azure_url}/#{options.tenant_id}/oauth2#{options.v2}/authorize" + options.client_options.token_url = "#{options.base_azure_url}/#{options.tenant_id}/oauth2#{options.v2}/token" super end uid { - raw_info['sub'] + raw_info[options.uid_claim] } info do @@ -55,8 +60,13 @@ def client end def token_params - azure_resource = request.env['omniauth.params'] && request.env['omniauth.params']['azure_resource'] - super.merge(resource: azure_resource || options.resource) + if options.v2 == '/v2.0' + azure_scope = request.env['omniauth.params'] && request.env['omniauth.params']['azure_scope'] + super.merge(scope: azure_scope || options.scope) + else + azure_resource = request.env['omniauth.params'] && request.env['omniauth.params']['azure_resource'] + super.merge(resource: azure_resource || options.resource) + end end def callback_url diff --git a/spec/omniauth/strategies/azure_oauth2_spec.rb b/spec/omniauth/strategies/azure_oauth2_spec.rb index d171d88..66f2241 100644 --- a/spec/omniauth/strategies/azure_oauth2_spec.rb +++ b/spec/omniauth/strategies/azure_oauth2_spec.rb @@ -119,6 +119,83 @@ module JWT; end end end + describe 'static common configuration v2' do + let(:options) { @options || {} } + subject do + OmniAuth::Strategies::AzureOauth2.new(app, {client_id: 'id', client_secret: 'secret', v2: true}.merge(options)) + end + + before do + allow(subject).to receive(:request) { request } + end + + describe '#client' do + it 'has correct authorize url' do + expect(subject.client.options[:authorize_url]).to eql('https://login.microsoftonline.com/common/oauth2/v2.0/authorize') + end + + it 'has correct token url' do + expect(subject.client.options[:token_url]).to eql('https://login.microsoftonline.com/common/oauth2/v2.0/token') + end + + it 'has correct authorize params' do + subject.client + expect(subject.authorize_params[:scope]).to eql('User.Read') + expect(subject.authorize_params[:resource]).to be_nil + end + + it 'has correct token params' do + subject.client + expect(subject.token_params[:scope]).to eql('User.Read') + expect(subject.token_params[:resource]).to be_nil + end + + it 'has correct uid claim' do + subject.client + expect(subject.options[:uid_claim]).to eql('sub') + end + end + end + + describe 'static custom configuration v2' do + let(:options) { @options || {} } + subject do + OmniAuth::Strategies::AzureOauth2.new(app, {client_id: 'id', client_secret: 'secret', v2: true, uid_claim: 'oid'}.merge(options)) + end + + before do + allow(subject).to receive(:request) { request } + end + + describe '#client' do + it 'has correct authorize url' do + expect(subject.client.options[:authorize_url]).to eql('https://login.microsoftonline.com/common/oauth2/v2.0/authorize') + end + + it 'has correct token url' do + expect(subject.client.options[:token_url]).to eql('https://login.microsoftonline.com/common/oauth2/v2.0/token') + end + + it 'has correct authorize params' do + subject.client + puts subject.authorize_params + expect(subject.authorize_params[:scope]).to eql('User.Read') + expect(subject.authorize_params[:resource]).to be_nil + end + + it 'has correct token params' do + subject.client + expect(subject.token_params[:scope]).to eql('User.Read') + expect(subject.token_params[:resource]).to be_nil + end + + it 'has correct uid claim' do + subject.client + expect(subject.options[:uid_claim]).to eql('oid') + end + end + end + describe 'dynamic configuration' do let(:provider_klass) { Class.new { @@ -281,6 +358,123 @@ def client_secret end end + describe 'dynamic common configuration v2' do + let(:provider_klass) { + Class.new { + def initialize(strategy) + end + + def client_id + 'id' + end + + def client_secret + 'secret' + end + + def v2 + true + end + } + } + + subject do + OmniAuth::Strategies::AzureOauth2.new(app, provider_klass) + end + + before do + allow(subject).to receive(:request) { request } + end + + describe '#client' do + it 'has correct authorize url' do + expect(subject.client.options[:authorize_url]).to eql('https://login.microsoftonline.com/common/oauth2/v2.0/authorize') + end + + it 'has correct token url' do + expect(subject.client.options[:token_url]).to eql('https://login.microsoftonline.com/common/oauth2/v2.0/token') + end + + it 'has correct authorize params' do + subject.client + expect(subject.authorize_params[:scope]).to eql('User.Read') + expect(subject.authorize_params[:resource]).to be_nil + end + + it 'has correct token params' do + subject.client + expect(subject.token_params[:scope]).to eql('User.Read') + expect(subject.token_params[:resource]).to be_nil + end + + it 'has correct uid claim' do + subject.client + expect(subject.options[:uid_claim]).to eql('sub') + end + end + end + + describe 'dynamic custom configuration v2' do + let(:provider_klass) { + Class.new { + def initialize(strategy) + end + + def client_id + 'id' + end + + def client_secret + 'secret' + end + + def v2 + true + end + + def uid_claim + 'oid' + end + } + } + + subject do + OmniAuth::Strategies::AzureOauth2.new(app, provider_klass) + end + + before do + allow(subject).to receive(:request) { request } + end + + describe '#client' do + it 'has correct authorize url' do + expect(subject.client.options[:authorize_url]).to eql('https://login.microsoftonline.com/common/oauth2/v2.0/authorize') + end + + it 'has correct token url' do + expect(subject.client.options[:token_url]).to eql('https://login.microsoftonline.com/common/oauth2/v2.0/token') + end + + it 'has correct authorize params' do + subject.client + puts subject.authorize_params + expect(subject.authorize_params[:scope]).to eql('User.Read') + expect(subject.authorize_params[:resource]).to be_nil + end + + it 'has correct token params' do + subject.client + expect(subject.token_params[:scope]).to eql('User.Read') + expect(subject.token_params[:resource]).to be_nil + end + + it 'has correct uid claim' do + subject.client + expect(subject.options[:uid_claim]).to eql('oid') + end + end + end + describe "raw_info" do subject do OmniAuth::Strategies::AzureOauth2.new(app, {client_id: 'id', client_secret: 'secret'})