omniauth-honin 0.1.0 → 0.2.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 +4 -4
- data/lib/omniauth/honin/version.rb +1 -1
- data/lib/omniauth/strategies/honin.rb +48 -115
- metadata +8 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ac29cb208ee56c02f6d29e9364c57e3093a7fdc9b776c42a88a7c5d31a7ab395
|
|
4
|
+
data.tar.gz: 67fc811523c4ddf6dd690b0019b14ea9333f47c79139f6e0049ff93c0763a77d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1cdb6461a0e684528666698e1f67a1ef6ef07c02d708fc2f30ad998998dd1620d5505a4148c8e0a6deccd274fb443eb0d08b56fc707774f42d027e850378ad34
|
|
7
|
+
data.tar.gz: c5562a1b8c375dabf8578407e35d0ab10fb7a4bf4636b488b827be0b7dc1c3163ba7eee7a48a8713cf821d7c7e07f6a65df438539e67cce1e18f333be406f824
|
|
@@ -2,13 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "omniauth"
|
|
4
4
|
require "omniauth-oauth2"
|
|
5
|
-
require "
|
|
6
|
-
require "securerandom"
|
|
7
|
-
require "base64"
|
|
8
|
-
require "digest"
|
|
9
|
-
require "net/http"
|
|
10
|
-
require "uri"
|
|
11
|
-
require "json"
|
|
5
|
+
require "honin/client"
|
|
12
6
|
|
|
13
7
|
module OmniAuth
|
|
14
8
|
module Strategies
|
|
@@ -20,8 +14,7 @@ module OmniAuth
|
|
|
20
14
|
authorize_url: "/oauth/authorize",
|
|
21
15
|
token_url: "/oauth/token"
|
|
22
16
|
|
|
23
|
-
# Disabled
|
|
24
|
-
# implementation would conflict with our session keys and S256 enforcement.
|
|
17
|
+
# Disabled — we own the full PKCE flow via HoninClient::PKCE.
|
|
25
18
|
option :pkce, false
|
|
26
19
|
|
|
27
20
|
option :scope, "email profile"
|
|
@@ -29,19 +22,22 @@ module OmniAuth
|
|
|
29
22
|
# Honin only supports client_secret_post, not client_secret_basic.
|
|
30
23
|
option :auth_token_params, {client_secret_param: "client_secret"}
|
|
31
24
|
|
|
32
|
-
# Sub-path mount support. Set
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
# Leave empty (default) for honin.id and root-mounted instances.
|
|
25
|
+
# Sub-path mount support. Set when the Honin instance is not at the root
|
|
26
|
+
# (e.g. "/auth" for id.acme.co/auth/...). The issuer claim in the JWT is
|
|
27
|
+
# verified against site + base_path. Leave empty for honin.id.
|
|
36
28
|
option :base_path, ""
|
|
37
29
|
|
|
30
|
+
# Provide a JWKS URI to enable RS256 JWT verification (recommended).
|
|
38
31
|
option :jwks_uri, nil
|
|
39
|
-
option :jwks, nil
|
|
40
32
|
|
|
41
|
-
|
|
33
|
+
# Alternatively, supply a pre-loaded JWK set (Hash, Array, or JWT::JWK::Set).
|
|
34
|
+
# Useful for self-hosted instances with a known public key.
|
|
35
|
+
option :jwks, nil
|
|
42
36
|
|
|
43
37
|
def request_phase
|
|
44
|
-
|
|
38
|
+
pkce = HoninClient::PKCE.new
|
|
39
|
+
session[:honin_pkce_verifier] = pkce.code_verifier
|
|
40
|
+
session[:honin_pkce_challenge] = pkce.code_challenge
|
|
45
41
|
super
|
|
46
42
|
end
|
|
47
43
|
|
|
@@ -61,30 +57,31 @@ module OmniAuth
|
|
|
61
57
|
def callback_phase
|
|
62
58
|
super
|
|
63
59
|
ensure
|
|
64
|
-
|
|
60
|
+
session.delete(:honin_pkce_verifier)
|
|
61
|
+
session.delete(:honin_pkce_challenge)
|
|
65
62
|
end
|
|
66
63
|
|
|
67
|
-
uid {
|
|
64
|
+
uid { honin_identity.sub }
|
|
68
65
|
|
|
69
66
|
info do
|
|
70
|
-
|
|
67
|
+
id = honin_identity
|
|
71
68
|
{
|
|
72
|
-
account_type:
|
|
73
|
-
name:
|
|
74
|
-
email:
|
|
75
|
-
email_verified:
|
|
76
|
-
timezone:
|
|
77
|
-
locale:
|
|
69
|
+
account_type: id.account_type,
|
|
70
|
+
name: id.display_name,
|
|
71
|
+
email: id.email,
|
|
72
|
+
email_verified: id.email_verified,
|
|
73
|
+
timezone: id.timezone,
|
|
74
|
+
locale: id.locale
|
|
78
75
|
}.compact
|
|
79
76
|
end
|
|
80
77
|
|
|
81
78
|
extra do
|
|
82
|
-
|
|
79
|
+
id = honin_identity
|
|
83
80
|
{
|
|
84
|
-
raw_info:
|
|
85
|
-
granted_scopes:
|
|
86
|
-
required_scopes:
|
|
87
|
-
}
|
|
81
|
+
raw_info: id.to_h,
|
|
82
|
+
granted_scopes: id.granted_scopes,
|
|
83
|
+
required_scopes: id.required_scopes
|
|
84
|
+
}
|
|
88
85
|
end
|
|
89
86
|
|
|
90
87
|
def client
|
|
@@ -94,98 +91,34 @@ module OmniAuth
|
|
|
94
91
|
token_url: "#{options.base_path}/oauth/token")
|
|
95
92
|
end
|
|
96
93
|
|
|
97
|
-
def
|
|
98
|
-
@
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
decode_verified_jwt(token)
|
|
107
|
-
else
|
|
108
|
-
decode_unverified_jwt(token)
|
|
109
|
-
end
|
|
94
|
+
def honin_identity
|
|
95
|
+
@honin_identity ||= if options.jwks_uri && !options.jwks_uri.to_s.empty?
|
|
96
|
+
build_verifier(self.class.jwks_cache_for(options.jwks_uri)).verify(access_token.token)
|
|
97
|
+
elsif options.jwks
|
|
98
|
+
build_verifier(HoninClient::JwksCache::Static.new(options.jwks)).verify(access_token.token)
|
|
99
|
+
else
|
|
100
|
+
# No JWKS configured — decode without verification (dev/test only)
|
|
101
|
+
payload, = JWT.decode(access_token.token, nil, false)
|
|
102
|
+
HoninClient::Identity.new(payload)
|
|
110
103
|
end
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
private
|
|
114
|
-
|
|
115
|
-
def generate_pkce!
|
|
116
|
-
verifier = SecureRandom.urlsafe_base64(PKCE_VERIFIER_LENGTH)
|
|
117
|
-
challenge = Base64.urlsafe_encode64(
|
|
118
|
-
Digest::SHA256.digest(verifier),
|
|
119
|
-
padding: false
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
session[:honin_pkce_verifier] = verifier
|
|
123
|
-
session[:honin_pkce_challenge] = challenge
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def clear_pkce_session!
|
|
127
|
-
session.delete(:honin_pkce_verifier)
|
|
128
|
-
session.delete(:honin_pkce_challenge)
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def jwks_verification_available?
|
|
132
|
-
(options.jwks_uri && !options.jwks_uri.to_s.empty?) ||
|
|
133
|
-
(!options.jwks.nil? && options.jwks != false)
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def decode_verified_jwt(token)
|
|
137
|
-
decode_opts = {
|
|
138
|
-
algorithms: ["RS256"],
|
|
139
|
-
verify_iss: true,
|
|
140
|
-
iss: "#{options.client_options.site}#{options.base_path}",
|
|
141
|
-
verify_aud: true,
|
|
142
|
-
aud: options.client_id
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
payload, = JWT.decode(token, nil, true, decode_opts.merge(jwks: resolve_jwk_set))
|
|
146
|
-
payload
|
|
147
|
-
rescue JWT::DecodeError, JWT::VerificationError, JWT::ExpiredSignature,
|
|
148
|
-
JWT::InvalidIssuerError, JWT::JWKError => e
|
|
104
|
+
rescue HoninClient::Error => e
|
|
149
105
|
raise OmniAuth::Strategies::OAuth2::CallbackError.new(:jwt_verification_failed, e.message)
|
|
150
106
|
end
|
|
151
107
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def resolve_jwk_set
|
|
158
|
-
if options.jwks_uri && !options.jwks_uri.to_s.empty?
|
|
159
|
-
fetch_jwks(options.jwks_uri)
|
|
160
|
-
else
|
|
161
|
-
coerce_jwks(options.jwks)
|
|
162
|
-
end
|
|
108
|
+
# Class-level JWKS cache — persists across requests, keyed by URI.
|
|
109
|
+
def self.jwks_cache_for(uri)
|
|
110
|
+
@jwks_caches ||= {}
|
|
111
|
+
@jwks_caches[uri] ||= HoninClient::JwksCache.new(uri)
|
|
163
112
|
end
|
|
164
113
|
|
|
165
|
-
|
|
166
|
-
case jwks
|
|
167
|
-
when JWT::JWK::Set
|
|
168
|
-
jwks
|
|
169
|
-
when Hash
|
|
170
|
-
JWT::JWK::Set.new(jwks[:keys] || jwks["keys"] || [jwks])
|
|
171
|
-
when Array
|
|
172
|
-
JWT::JWK::Set.new(jwks)
|
|
173
|
-
else
|
|
174
|
-
raise ArgumentError, "jwks must be a Hash, Array, or JWT::JWK::Set, got #{jwks.class}"
|
|
175
|
-
end
|
|
176
|
-
end
|
|
114
|
+
private
|
|
177
115
|
|
|
178
|
-
def
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
end
|
|
185
|
-
body = JSON.parse(response.body)
|
|
186
|
-
JWT::JWK::Set.new(body["keys"] || [body])
|
|
187
|
-
rescue JSON::ParserError, JWT::JWKError => e
|
|
188
|
-
raise OmniAuth::Strategies::OAuth2::CallbackError.new(:jwks_fetch_failed, e.message)
|
|
116
|
+
def build_verifier(cache)
|
|
117
|
+
HoninClient::TokenVerifier.new(
|
|
118
|
+
jwks_cache: cache,
|
|
119
|
+
issuer: "#{options.client_options.site}#{options.base_path}",
|
|
120
|
+
client_id: options.client_id
|
|
121
|
+
)
|
|
189
122
|
end
|
|
190
123
|
end
|
|
191
124
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omniauth-honin
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Axel Gustav
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: omniauth
|
|
@@ -38,19 +38,19 @@ dependencies:
|
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '1.8'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
|
-
name:
|
|
41
|
+
name: honin-client
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
|
-
- - "
|
|
44
|
+
- - ">="
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version:
|
|
46
|
+
version: 0.1.0
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
|
-
- - "
|
|
51
|
+
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version:
|
|
53
|
+
version: 0.1.0
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
55
|
name: rake
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -169,7 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
169
169
|
- !ruby/object:Gem::Version
|
|
170
170
|
version: '0'
|
|
171
171
|
requirements: []
|
|
172
|
-
rubygems_version: 3.6.
|
|
172
|
+
rubygems_version: 3.6.9
|
|
173
173
|
specification_version: 4
|
|
174
174
|
summary: Honin OAuth2 strategy for OmniAuth
|
|
175
175
|
test_files: []
|