Skip to content

Azure v2 Support #30

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 16 additions & 6 deletions lib/omniauth/strategies/azure_oauth2.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
194 changes: 194 additions & 0 deletions spec/omniauth/strategies/azure_oauth2_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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'})
Expand Down