omniauth-entra_id_jwt 0.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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/CODE_OF_CONDUCT.md +132 -0
  4. data/LICENSE +21 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +252 -0
  7. data/Rakefile +12 -0
  8. data/coverage/.last_run.json +5 -0
  9. data/coverage/.resultset.json +155 -0
  10. data/coverage/.resultset.json.lock +0 -0
  11. data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_asc.png +0 -0
  12. data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
  13. data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_both.png +0 -0
  14. data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_desc.png +0 -0
  15. data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
  16. data/coverage/assets/0.13.1/application.css +1 -0
  17. data/coverage/assets/0.13.1/application.js +7 -0
  18. data/coverage/assets/0.13.1/colorbox/border.png +0 -0
  19. data/coverage/assets/0.13.1/colorbox/controls.png +0 -0
  20. data/coverage/assets/0.13.1/colorbox/loading.gif +0 -0
  21. data/coverage/assets/0.13.1/colorbox/loading_background.png +0 -0
  22. data/coverage/assets/0.13.1/favicon_green.png +0 -0
  23. data/coverage/assets/0.13.1/favicon_red.png +0 -0
  24. data/coverage/assets/0.13.1/favicon_yellow.png +0 -0
  25. data/coverage/assets/0.13.1/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  26. data/coverage/assets/0.13.1/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  27. data/coverage/assets/0.13.1/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  28. data/coverage/assets/0.13.1/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  29. data/coverage/assets/0.13.1/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  30. data/coverage/assets/0.13.1/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  31. data/coverage/assets/0.13.1/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  32. data/coverage/assets/0.13.1/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  33. data/coverage/assets/0.13.1/images/ui-icons_222222_256x240.png +0 -0
  34. data/coverage/assets/0.13.1/images/ui-icons_2e83ff_256x240.png +0 -0
  35. data/coverage/assets/0.13.1/images/ui-icons_454545_256x240.png +0 -0
  36. data/coverage/assets/0.13.1/images/ui-icons_888888_256x240.png +0 -0
  37. data/coverage/assets/0.13.1/images/ui-icons_cd0a0a_256x240.png +0 -0
  38. data/coverage/assets/0.13.1/loading.gif +0 -0
  39. data/coverage/assets/0.13.1/magnify.png +0 -0
  40. data/coverage/index.html +1700 -0
  41. data/lib/omniauth/entra_id_jwt/version.rb +11 -0
  42. data/lib/omniauth/entra_id_jwt.rb +2 -0
  43. data/lib/omniauth/strategies/entra_id_jwt.rb +138 -0
  44. data/lib/omniauth-entra_id_jwt.rb +1 -0
  45. data/sig/omniauth/entra/id/jwt.rbs +10 -0
  46. metadata +157 -0
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAuth
4
+ module Entra
5
+ module Id
6
+ module JWT
7
+ VERSION = "0.1.0"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,2 @@
1
+ require File.join("omniauth", "entra_id_jwt", "version")
2
+ require File.join("omniauth", "strategies", "entra_id_jwt")
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "omniauth-oauth2"
4
+
5
+ module OmniAuth
6
+ module Strategies
7
+ class EntraIdJWT < OmniAuth::Strategies::OAuth2
8
+ BASE_URL = "https://login.microsoftonline.com"
9
+
10
+ option :name, "entra_id_jwt"
11
+ option :tenant_provider, nil
12
+
13
+ DEFAULT_SCOPE = "openid profile email"
14
+ COMMON_TENANT_ID = "common"
15
+
16
+ # The tenant_provider must return client_id, client_secret and,
17
+ # optionally, tenant_id and base_url.
18
+ #
19
+ args [:tenant_provider]
20
+
21
+ def client
22
+ provider = options.tenant_provider ? options.tenant_provider.new(self) : options
23
+ options.client_id = provider.client_id
24
+
25
+ unless provider.respond_to?(:client_secret) && provider.client_secret
26
+ raise ArgumentError, "You must provide client_secret"
27
+ end
28
+
29
+ options.client_secret = provider.client_secret
30
+
31
+ tenant_id = provider.respond_to?(:tenant_id) ? provider.tenant_id : COMMON_TENANT_ID
32
+ base_url = provider.respond_to?(:base_url) ? provider.base_url : BASE_URL
33
+
34
+ options.authorize_params = provider.authorize_params if provider.respond_to?(:authorize_params)
35
+ options.token_params.scope = if defined?(request) && request.params["scope"]
36
+ request.params["scope"]
37
+ elsif provider.respond_to?(:scope) && provider.scope
38
+ provider.scope
39
+ else
40
+ DEFAULT_SCOPE
41
+ end
42
+ oauth2 = "oauth2/v2.0"
43
+
44
+ tenanted_endpoint_base_url = "#{base_url}/#{tenant_id}"
45
+
46
+ options.client_options.authorize_url = "#{tenanted_endpoint_base_url}/#{oauth2}/authorize"
47
+ options.client_options.token_url = "#{tenanted_endpoint_base_url}/#{oauth2}/token"
48
+
49
+ # On Behalf Of flow requires the default as the grant_type, so should only need to be configured
50
+ # in the provider (if at all).
51
+ options.token_params.grant_type = if provider.respond_to?(:grant_type) && provider.grant_type
52
+ provider.grant_type
53
+ else
54
+ grant_type
55
+ end
56
+
57
+ # On Behalf Of flow requires the default as the requested_token_use, so should only need to be configured
58
+ # in the provider (if at all).
59
+ options.token_params.requested_token_use = if provider.respond_to?(:requested_token_use) && provider.requested_token_use
60
+ provider.requested_token_use
61
+ else
62
+ requested_token_use
63
+ end
64
+
65
+ super
66
+ end
67
+
68
+ uid do
69
+ raw_info["userPrincipalName"]
70
+ end
71
+
72
+ # As per omniauth-microsoft_graph definition
73
+ # https://github.com/omniauth/omniauth/wiki/Auth-Hash-Schema
74
+ info do
75
+ {
76
+ "name" => [raw_info["givenName"], raw_info["surname"]].join(' '),
77
+ "email" => raw_info["mail"],
78
+ "first_name" => raw_info["givenName"],
79
+ "last_name" => raw_info["surname"],
80
+ "nickname" => raw_info["displayName"],
81
+ "phone" => raw_info["mobilePhone"]
82
+ }
83
+ end
84
+
85
+
86
+ # Although the only extra referred to in https://github.com/omniauth/omniauth/wiki/Auth-Hash-Schema
87
+ # is raw_info, include other useful information such as tenant identifier as other keys.
88
+ # Probably best not to merge them all into the raw_info key, so they remain different.
89
+ extra do
90
+ {
91
+ 'raw_info' => raw_info,
92
+ 'params' => access_token.params,
93
+ 'aud' => options.client_id,
94
+ 'tid' => @jwt_data["tid"]
95
+ }
96
+ end
97
+
98
+ def callback_url
99
+ raise NotImplementedError, "Callback URL is not supported in on behalf of flow"
100
+ end
101
+
102
+ def raw_info
103
+ @raw_info = access_token.request(:get, "https://graph.microsoft.com/v1.0/me").parsed if @raw_info.nil?
104
+ @raw_info
105
+ end
106
+
107
+ def callback_phase
108
+ options.provider_ignores_state = true
109
+ super
110
+ end
111
+
112
+ protected
113
+
114
+ def grant_type
115
+ "urn:ietf:params:oauth:grant-type:jwt-bearer"
116
+ end
117
+
118
+ def requested_token_use
119
+ "on_behalf_of"
120
+ end
121
+
122
+ # This requires examining the request body, as it is almost definitely delivered as
123
+ # ContentType application/json
124
+ def build_access_token
125
+ body = JSON.parse(request.body.read)
126
+ request.body.rewind
127
+ # Need to store the JWT token here, as otherwise the actual tenant (tid)
128
+ # is not found in the subsequent /me API call.
129
+ # Check for tid claim, as it will be useful.
130
+ @jwt_data = JWT.decode(body['code'], nil, false, { required_claims: "tid" })[0]
131
+
132
+ client.get_token({ assertion: body['code'] }.merge(token_params))
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ OmniAuth.config.add_camelization 'entra_id_jwt', 'EntraIdJWT'
@@ -0,0 +1 @@
1
+ require File.join("omniauth", "entra_id_jwt")
@@ -0,0 +1,10 @@
1
+ module OmniAuth
2
+ module Entra
3
+ module Id
4
+ module JWT
5
+ VERSION: String
6
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
7
+ end
8
+ end
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omniauth-entra_id_jwt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Benjamin Elias
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-06-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: omniauth-oauth2
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.8'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.13'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.13'
41
+ - !ruby/object:Gem::Dependency
42
+ name: simplecov
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.22'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.22'
55
+ - !ruby/object:Gem::Dependency
56
+ name: webmock
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.25'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.25'
69
+ - !ruby/object:Gem::Dependency
70
+ name: debug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.0'
83
+ description:
84
+ email:
85
+ - 12136262+collabital@users.noreply.github.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - CHANGELOG.md
91
+ - CODE_OF_CONDUCT.md
92
+ - LICENSE
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - coverage/.last_run.json
97
+ - coverage/.resultset.json
98
+ - coverage/.resultset.json.lock
99
+ - coverage/assets/0.13.1/DataTables-1.10.20/images/sort_asc.png
100
+ - coverage/assets/0.13.1/DataTables-1.10.20/images/sort_asc_disabled.png
101
+ - coverage/assets/0.13.1/DataTables-1.10.20/images/sort_both.png
102
+ - coverage/assets/0.13.1/DataTables-1.10.20/images/sort_desc.png
103
+ - coverage/assets/0.13.1/DataTables-1.10.20/images/sort_desc_disabled.png
104
+ - coverage/assets/0.13.1/application.css
105
+ - coverage/assets/0.13.1/application.js
106
+ - coverage/assets/0.13.1/colorbox/border.png
107
+ - coverage/assets/0.13.1/colorbox/controls.png
108
+ - coverage/assets/0.13.1/colorbox/loading.gif
109
+ - coverage/assets/0.13.1/colorbox/loading_background.png
110
+ - coverage/assets/0.13.1/favicon_green.png
111
+ - coverage/assets/0.13.1/favicon_red.png
112
+ - coverage/assets/0.13.1/favicon_yellow.png
113
+ - coverage/assets/0.13.1/images/ui-bg_flat_0_aaaaaa_40x100.png
114
+ - coverage/assets/0.13.1/images/ui-bg_flat_75_ffffff_40x100.png
115
+ - coverage/assets/0.13.1/images/ui-bg_glass_55_fbf9ee_1x400.png
116
+ - coverage/assets/0.13.1/images/ui-bg_glass_65_ffffff_1x400.png
117
+ - coverage/assets/0.13.1/images/ui-bg_glass_75_dadada_1x400.png
118
+ - coverage/assets/0.13.1/images/ui-bg_glass_75_e6e6e6_1x400.png
119
+ - coverage/assets/0.13.1/images/ui-bg_glass_95_fef1ec_1x400.png
120
+ - coverage/assets/0.13.1/images/ui-bg_highlight-soft_75_cccccc_1x100.png
121
+ - coverage/assets/0.13.1/images/ui-icons_222222_256x240.png
122
+ - coverage/assets/0.13.1/images/ui-icons_2e83ff_256x240.png
123
+ - coverage/assets/0.13.1/images/ui-icons_454545_256x240.png
124
+ - coverage/assets/0.13.1/images/ui-icons_888888_256x240.png
125
+ - coverage/assets/0.13.1/images/ui-icons_cd0a0a_256x240.png
126
+ - coverage/assets/0.13.1/loading.gif
127
+ - coverage/assets/0.13.1/magnify.png
128
+ - coverage/index.html
129
+ - lib/omniauth-entra_id_jwt.rb
130
+ - lib/omniauth/entra_id_jwt.rb
131
+ - lib/omniauth/entra_id_jwt/version.rb
132
+ - lib/omniauth/strategies/entra_id_jwt.rb
133
+ - sig/omniauth/entra/id/jwt.rbs
134
+ homepage: https://github.com/collabital/omniauth-entra_id_jwt
135
+ licenses:
136
+ - MIT
137
+ metadata: {}
138
+ post_install_message:
139
+ rdoc_options: []
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: 3.0.0
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ requirements: []
153
+ rubygems_version: 3.5.23
154
+ signing_key:
155
+ specification_version: 4
156
+ summary: OAuth 2 authentication with the Entra ID API using an On Behalf Of JWT token.
157
+ test_files: []