omniauth-azure-devops 1.0.8 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b49d7b12fbd7a19f157141da1e8b5bcb79a5e99eb4f315add5b4089e36edc579
4
- data.tar.gz: 7b148fc02e6b8313e2ba3d29b6d6ff773b161ddd077684933d825347d436c6ae
3
+ metadata.gz: 544262ba6729895bcbcb165b305e95674278a1cc32589fa6402fffe5fc542cec
4
+ data.tar.gz: f503a152f1220753813fbb49a8d61a164b4ed81b791f001a429ebf4bcf73d651
5
5
  SHA512:
6
- metadata.gz: 0006ead27889ce6ea0b472d4c0f2b632ca994a6b078664afb9bfa59262faa6d3c1109e5ca50f606be2619962520a9456f828c25c04156d6f1bc1f4f09b6ef60a
7
- data.tar.gz: cbfe15cc8df7aa199ce1562ad2441de69e55ae22535cf2480f1894fe1e3043ee33bd1c552793bdabc815c487bb3d8cd2a4d4295404e98c7985823151f95429a9
6
+ metadata.gz: b0f6aa4de3c127933424bc9f72fe0b99ccc5ba13ac01158739dbece602ef0a22e12e1f6a684d746a8146461034a9d5f2787cc6470096106f637415f054b41477
7
+ data.tar.gz: 7e74f1e5c66372654886c8a631e8d294ad5d3dce614203f354b0c2ac9563de29936da7e8640ccd8792087c41b394deae5ac5002da381cc5f7bc6557ff17d3eaf
data/.rubocop.yml CHANGED
@@ -46,6 +46,9 @@ Metrics/PerceivedComplexity:
46
46
  Enabled: false
47
47
 
48
48
  # Naming cop configuration
49
+ Naming/FileName:
50
+ Exclude:
51
+ - lib/omniauth-azure-devops.rb
49
52
  Naming/MethodParameterName:
50
53
  Enabled: true
51
54
  Exclude:
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.1.0]
4
+
5
+ - Add the `azure_devops_entra` strategy: Microsoft Entra ID authorization code + PKCE with a certificate-signed client assertion, alongside the unchanged legacy `azure_devops` strategy
6
+ - Require `omniauth-oauth2` `~> 1.7` (PKCE) and `oauth2` `~> 2.0` (`private_key_jwt` client auth and snake_cased responses), and declare the `jwt` dependency
7
+
3
8
  ## [1.0.8]
4
9
 
5
10
  - Address Dependabot security advisories in transitive dependencies by updating `Gemfile.lock`:
data/Gemfile.lock CHANGED
@@ -1,9 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- omniauth-azure-devops (1.0.8)
4
+ omniauth-azure-devops (1.1.0)
5
+ jwt (>= 2.10, < 4)
6
+ oauth2 (~> 2.0)
5
7
  omniauth (>= 1, < 3)
6
- omniauth-oauth2 (~> 1.1)
8
+ omniauth-oauth2 (~> 1.7)
7
9
 
8
10
  GEM
9
11
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -34,6 +34,22 @@ bundle install
34
34
 
35
35
  5. Access user information and tokens as needed in your application by utilizing the OmniAuth authentication data.
36
36
 
37
+ ### Microsoft Entra ID
38
+
39
+ The `azure_devops_entra` strategy signs users in through a multi-tenant Microsoft Entra ID application (authorization code + PKCE) and authenticates the code exchange with a certificate-signed client assertion, so no client secret is used.
40
+
41
+ ```ruby
42
+ provider :azure_devops_entra, ENV['ENTRA_APP_ID'], nil,
43
+ client_private_key: ENV['ENTRA_CLIENT_PRIVATE_KEY'],
44
+ certificate_thumbprint: ENV['ENTRA_CERTIFICATE_THUMBPRINT'],
45
+ scope: '499b84ac-1321-427f-aa17-267ca6975798/.default offline_access',
46
+ callback_path: '/link/azure_devops_entra/oauth_callback'
47
+ ```
48
+
49
+ - `client_private_key` is the PEM private key of the certificate registered on the Entra application, and `certificate_thumbprint` is its base64url-encoded SHA-1 thumbprint (the JWT `x5t` header).
50
+ - `scope` is sent exactly as given. Include `offline_access`, or Entra issues no refresh token.
51
+ - The auth hash `uid` is the Azure DevOps profile id, the same value the `azure_devops` strategy returns. `extra.tenant_id` is the user's Entra tenant id, which is needed to refresh the token later at `https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token`.
52
+
37
53
  ## Running Tests
38
54
 
39
55
  You can run the test suite using the following command:
@@ -2,6 +2,6 @@
2
2
 
3
3
  module OmniAuth
4
4
  module AzureDevops
5
- VERSION = '1.0.8'
5
+ VERSION = '1.1.0'
6
6
  end
7
7
  end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jwt'
