logi_auth 1.0.0 → 1.0.1
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/logi_auth/errors.rb +1 -0
- data/lib/logi_auth/id_token_verifier.rb +32 -6
- data/lib/logi_auth/server.rb +7 -4
- data/lib/logi_auth/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 831ea9a87a2ab041f8056110a66b5bdb70edfb5922c24e611f2928812a90a192
|
|
4
|
+
data.tar.gz: a81b696e656399b2aebcffe49346f6278b95af02bcde4fd535a7de921a394aee
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0cbd469cbd055f391654f6c968692a3ca3d8d74d7a6c124daddd0225cfc54d0cc8e1f5a91279d8ba7bff83ea1317d28ca923c6bdd755dafd20c6eac954571cff
|
|
7
|
+
data.tar.gz: e66aa214038aac5f916c9a0af991edd2af705c7322c958551c255043e68f62ac720e8ecdc859733136cc8fc9227aaf7fbdef2cd8c9a020df2c3fcd7f1694d13d
|
data/lib/logi_auth/errors.rb
CHANGED
|
@@ -24,12 +24,15 @@ module LogiAuth
|
|
|
24
24
|
module_function
|
|
25
25
|
|
|
26
26
|
# Verify +id_token+ and return a Result. Raises IdTokenError on any failure.
|
|
27
|
-
# Claim order: signature -> iss -> aud -> exp -> iat -> nonce -> sub.
|
|
27
|
+
# Claim order: signature -> iss -> aud -> exp -> iat -> nonce -> sub -> at_hash.
|
|
28
28
|
#
|
|
29
|
-
# jwks:
|
|
30
|
-
# expected:
|
|
31
|
-
# now:
|
|
32
|
-
|
|
29
|
+
# jwks: Hash with "keys" => [ {"kty","n","e","kid",...}, ... ]
|
|
30
|
+
# expected: { issuer:, client_id:, nonce: } (nonce optional)
|
|
31
|
+
# now: Unix seconds; defaults to Time.now. Injectable for tests.
|
|
32
|
+
# access_token: OAuth access_token string. When both the payload carries an
|
|
33
|
+
# `at_hash` and this is provided, OIDC §3.1.3.6 at_hash binding is enforced
|
|
34
|
+
# (present-only): default nil skips the check, preserving backward compat.
|
|
35
|
+
def verify(id_token, jwks:, expected:, now: nil, clock_skew_sec: 60, access_token: nil)
|
|
33
36
|
now ||= Time.now.to_i
|
|
34
37
|
|
|
35
38
|
parts = id_token.split(".")
|
|
@@ -46,7 +49,15 @@ module LogiAuth
|
|
|
46
49
|
kid = header["kid"]
|
|
47
50
|
raise IdTokenError, "missing_kid" unless kid.is_a?(String) && !kid.empty?
|
|
48
51
|
|
|
49
|
-
|
|
52
|
+
# Tolerant JWKS key selection: pick the RS256 signing RSA key matching kid.
|
|
53
|
+
# Guards against a future EC (or encryption) key sharing the kid space —
|
|
54
|
+
# exact-match kty/use/alg filter keeps login working when JWKS gains keys.
|
|
55
|
+
jwk = Array(jwks["keys"]).find do |k|
|
|
56
|
+
k["kty"] == "RSA" &&
|
|
57
|
+
(k["use"].nil? || k["use"] == "sig") &&
|
|
58
|
+
(k["alg"].nil? || k["alg"] == "RS256") &&
|
|
59
|
+
k["kid"] == kid
|
|
60
|
+
end
|
|
50
61
|
raise IdTokenError, "unknown_kid" if jwk.nil?
|
|
51
62
|
|
|
52
63
|
signature = base64url_decode(parts[2])
|
|
@@ -82,9 +93,24 @@ module LogiAuth
|
|
|
82
93
|
sub = payload["sub"]
|
|
83
94
|
raise IdTokenError, "missing_claim" unless sub.is_a?(String) && !sub.empty?
|
|
84
95
|
|
|
96
|
+
# OIDC §3.1.3.6 at_hash — enforced last, present-only. If the payload
|
|
97
|
+
# carries an at_hash and an access_token was supplied, they must bind:
|
|
98
|
+
# base64url_nopad(SHA256(access_token bytes)[0...16]) == at_hash. Absent
|
|
99
|
+
# at_hash or absent access_token skips the check (backward compatible).
|
|
100
|
+
at_hash = payload["at_hash"]
|
|
101
|
+
if access_token && at_hash.is_a?(String) && !at_hash.empty?
|
|
102
|
+
raise IdTokenError, "at_hash_mismatch" unless at_hash == compute_at_hash(access_token)
|
|
103
|
+
end
|
|
104
|
+
|
|
85
105
|
Result.new(sub: sub, claims: payload)
|
|
86
106
|
end
|
|
87
107
|
|
|
108
|
+
# left-most 128 bits of SHA256(access_token), base64url without padding.
|
|
109
|
+
def compute_at_hash(access_token)
|
|
110
|
+
digest = OpenSSL::Digest::SHA256.digest(access_token.encode(Encoding::UTF_8))
|
|
111
|
+
Base64.urlsafe_encode64(digest[0, 16], padding: false)
|
|
112
|
+
end
|
|
113
|
+
|
|
88
114
|
def verify_rs256(signing_input, signature, jwk)
|
|
89
115
|
key = rsa_public_key(jwk)
|
|
90
116
|
return false unless key
|
data/lib/logi_auth/server.rb
CHANGED
|
@@ -92,7 +92,10 @@ module LogiAuth
|
|
|
92
92
|
raise ServerError.new("missing_id_token", "Token response had no id_token — was `openid` in the scopes?")
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
# Thread the access_token so OIDC §3.1.3.6 at_hash binding is enforced
|
|
96
|
+
# before the Session is returned (present-only: no-op when the id_token
|
|
97
|
+
# carries no at_hash). parse_token_body guarantees a String access_token.
|
|
98
|
+
verified = verify_with_rotation_retry(id_token, nonce, tokens["access_token"])
|
|
96
99
|
email = verified.claims["email"]
|
|
97
100
|
expires_in = tokens["expires_in"]
|
|
98
101
|
|
|
@@ -110,17 +113,17 @@ module LogiAuth
|
|
|
110
113
|
|
|
111
114
|
private
|
|
112
115
|
|
|
113
|
-
def verify_with_rotation_retry(id_token, nonce)
|
|
116
|
+
def verify_with_rotation_retry(id_token, nonce, access_token = nil)
|
|
114
117
|
expected = { issuer: @token_issuer, client_id: @client_id, nonce: nonce }
|
|
115
118
|
jwks, from_cache = fetch_jwks(force: false)
|
|
116
119
|
begin
|
|
117
|
-
IdTokenVerifier.verify(id_token, jwks: jwks, expected: expected)
|
|
120
|
+
IdTokenVerifier.verify(id_token, jwks: jwks, expected: expected, access_token: access_token)
|
|
118
121
|
rescue IdTokenError => e
|
|
119
122
|
# Key rotation within the cache TTL — bust + refetch once.
|
|
120
123
|
if e.code == "unknown_kid" && from_cache
|
|
121
124
|
fresh, = fetch_jwks(force: true)
|
|
122
125
|
begin
|
|
123
|
-
IdTokenVerifier.verify(id_token, jwks: fresh, expected: expected)
|
|
126
|
+
IdTokenVerifier.verify(id_token, jwks: fresh, expected: expected, access_token: access_token)
|
|
124
127
|
rescue IdTokenError => retry_err
|
|
125
128
|
raise as_id_token_invalid(retry_err)
|
|
126
129
|
end
|
data/lib/logi_auth/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: logi_auth
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dcode
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-07-
|
|
10
|
+
date: 2026-07-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: minitest
|