omniauth-microsoft-identity2 1.0.4 → 1.0.5
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 +4 -4
- data/README.md +3 -1
- data/lib/omniauth/microsoft_identity2/version.rb +1 -1
- data/lib/omniauth/microsoft_identity2.rb +2 -2
- data/lib/omniauth/strategies/microsoft_identity2.rb +59 -59
- data/lib/omniauth-microsoft-identity2.rb +1 -1
- data/omniauth-microsoft-identity2.gemspec +23 -23
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2feb48d6a9088704e28a622d6ee1790693e82f64c208edc483d7bb5d5a9df8ea
|
|
4
|
+
data.tar.gz: 169bebfdb3d7a05f4f943a4368b28e06c7462879995d95e0ef68e00f3a205b93
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6bc756c9d30ec256c1779e061c17a9a4e9ef0ed95dac43e05245656744e4b03ba59fa14e4a1b16956e6afb5d5c496fb70bc12cd94f8e4bc66bf6dbf45e66a461
|
|
7
|
+
data.tar.gz: 4d6c02ba52db1dd5dbaaef4b86706b9cf0d15e77c87dd41315ee00eb1fff8e46fbb2785c860fbd6389eff3487613c7f11f5286f229073a77774565d1756e9649
|
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# OmniAuth
|
|
1
|
+
# OmniAuth Microsoft Identity Strategy
|
|
2
2
|
|
|
3
3
|
[](https://github.com/icoretech/omniauth-microsoft-identity2/actions/workflows/test.yml?query=branch%3Amain)
|
|
4
4
|
[](https://badge.fury.io/rb/omniauth-microsoft-identity2)
|
|
@@ -47,6 +47,7 @@ provider :windowslive, ENV.fetch('MICROSOFT_CLIENT_ID'), ENV.fetch('MICROSOFT_CL
|
|
|
47
47
|
## Options
|
|
48
48
|
|
|
49
49
|
Supported options include:
|
|
50
|
+
|
|
50
51
|
- `tenant` (default: `common`)
|
|
51
52
|
- `scope` (default: `openid profile email offline_access User.Read`)
|
|
52
53
|
- `prompt`
|
|
@@ -133,6 +134,7 @@ Example payload from `request.env['omniauth.auth']` (realistic shape, anonymized
|
|
|
133
134
|
## Endpoints
|
|
134
135
|
|
|
135
136
|
This gem uses Microsoft Identity v2 endpoints and Microsoft Graph user info endpoints:
|
|
137
|
+
|
|
136
138
|
- `https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize`
|
|
137
139
|
- `https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token`
|
|
138
140
|
- `https://graph.microsoft.com/oidc/userinfo`
|
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require "jwt"
|
|
4
|
+
require "omniauth-oauth2"
|
|
5
5
|
|
|
6
6
|
module OmniAuth
|
|
7
7
|
module Strategies
|
|
8
8
|
# OmniAuth strategy for Microsoft Identity (Entra ID) OAuth2/OpenID Connect.
|
|
9
9
|
class MicrosoftIdentity2 < OmniAuth::Strategies::OAuth2
|
|
10
|
-
BASE_URL =
|
|
11
|
-
DEFAULT_SCOPE =
|
|
12
|
-
USER_INFO_URL =
|
|
13
|
-
GRAPH_ME_URL =
|
|
10
|
+
BASE_URL = "https://login.microsoftonline.com"
|
|
11
|
+
DEFAULT_SCOPE = "openid profile email offline_access User.Read"
|
|
12
|
+
USER_INFO_URL = "https://graph.microsoft.com/oidc/userinfo"
|
|
13
|
+
GRAPH_ME_URL = "https://graph.microsoft.com/v1.0/me"
|
|
14
14
|
|
|
15
|
-
option :name,
|
|
15
|
+
option :name, "microsoft_identity2"
|
|
16
16
|
option :authorize_options, %i[scope state prompt login_hint domain_hint response_mode redirect_uri nonce]
|
|
17
|
-
option :tenant,
|
|
17
|
+
option :tenant, "common"
|
|
18
18
|
option :base_url, BASE_URL
|
|
19
19
|
option :scope, DEFAULT_SCOPE
|
|
20
20
|
option :skip_jwt, false
|
|
21
21
|
option :uid_with_tenant, true
|
|
22
22
|
|
|
23
23
|
option :client_options,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
24
|
+
site: BASE_URL,
|
|
25
|
+
authorize_url: "common/oauth2/v2.0/authorize",
|
|
26
|
+
token_url: "common/oauth2/v2.0/token",
|
|
27
|
+
connection_opts: {
|
|
28
|
+
headers: {
|
|
29
|
+
user_agent: "icoretech-omniauth-microsoft-identity2 gem",
|
|
30
|
+
accept: "application/json",
|
|
31
|
+
content_type: "application/x-www-form-urlencoded"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
34
|
|
|
35
35
|
uid do
|
|
36
|
-
oid_or_sub = raw_info[
|
|
37
|
-
tid = raw_info[
|
|
36
|
+
oid_or_sub = raw_info["oid"] || raw_info["sub"] || raw_info["id"]
|
|
37
|
+
tid = raw_info["tid"]
|
|
38
38
|
|
|
39
39
|
if options[:uid_with_tenant] && present?(tid) && present?(oid_or_sub)
|
|
40
40
|
"#{tid}:#{oid_or_sub}"
|
|
@@ -44,34 +44,34 @@ module OmniAuth
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
info do
|
|
47
|
-
email = raw_info[
|
|
47
|
+
email = raw_info["email"] || raw_info["preferred_username"] || raw_info["upn"] || raw_info["mail"]
|
|
48
48
|
{
|
|
49
|
-
name: raw_info[
|
|
49
|
+
name: raw_info["name"],
|
|
50
50
|
email: email,
|
|
51
|
-
first_name: raw_info[
|
|
52
|
-
last_name: raw_info[
|
|
53
|
-
nickname: raw_info[
|
|
54
|
-
image: raw_info[
|
|
51
|
+
first_name: raw_info["given_name"],
|
|
52
|
+
last_name: raw_info["family_name"],
|
|
53
|
+
nickname: raw_info["preferred_username"] || raw_info["upn"] || email || raw_info["sub"],
|
|
54
|
+
image: raw_info["picture"]
|
|
55
55
|
}.compact
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
credentials do
|
|
59
59
|
{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
"token" => access_token.token,
|
|
61
|
+
"refresh_token" => access_token.refresh_token,
|
|
62
|
+
"expires_at" => access_token.expires_at,
|
|
63
|
+
"expires" => access_token.expires?,
|
|
64
|
+
"scope" => token_scope
|
|
65
65
|
}.compact
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
extra do
|
|
69
|
-
data = {
|
|
69
|
+
data = {"raw_info" => raw_info}
|
|
70
70
|
id_token = raw_id_token
|
|
71
71
|
if present?(id_token)
|
|
72
|
-
data[
|
|
72
|
+
data["id_token"] = id_token
|
|
73
73
|
decoded = decoded_id_token
|
|
74
|
-
data[
|
|
74
|
+
data["id_info"] = decoded if decoded
|
|
75
75
|
end
|
|
76
76
|
data
|
|
77
77
|
end
|
|
@@ -116,7 +116,7 @@ module OmniAuth
|
|
|
116
116
|
|
|
117
117
|
# Prevent authorization response params from being appended to redirect_uri.
|
|
118
118
|
def query_string
|
|
119
|
-
return
|
|
119
|
+
return "" if request.params["code"]
|
|
120
120
|
|
|
121
121
|
super
|
|
122
122
|
end
|
|
@@ -125,10 +125,10 @@ module OmniAuth
|
|
|
125
125
|
|
|
126
126
|
def fetch_user_info
|
|
127
127
|
normalize_user_info(access_token.get(USER_INFO_URL).parsed)
|
|
128
|
-
rescue
|
|
128
|
+
rescue
|
|
129
129
|
begin
|
|
130
130
|
normalize_user_info(access_token.get(GRAPH_ME_URL).parsed)
|
|
131
|
-
rescue
|
|
131
|
+
rescue
|
|
132
132
|
{}
|
|
133
133
|
end
|
|
134
134
|
end
|
|
@@ -138,21 +138,21 @@ module OmniAuth
|
|
|
138
138
|
|
|
139
139
|
return payload unless graph_profile_payload?(payload)
|
|
140
140
|
|
|
141
|
-
upn = payload[
|
|
141
|
+
upn = payload["userPrincipalName"]
|
|
142
142
|
{
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
143
|
+
"sub" => payload["id"],
|
|
144
|
+
"oid" => payload["id"],
|
|
145
|
+
"name" => payload["displayName"],
|
|
146
|
+
"given_name" => payload["givenName"],
|
|
147
|
+
"family_name" => payload["surname"],
|
|
148
|
+
"email" => payload["mail"] || upn,
|
|
149
|
+
"preferred_username" => upn,
|
|
150
|
+
"upn" => upn
|
|
151
151
|
}.merge(payload).compact
|
|
152
152
|
end
|
|
153
153
|
|
|
154
154
|
def normalize_scope(raw_scope)
|
|
155
|
-
raw_scope.to_s.split(/[\s,]+/).reject(&:empty?).uniq.join(
|
|
155
|
+
raw_scope.to_s.split(/[\s,]+/).reject(&:empty?).uniq.join(" ")
|
|
156
156
|
end
|
|
157
157
|
|
|
158
158
|
def apply_request_authorize_overrides(params)
|
|
@@ -163,12 +163,12 @@ module OmniAuth
|
|
|
163
163
|
end
|
|
164
164
|
|
|
165
165
|
def graph_profile_payload?(payload)
|
|
166
|
-
payload.key?(
|
|
166
|
+
payload.key?("displayName") || payload.key?("userPrincipalName")
|
|
167
167
|
end
|
|
168
168
|
|
|
169
169
|
def configure_tenant_client_urls
|
|
170
170
|
tenant = options[:tenant].to_s.strip
|
|
171
|
-
tenant =
|
|
171
|
+
tenant = "common" if tenant.empty?
|
|
172
172
|
|
|
173
173
|
base_url = options[:base_url].to_s.strip
|
|
174
174
|
base_url = BASE_URL if base_url.empty?
|
|
@@ -178,16 +178,16 @@ module OmniAuth
|
|
|
178
178
|
end
|
|
179
179
|
|
|
180
180
|
def persist_authorize_state(params)
|
|
181
|
-
session[
|
|
181
|
+
session["omniauth.state"] = params[:state] if params[:state]
|
|
182
182
|
end
|
|
183
183
|
|
|
184
184
|
def token_scope
|
|
185
|
-
access_token.params[
|
|
185
|
+
access_token.params["scope"] || access_token["scope"]
|
|
186
186
|
end
|
|
187
187
|
|
|
188
188
|
def raw_id_token
|
|
189
189
|
params = access_token.respond_to?(:params) ? access_token.params : {}
|
|
190
|
-
params[
|
|
190
|
+
params["id_token"] || access_token["id_token"]
|
|
191
191
|
end
|
|
192
192
|
|
|
193
193
|
def decoded_id_token
|
|
@@ -211,7 +211,7 @@ module OmniAuth
|
|
|
211
211
|
end
|
|
212
212
|
|
|
213
213
|
def missing_session_state?
|
|
214
|
-
present?(request.params[
|
|
214
|
+
present?(request.params["state"]) && blank?(session["omniauth.state"])
|
|
215
215
|
end
|
|
216
216
|
|
|
217
217
|
def oauth_state_nil_compare_error?(error)
|
|
@@ -221,23 +221,23 @@ module OmniAuth
|
|
|
221
221
|
def fail_state_mismatch
|
|
222
222
|
fail!(
|
|
223
223
|
:csrf_detected,
|
|
224
|
-
OmniAuth::Strategies::OAuth2::CallbackError.new(:csrf_detected,
|
|
224
|
+
OmniAuth::Strategies::OAuth2::CallbackError.new(:csrf_detected, "OAuth state was missing or mismatched")
|
|
225
225
|
)
|
|
226
226
|
end
|
|
227
227
|
end
|
|
228
228
|
|
|
229
229
|
# Backward-compatible strategy name for existing callback paths.
|
|
230
230
|
class MicrosoftIdentity < MicrosoftIdentity2
|
|
231
|
-
option :name,
|
|
231
|
+
option :name, "microsoft_identity"
|
|
232
232
|
end
|
|
233
233
|
|
|
234
234
|
# Compatibility alias for legacy windowslive callback paths.
|
|
235
235
|
class Windowslive < MicrosoftIdentity2
|
|
236
|
-
option :name,
|
|
236
|
+
option :name, "windowslive"
|
|
237
237
|
end
|
|
238
238
|
end
|
|
239
239
|
end
|
|
240
240
|
|
|
241
|
-
OmniAuth.config.add_camelization
|
|
242
|
-
OmniAuth.config.add_camelization
|
|
243
|
-
OmniAuth.config.add_camelization
|
|
241
|
+
OmniAuth.config.add_camelization "microsoft_identity2", "MicrosoftIdentity2"
|
|
242
|
+
OmniAuth.config.add_camelization "microsoft_identity", "MicrosoftIdentity"
|
|
243
|
+
OmniAuth.config.add_camelization "windowslive", "Windowslive"
|
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
lib = File.expand_path(
|
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
-
require
|
|
5
|
+
require "omniauth/microsoft_identity2/version"
|
|
6
6
|
|
|
7
7
|
Gem::Specification.new do |spec|
|
|
8
|
-
spec.name =
|
|
8
|
+
spec.name = "omniauth-microsoft-identity2"
|
|
9
9
|
spec.version = OmniAuth::MicrosoftIdentity2::VERSION
|
|
10
|
-
spec.authors = [
|
|
11
|
-
spec.email = [
|
|
10
|
+
spec.authors = ["Claudio Poli"]
|
|
11
|
+
spec.email = ["masterkain@gmail.com"]
|
|
12
12
|
|
|
13
|
-
spec.summary =
|
|
13
|
+
spec.summary = "OmniAuth strategy for Microsoft Identity (Entra ID) OAuth2/OpenID Connect authentication."
|
|
14
14
|
spec.description =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
spec.homepage =
|
|
18
|
-
spec.license =
|
|
19
|
-
spec.required_ruby_version =
|
|
15
|
+
"OAuth2/OpenID Connect strategy for OmniAuth that authenticates users " \
|
|
16
|
+
"with Microsoft Identity and exposes profile metadata."
|
|
17
|
+
spec.homepage = "https://github.com/icoretech/omniauth-microsoft-identity2"
|
|
18
|
+
spec.license = "MIT"
|
|
19
|
+
spec.required_ruby_version = ">= 3.2"
|
|
20
20
|
|
|
21
|
-
spec.metadata[
|
|
22
|
-
spec.metadata[
|
|
23
|
-
spec.metadata[
|
|
24
|
-
spec.metadata[
|
|
21
|
+
spec.metadata["source_code_uri"] = "https://github.com/icoretech/omniauth-microsoft-identity2"
|
|
22
|
+
spec.metadata["bug_tracker_uri"] = "https://github.com/icoretech/omniauth-microsoft-identity2/issues"
|
|
23
|
+
spec.metadata["changelog_uri"] = "https://github.com/icoretech/omniauth-microsoft-identity2/releases"
|
|
24
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
25
25
|
|
|
26
26
|
spec.files = Dir[
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
"lib/**/*.rb",
|
|
28
|
+
"README*",
|
|
29
|
+
"LICENSE*",
|
|
30
|
+
"*.gemspec"
|
|
31
31
|
]
|
|
32
|
-
spec.require_paths = [
|
|
32
|
+
spec.require_paths = ["lib"]
|
|
33
33
|
|
|
34
|
-
spec.add_dependency
|
|
35
|
-
spec.add_dependency
|
|
36
|
-
spec.add_dependency
|
|
34
|
+
spec.add_dependency "cgi", ">= 0.3.6"
|
|
35
|
+
spec.add_dependency "jwt", ">= 2.9.2"
|
|
36
|
+
spec.add_dependency "omniauth-oauth2", ">= 1.8", "< 2.0"
|
|
37
37
|
end
|