4
+ require 'openssl'
5
+ require 'securerandom'
6
+ require 'omniauth/strategies/oauth2'
7
+
8
+ module OmniAuth
9
+ module Strategies
10
+ class AzureDevopsEntra < OmniAuth::Strategies::OAuth2
11
+ TOKEN_URL = 'https://login.microsoftonline.com/organizations/oauth2/v2.0/token'
12
+ CLIENT_ASSERTION_TYPE = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
13
+ CLIENT_ASSERTION_LIFETIME_SECONDS = 120
14
+
15
+ option :name, :azure_devops_entra
16
+ option :pkce, true
17
+ option :client_private_key, nil
18
+ option :certificate_thumbprint, nil
19
+
20
+ option :client_options, {
21
+ site: 'https://app.vssps.visualstudio.com',
22
+ authorize_url: 'https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize',
23
+ token_url: TOKEN_URL,
24
+ auth_scheme: :private_key_jwt
25
+ }
26
+
27
+ option :authorize_params, {
28
+ prompt: 'select_account'
29
+ }
30
+
31
+ # Must stay the ADO profile id, never the token's `oid` claim: they are different GUIDs, and rewind-app finds the
32
+ # existing service instance by `platform_id: uid`, so an `oid` here silently creates a second instance.
33
+ uid { raw_info['id'] }
34
+
35
+ info do
36
+ {
37
+ name: raw_info['display_name'],
38
+ email_address: raw_info['email_address']
39
+ }
40
+ end
41
+
42
+ extra do
43
+ {
44
+ raw_info: raw_info,
45
+ tenant_id: tenant_id
46
+ }
47
+ end
48
+
49
+ def raw_info
50
+ @raw_info ||= access_token.get('/_apis/profile/profiles/me', params: { 'api-version' => '7.1' }).parsed
51
+ end
52
+
53
+ def tenant_id
54
+ @tenant_id ||= JWT.decode(access_token.token, nil, false).first['tid']
55
+ end
56
+
57
+ def token_params
58
+ super.tap do |params|
59
+ params[:client_id] = client.id
60
+ params[:client_assertion_type] = CLIENT_ASSERTION_TYPE
61
+ params[:client_assertion] = client_assertion
62
+ end
63
+ end
64
+
65
+ def callback_url
66
+ full_host + callback_path
67
+ end
68
+
69
+ private
70
+
71
+ def client_assertion
72
+ now = Time.now.to_i
73
+ claims = {
74
+ iss: client.id,
75
+ sub: client.id,
76
+ aud: TOKEN_URL,
77
+ jti: SecureRandom.uuid,
78
+ exp: now + CLIENT_ASSERTION_LIFETIME_SECONDS,
79
+ nbf: now,
80
+ iat: now
81
+ }
82
+
83
+ JWT.encode(claims, OpenSSL::PKey::RSA.new(options.client_private_key), 'RS256',
84
+ { alg: 'RS256', typ: 'JWT', x5t: options.certificate_thumbprint })
85
+ end
86
+ end
87
+ end
88
+ end
@@ -2,3 +2,4 @@
2
2
 
3
3
  require 'omni_auth/azure_devops/version'
4
4
  require 'omni_auth/strategies/azure_devops'
5
+ require 'omni_auth/strategies/azure_devops_entra'
@@ -28,6 +28,8 @@ Gem::Specification.new do |gem|
28
28
 
29
29
  gem.required_ruby_version = '>= 3.4'
30
30
 
31
+ gem.add_dependency 'jwt', '>= 2.10', '< 4'
32
+ gem.add_dependency 'oauth2', '~> 2.0'
31
33
  gem.add_dependency 'omniauth', '>= 1', '< 3'
32
- gem.add_dependency 'omniauth-oauth2', '~> 1.1'
34
+ gem.add_dependency 'omniauth-oauth2', '~> 1.7'
33
35
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omniauth-azure-devops
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.8
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rewind Software Inc. | Parth Chandgadhiya
@@ -9,6 +9,40 @@ bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: jwt
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '2.10'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '4'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '2.10'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '4'
32
+ - !ruby/object:Gem::Dependency
33
+ name: oauth2
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - "~>"
37
+ - !ruby/object:Gem::Version
38
+ version: '2.0'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - "~>"
44
+ - !ruby/object:Gem::Version
45
+ version: '2.0'
12
46
  - !ruby/object:Gem::Dependency
13
47
  name: omniauth
14
48
  requirement: !ruby/object:Gem::Requirement
@@ -35,14 +69,14 @@ dependencies:
35
69
  requirements:
36
70
  - - "~>"
37
71
  - !ruby/object:Gem::Version
38
- version: '1.1'
72
+ version: '1.7'
39
73
  type: :runtime
40
74
  prerelease: false
41
75
  version_requirements: !ruby/object:Gem::Requirement
42
76
  requirements:
43
77
  - - "~>"
44
78
  - !ruby/object:Gem::Version
45
- version: '1.1'
79
+ version: '1.7'
46
80
  description: An Azure Devops OAuth strategy for OmniAuth 2.0
47
81
  email:
48
82
  - team@rewind.io
@@ -64,6 +98,7 @@ files:
64
98
  - bin/setup
65
99
  - lib/omni_auth/azure_devops/version.rb
66
100
  - lib/omni_auth/strategies/azure_devops.rb
101
+ - lib/omni_auth/strategies/azure_devops_entra.rb
67
102
  - lib/omniauth-azure-devops.rb
68
103
  - omniauth-azure-devops.gemspec
69
104
  homepage: https://github.com/rewindio/omniauth-azure-devops