omniauth-microsoft_graph 1.2.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +4 -5
- data/README.md +36 -1
- data/lib/omniauth/microsoft_graph/domain_verifier.rb +99 -0
- data/lib/omniauth/microsoft_graph/version.rb +1 -1
- data/lib/omniauth/microsoft_graph.rb +1 -0
- data/lib/omniauth/strategies/microsoft_graph.rb +14 -3
- data/omniauth-microsoft_graph.gemspec +2 -1
- data/spec/omniauth/microsoft_graph/domain_verifier_spec.rb +113 -0
- data/spec/omniauth/strategies/microsoft_graph_oauth2_spec.rb +12 -1
- metadata +22 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d89d349bdaa2e7c2d75edf01ef55baa73fb647ec0ce79a6542ad946e84f6cfe4
|
4
|
+
data.tar.gz: 7d1f758e047e86b318f8d71007d3ba5735b771a1075726c49b8a2e95bc7cbdff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: afdcf7236c17dc9a213c64a44b7dc8a81e6ee46bd34696ad3889ef9207066eb46b51a8efe5cf88612975356d8d126b24714f0f0bdf8a4e3fad216eeb26b34b8c
|
7
|
+
data.tar.gz: a6f547877dacd8c7dbfcd1f8299a2fc432de9b1712b2bf74f8ae50326c360b524a790f1b63a1f9803795b51c49ff88ac9e4d2f94572454482dc4972f39334a35
|
data/.github/workflows/ruby.yml
CHANGED
@@ -8,24 +8,23 @@
|
|
8
8
|
name: Ruby
|
9
9
|
|
10
10
|
on:
|
11
|
-
push:
|
12
11
|
pull_request:
|
13
12
|
|
14
13
|
jobs:
|
15
14
|
test:
|
16
15
|
|
17
|
-
runs-on: ubuntu-latest
|
18
16
|
strategy:
|
19
17
|
matrix:
|
20
|
-
|
18
|
+
os: [ubuntu-latest, macos-latest]
|
19
|
+
ruby-version: ['3.0', '3.1', '3.2', '3.3']
|
20
|
+
runs-on: ${{ matrix.os }}
|
21
21
|
|
22
22
|
steps:
|
23
23
|
- uses: actions/checkout@v2
|
24
24
|
- name: Set up Ruby
|
25
25
|
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
26
26
|
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
27
|
-
|
28
|
-
uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
|
27
|
+
uses: ruby/setup-ruby@v1
|
29
28
|
with:
|
30
29
|
ruby-version: ${{ matrix.ruby-version }}
|
31
30
|
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
data/README.md
CHANGED
@@ -22,6 +22,8 @@ Or install it yourself as:
|
|
22
22
|
|
23
23
|
## Usage
|
24
24
|
|
25
|
+
Register a new app in the [Azure Portal / App registrations](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade) to get the `AZURE_APPLICATION_CLIENT_ID` and `AZURE_APPLICATION_CLIENT_SECRET` below.
|
26
|
+
|
25
27
|
#### Configuration
|
26
28
|
```ruby
|
27
29
|
Rails.application.config.middleware.use OmniAuth::Builder do
|
@@ -30,10 +32,43 @@ end
|
|
30
32
|
```
|
31
33
|
|
32
34
|
#### Login Hint
|
33
|
-
Just add {login_hint: "email@example.com"} to your url generation to form:
|
35
|
+
Just add `{login_hint: "email@example.com"}` to your url generation to form:
|
34
36
|
```ruby
|
35
37
|
/auth/microsoft_graph?login_hint=email@example.com
|
36
38
|
```
|
39
|
+
|
40
|
+
#### Domain Verification
|
41
|
+
Because Microsoft allows users to set vanity emails on their accounts, the value of the user's "email" doesn't establish membership in that domain. Put another way, user malicious@hacker.biz can edit their email in Active Directory to ceo@yourcompany.com, and (depending on your auth implementation) may be able to log in automatically as that user.
|
42
|
+
|
43
|
+
To establish membership in the claimed email domain, we use two strategies:
|
44
|
+
|
45
|
+
* `email` domain matches `userPrincipalName` domain (which by definition is a verified domain)
|
46
|
+
* The user's `id_token` includes the `xms_edov` ("Email Domain Ownership Verified") claim, with a truthy value
|
47
|
+
|
48
|
+
The `xms_edov` claim is [optional](https://github.com/MicrosoftDocs/azure-docs/issues/111425), and must be configured in the Azure console before it's available in the token. Refer to [Clerk's guide](https://clerk.com/docs/authentication/social-connections/microsoft#stay-secure-against-the-n-o-auth-vulnerability) for instructions on configuring the claim.
|
49
|
+
|
50
|
+
If you're not able or don't need to support domain verification, you can bypass for an individual domain:
|
51
|
+
```ruby
|
52
|
+
Rails.application.config.middleware.use OmniAuth::Builder do
|
53
|
+
provider :microsoft_graph,
|
54
|
+
ENV['AZURE_APPLICATION_CLIENT_ID'],
|
55
|
+
ENV['AZURE_APPLICATION_CLIENT_SECRET'],
|
56
|
+
skip_domain_verification: %w[contoso.com]
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
Or, you can disable domain verification entirely. We *strongly recommend* that you do *not* disable domain verification if at all possible.
|
61
|
+
```ruby
|
62
|
+
Rails.application.config.middleware.use OmniAuth::Builder do
|
63
|
+
provider :microsoft_graph,
|
64
|
+
ENV['AZURE_APPLICATION_CLIENT_ID'],
|
65
|
+
ENV['AZURE_APPLICATION_CLIENT_SECRET'],
|
66
|
+
skip_domain_verification: true
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
[nOAuth: How Microsoft OAuth Misconfiguration Can Lead to Full Account Takeover](https://www.descope.com/blog/post/noauth) from [Descope](https://www.descope.com/)
|
71
|
+
|
37
72
|
### Upgrading to 1.0.0
|
38
73
|
This version requires OmniAuth v2. If you are using Rails, you will need to include or upgrade `omniauth-rails_csrf_protection`. If you upgrade and get an error in your logs complaining about "authenticity error" or similiar, make sure to do `bundle update omniauth-rails_csrf_protection`
|
39
74
|
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'jwt' # for token signature validation
|
3
|
+
require 'omniauth' # to inherit from OmniAuth::Error
|
4
|
+
require 'oauth2' # to rescue OAuth2::Error
|
5
|
+
|
6
|
+
module OmniAuth
|
7
|
+
module MicrosoftGraph
|
8
|
+
# Verify user email domains to mitigate the nOAuth vulnerability
|
9
|
+
# https://www.descope.com/blog/post/noauth
|
10
|
+
# https://clerk.com/docs/authentication/social-connections/microsoft#stay-secure-against-the-n-o-auth-vulnerability
|
11
|
+
OIDC_CONFIG_URL = 'https://login.microsoftonline.com/organizations/v2.0/.well-known/openid-configuration'
|
12
|
+
COMMON_JWKS_URL = 'https://login.microsoftonline.com/common/discovery/v2.0/keys'
|
13
|
+
|
14
|
+
class DomainVerificationError < OmniAuth::Error; end
|
15
|
+
|
16
|
+
class DomainVerifier
|
17
|
+
def self.verify!(auth_hash, access_token, options)
|
18
|
+
new(auth_hash, access_token, options).verify!
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(auth_hash, access_token, options)
|
22
|
+
@email_domain = auth_hash['info']['email']&.split('@')&.last
|
23
|
+
@upn_domain = auth_hash['extra']['raw_info']['userPrincipalName']&.split('@')&.last
|
24
|
+
@access_token = access_token
|
25
|
+
@id_token = access_token.params['id_token']
|
26
|
+
@skip_verification = options[:skip_domain_verification]
|
27
|
+
end
|
28
|
+
|
29
|
+
def verify!
|
30
|
+
# The userPrincipalName property is mutable, but must always contain a
|
31
|
+
# verified domain:
|
32
|
+
#
|
33
|
+
# "The general format is alias@domain, where domain must be present in
|
34
|
+
# the tenant's collection of verified domains."
|
35
|
+
# https://learn.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0
|
36
|
+
#
|
37
|
+
# This means while it's not suitable for consistently identifying a user
|
38
|
+
# (the domain might change), it is suitable for verifying membership in
|
39
|
+
# a given domain.
|
40
|
+
return true if email_domain == upn_domain ||
|
41
|
+
skip_verification == true ||
|
42
|
+
(skip_verification.is_a?(Array) && skip_verification.include?(email_domain)) ||
|
43
|
+
domain_verified_jwt_claim
|
44
|
+
raise DomainVerificationError, verification_error_message
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
attr_reader :access_token,
|
50
|
+
:email_domain,
|
51
|
+
:id_token,
|
52
|
+
:permitted_domains,
|
53
|
+
:skip_verification,
|
54
|
+
:upn_domain
|
55
|
+
|
56
|
+
# https://learn.microsoft.com/en-us/entra/identity-platform/optional-claims-reference
|
57
|
+
# Microsoft offers an optional claim `xms_edov` that will indicate whether the
|
58
|
+
# user's email domain is part of the organization's verified domains. This has to be
|
59
|
+
# explicitly configured in the app registration.
|
60
|
+
#
|
61
|
+
# To get to it, we need to decode the ID token with the key material from Microsoft's
|
62
|
+
# OIDC configuration endpoint, and inspect it for the claim in question.
|
63
|
+
def domain_verified_jwt_claim
|
64
|
+
oidc_config = access_token.get(OIDC_CONFIG_URL).parsed
|
65
|
+
algorithms = oidc_config['id_token_signing_alg_values_supported']
|
66
|
+
jwks = get_jwks(oidc_config)
|
67
|
+
decoded_token = JWT.decode(id_token, nil, true, algorithms: algorithms, jwks: jwks)
|
68
|
+
xms_edov_valid?(decoded_token)
|
69
|
+
rescue JWT::VerificationError, ::OAuth2::Error
|
70
|
+
false
|
71
|
+
end
|
72
|
+
|
73
|
+
def xms_edov_valid?(decoded_token)
|
74
|
+
# https://github.com/MicrosoftDocs/azure-docs/issues/111425#issuecomment-1761043378
|
75
|
+
# Comments seemed to indicate the value is not consistent
|
76
|
+
['1', 1, 'true', true].include?(decoded_token.first['xms_edov'])
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_jwks(oidc_config)
|
80
|
+
# Depending on the tenant, the JWKS endpoint might be different. We need to
|
81
|
+
# consider both the JWKS from the OIDC configuration and the common JWKS endpoint.
|
82
|
+
oidc_config_jwk_keys = access_token.get(oidc_config['jwks_uri']).parsed[:keys]
|
83
|
+
common_jwk_keys = access_token.get(COMMON_JWKS_URL).parsed[:keys]
|
84
|
+
JWT::JWK::Set.new(oidc_config_jwk_keys + common_jwk_keys)
|
85
|
+
end
|
86
|
+
|
87
|
+
def verification_error_message
|
88
|
+
<<~MSG
|
89
|
+
The email domain '#{email_domain}' is not a verified domain for this Azure AD account.
|
90
|
+
You can either:
|
91
|
+
* Update the user's email to match the principal domain '#{upn_domain}'
|
92
|
+
* Skip verification on the '#{email_domain}' domain (not recommended)
|
93
|
+
* Disable verification with `skip_domain_verification: true` (NOT RECOMMENDED!)
|
94
|
+
Refer to the README for more details.
|
95
|
+
MSG
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -22,6 +22,7 @@ module OmniAuth
|
|
22
22
|
|
23
23
|
option :scope, DEFAULT_SCOPE
|
24
24
|
option :authorized_client_ids, []
|
25
|
+
option :skip_domain_verification, false
|
25
26
|
|
26
27
|
uid { raw_info["id"] }
|
27
28
|
|
@@ -43,6 +44,12 @@ module OmniAuth
|
|
43
44
|
}
|
44
45
|
end
|
45
46
|
|
47
|
+
def auth_hash
|
48
|
+
super.tap do |ah|
|
49
|
+
verify_email(ah, access_token)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
46
53
|
def authorize_params
|
47
54
|
super.tap do |params|
|
48
55
|
options[:authorize_options].each do |k|
|
@@ -54,7 +61,7 @@ module OmniAuth
|
|
54
61
|
|
55
62
|
session['omniauth.state'] = params[:state] if params[:state]
|
56
63
|
end
|
57
|
-
end
|
64
|
+
end
|
58
65
|
|
59
66
|
def raw_info
|
60
67
|
@raw_info ||= access_token.get('https://graph.microsoft.com/v1.0/me').parsed
|
@@ -62,7 +69,7 @@ module OmniAuth
|
|
62
69
|
|
63
70
|
def callback_url
|
64
71
|
options[:callback_url] || full_host + script_name + callback_path
|
65
|
-
end
|
72
|
+
end
|
66
73
|
|
67
74
|
def custom_build_access_token
|
68
75
|
access_token = get_access_token(request)
|
@@ -119,7 +126,11 @@ module OmniAuth
|
|
119
126
|
raw_response = client.request(:get, 'https://graph.microsoft.com/v1.0/me',
|
120
127
|
params: { access_token: access_token }).parsed
|
121
128
|
(raw_response['aud'] == options.client_id) || options.authorized_client_ids.include?(raw_response['aud'])
|
122
|
-
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def verify_email(auth_hash, access_token)
|
132
|
+
OmniAuth::MicrosoftGraph::DomainVerifier.verify!(auth_hash, access_token, options)
|
133
|
+
end
|
123
134
|
end
|
124
135
|
end
|
125
136
|
end
|
@@ -18,9 +18,10 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
+
spec.add_runtime_dependency 'jwt', '~> 2.0'
|
21
22
|
spec.add_runtime_dependency 'omniauth', '~> 2.0'
|
22
23
|
spec.add_runtime_dependency 'omniauth-oauth2', '~> 1.8.0'
|
23
|
-
spec.add_development_dependency "sinatra", '~>
|
24
|
+
spec.add_development_dependency "sinatra", '~> 2.2'
|
24
25
|
spec.add_development_dependency "rake", '~> 12.3.3', '>= 12.3.3'
|
25
26
|
spec.add_development_dependency 'rspec', '~> 3.6'
|
26
27
|
spec.add_development_dependency "mocha", '~> 0'
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'omniauth/microsoft_graph/domain_verifier'
|
5
|
+
|
6
|
+
RSpec.describe OmniAuth::MicrosoftGraph::DomainVerifier do
|
7
|
+
subject(:verifier) { described_class.new(auth_hash, access_token, options) }
|
8
|
+
|
9
|
+
let(:auth_hash) do
|
10
|
+
{
|
11
|
+
'info' => { 'email' => email },
|
12
|
+
'extra' => { 'raw_info' => { 'userPrincipalName' => upn } }
|
13
|
+
}
|
14
|
+
end
|
15
|
+
let(:email) { 'foo@example.com' }
|
16
|
+
let(:upn) { 'bar@hackerman.biz' }
|
17
|
+
let(:options) { { skip_domain_verification: false } }
|
18
|
+
let(:access_token) { double('OAuth2::AccessToken', params: { 'id_token' => id_token }) }
|
19
|
+
let(:id_token) { nil }
|
20
|
+
|
21
|
+
describe '#verify!' do
|
22
|
+
subject(:result) { verifier.verify! }
|
23
|
+
|
24
|
+
context 'when email domain and userPrincipalName domain match' do
|
25
|
+
let(:email) { 'foo@example.com' }
|
26
|
+
let(:upn) { 'bar@example.com' }
|
27
|
+
|
28
|
+
it { is_expected.to be_truthy }
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when domain validation is disabled' do
|
32
|
+
let(:options) { super().merge(skip_domain_verification: true) }
|
33
|
+
|
34
|
+
it { is_expected.to be_truthy }
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when the email domain is explicitly permitted' do
|
38
|
+
let(:options) { super().merge(skip_domain_verification: ['example.com']) }
|
39
|
+
|
40
|
+
it { is_expected.to be_truthy }
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when the ID token indicates domain verification' do
|
44
|
+
let(:mock_oidc_key) do
|
45
|
+
optional_parameters = { kid: 'mock_oidc_key', use: 'sig', alg: 'RS256' }
|
46
|
+
JWT::JWK.new(OpenSSL::PKey::RSA.new(2048), optional_parameters)
|
47
|
+
end
|
48
|
+
|
49
|
+
let(:mock_common_key) do
|
50
|
+
optional_parameters = { kid: 'mock_common_key', use: 'sig', alg: 'RS256' }
|
51
|
+
JWT::JWK.new(OpenSSL::PKey::RSA.new(2048), optional_parameters)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Mock the API responses to return the mock keys
|
55
|
+
before do
|
56
|
+
allow(access_token).to receive(:get)
|
57
|
+
.with(OmniAuth::MicrosoftGraph::OIDC_CONFIG_URL)
|
58
|
+
.and_return(
|
59
|
+
double(
|
60
|
+
'OAuth2::Response',
|
61
|
+
parsed: {
|
62
|
+
'id_token_signing_alg_values_supported' => ['RS256'],
|
63
|
+
'jwks_uri' => 'https://example.com/jwks-keys',
|
64
|
+
}
|
65
|
+
)
|
66
|
+
)
|
67
|
+
allow(access_token).to receive(:get)
|
68
|
+
.with('https://example.com/jwks-keys')
|
69
|
+
.and_return(
|
70
|
+
double(
|
71
|
+
'OAuth2::Response',
|
72
|
+
parsed: JWT::JWK::Set.new(mock_oidc_key).export
|
73
|
+
)
|
74
|
+
)
|
75
|
+
allow(access_token).to receive(:get)
|
76
|
+
.with(OmniAuth::MicrosoftGraph::COMMON_JWKS_URL)
|
77
|
+
.and_return(
|
78
|
+
double(
|
79
|
+
'OAuth2::Response',
|
80
|
+
parsed: JWT::JWK::Set.new(mock_common_key).export,
|
81
|
+
body: JWT::JWK::Set.new(mock_common_key).export.to_json
|
82
|
+
)
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'when the kid exists in the oidc key' do
|
87
|
+
let(:id_token) do
|
88
|
+
payload = { email: email, xms_edov: true }
|
89
|
+
JWT.encode(payload, mock_oidc_key.signing_key, mock_oidc_key[:alg], kid: mock_oidc_key[:kid])
|
90
|
+
end
|
91
|
+
|
92
|
+
it { is_expected.to be_truthy }
|
93
|
+
end
|
94
|
+
|
95
|
+
context "when the kid exists in the common key" do
|
96
|
+
let(:id_token) do
|
97
|
+
payload = { email: email, xms_edov: true }
|
98
|
+
JWT.encode(payload, mock_common_key.signing_key, mock_common_key[:alg], kid: mock_common_key[:kid])
|
99
|
+
end
|
100
|
+
|
101
|
+
it { is_expected.to be_truthy }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'when all verification strategies fail' do
|
106
|
+
before { allow(access_token).to receive(:get).and_raise(::OAuth2::Error.new('whoops')) }
|
107
|
+
|
108
|
+
it 'raises a DomainVerificationError' do
|
109
|
+
expect { result }.to raise_error OmniAuth::MicrosoftGraph::DomainVerificationError
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -280,6 +280,18 @@ describe OmniAuth::Strategies::MicrosoftGraph do
|
|
280
280
|
end
|
281
281
|
end
|
282
282
|
|
283
|
+
context 'when email verification fails' do
|
284
|
+
let(:response_hash) { { mail: 'something@domain.invalid' } }
|
285
|
+
let(:error) { OmniAuth::MicrosoftGraph::DomainVerificationError.new }
|
286
|
+
|
287
|
+
before do
|
288
|
+
allow(OmniAuth::MicrosoftGraph::DomainVerifier).to receive(:verify!).and_raise(error)
|
289
|
+
end
|
290
|
+
|
291
|
+
it 'raises an error' do
|
292
|
+
expect { subject.auth_hash }.to raise_error error
|
293
|
+
end
|
294
|
+
end
|
283
295
|
end
|
284
296
|
|
285
297
|
describe '#extra' do
|
@@ -445,5 +457,4 @@ describe OmniAuth::Strategies::MicrosoftGraph do
|
|
445
457
|
end.to raise_error(OAuth2::Error)
|
446
458
|
end
|
447
459
|
end
|
448
|
-
|
449
460
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: omniauth-microsoft_graph
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Philips
|
@@ -9,8 +9,22 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2024-06-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: jwt
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '2.0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '2.0'
|
14
28
|
- !ruby/object:Gem::Dependency
|
15
29
|
name: omniauth
|
16
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -45,14 +59,14 @@ dependencies:
|
|
45
59
|
requirements:
|
46
60
|
- - "~>"
|
47
61
|
- !ruby/object:Gem::Version
|
48
|
-
version: '
|
62
|
+
version: '2.2'
|
49
63
|
type: :development
|
50
64
|
prerelease: false
|
51
65
|
version_requirements: !ruby/object:Gem::Requirement
|
52
66
|
requirements:
|
53
67
|
- - "~>"
|
54
68
|
- !ruby/object:Gem::Version
|
55
|
-
version: '
|
69
|
+
version: '2.2'
|
56
70
|
- !ruby/object:Gem::Dependency
|
57
71
|
name: rake
|
58
72
|
requirement: !ruby/object:Gem::Requirement
|
@@ -119,10 +133,12 @@ files:
|
|
119
133
|
- Rakefile
|
120
134
|
- example/example.rb
|
121
135
|
- lib/omniauth/microsoft_graph.rb
|
136
|
+
- lib/omniauth/microsoft_graph/domain_verifier.rb
|
122
137
|
- lib/omniauth/microsoft_graph/version.rb
|
123
138
|
- lib/omniauth/strategies/microsoft_graph.rb
|
124
139
|
- lib/omniauth_microsoft_graph.rb
|
125
140
|
- omniauth-microsoft_graph.gemspec
|
141
|
+
- spec/omniauth/microsoft_graph/domain_verifier_spec.rb
|
126
142
|
- spec/omniauth/strategies/microsoft_graph_oauth2_spec.rb
|
127
143
|
- spec/spec_helper.rb
|
128
144
|
homepage: https://github.com/synth/omniauth-microsoft_graph
|
@@ -144,10 +160,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
144
160
|
- !ruby/object:Gem::Version
|
145
161
|
version: '0'
|
146
162
|
requirements: []
|
147
|
-
rubygems_version: 3.
|
163
|
+
rubygems_version: 3.3.26
|
148
164
|
signing_key:
|
149
165
|
specification_version: 4
|
150
166
|
summary: omniauth provider for Microsoft Graph
|
151
167
|
test_files:
|
168
|
+
- spec/omniauth/microsoft_graph/domain_verifier_spec.rb
|
152
169
|
- spec/omniauth/strategies/microsoft_graph_oauth2_spec.rb
|
153
170
|
- spec/spec_helper.rb
|