keycloak-sdk 1.1.0 → 1.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/README.md +2 -2
- data/lib/keycloak_sdk/auth_client.rb +5 -6
- data/lib/keycloak_sdk/jwt_validator.rb +9 -4
- data/lib/keycloak_sdk/version.rb +1 -1
- 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: 73ecdc82d8bb713a7529ce242c2eacfa8279d9e413ce09aa2e2e468e4fd269c3
|
|
4
|
+
data.tar.gz: c6670b1e3767730f49604d2215a4650d3630e34b5cac979ecc24d63517c423e3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 165d9c457ceb36f8b38e0cf622c15175af2ce8643e6ccfdba818143b4ca85a09d7967e0c51422108ce5b906ae6f805e28f0bc44380695f0a530e96cc9b0e01b6
|
|
7
|
+
data.tar.gz: fcbce56000bd6f42ff3c376b0c8913487792eac3803e7fbbc2a718d58bf1fec6826dc3f99e4d59b61f7cc950116926629766cb408b990347479df87bd2e64f6e
|
data/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Authentication (OIDC / OAuth2) and the Admin REST API for [Keycloak](https://www
|
|
|
4
4
|
|
|
5
5
|
Part of a **nine-language polyglot SDK** (Java · Python · Node · Go · C# · PHP · Rust · Ruby · Kotlin) — one API surface, isomorphic across all of them: [github.com/xzawed/KeyCloakSDK](https://github.com/xzawed/KeyCloakSDK).
|
|
6
6
|
|
|
7
|
-
> **`1.
|
|
7
|
+
> **`1.2.0` is on RubyGems** — a minor release on top of `1.1.0`; `1.0.0` was the first release carrying the stability guarantee. A bare `gem install keycloak-sdk` resolves it. **Fixed**: with `expected_audience` set to something other than the client_id (a resource server), `exchange_code` with an `expected_nonce:` rejected the server's valid id_token as `invalid id_token` (`Invalid audience`), because the id_token was checked against the same override as access tokens. The id_token's `aud` is now checked for the **client_id** (OIDC Core §2, §3.1.3.7), so that exchange passes, and an id_token without the client_id in `aud` is refused even when it carries the overridden value. `validate` still applies the override to access tokens, and both share one JWKS cache and refetch limit. **Added**: an `audience:` keyword on `JwtValidator#validate(token, audience:)` — omit it and nothing changes; `nil` or a blank value raises `ConfigError`.
|
|
8
8
|
|
|
9
9
|
## Requirements
|
|
10
10
|
|
|
@@ -55,7 +55,7 @@ client.admin.users.delete(user_id)
|
|
|
55
55
|
client.close
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
> **Audience:** validation requires the token's `aud` to contain `client_id`. A stock realm does *not* put the client id in a client-credentials token's `aud`, so on a default realm either pass `expected_audience: "my-api"` (the audience your realm actually issues), or add an *Audience* protocol mapper to the client in Keycloak.
|
|
58
|
+
> **Audience:** validation requires the token's `aud` to contain `client_id`. A stock realm does *not* put the client id in a client-credentials token's `aud`, so on a default realm either pass `expected_audience: "my-api"` (the audience your realm actually issues), or add an *Audience* protocol mapper to the client in Keycloak. `expected_audience` applies to access tokens (`validate`) only: the `id_token` that `exchange_code(expected_nonce:)` validates must always carry `client_id` in `aud` (OIDC Core §3.1.3.7).
|
|
59
59
|
|
|
60
60
|
The five admin resources — `users` / `clients` / `roles` / `groups` / `realms` — offer symmetric CRUD, and `client.admin.raw` is the escape hatch to the underlying bearer-authenticated `Faraday::Connection`.
|
|
61
61
|
|
|
@@ -39,7 +39,7 @@ module KeycloakSdk
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
# `expected_nonce`가 주어지면(create_authorization_request가 항상 돌려주는 nonce) 응답 id_token을
|
|
42
|
-
# realm JWKS로 서명·iss·aud·exp까지 강화 검증한 뒤 nonce 클레임을 대조한다 — OIDC nonce 재생
|
|
42
|
+
# realm JWKS로 서명·iss·aud(client_id)·exp까지 강화 검증한 뒤 nonce 클레임을 대조한다 — OIDC nonce 재생
|
|
43
43
|
# 방지. 불일치·부재·검증실패는 모두 거부(fail-closed). 생략 시 id_token 검증을 건너뛴다
|
|
44
44
|
# (여덟 언어 공통 — exchange에서 nonce를 필수로 만들지 않는다).
|
|
45
45
|
def exchange_code(code:, code_verifier:, redirect_uri:, expected_nonce: nil)
|
|
@@ -104,14 +104,13 @@ module KeycloakSdk
|
|
|
104
104
|
end
|
|
105
105
|
end
|
|
106
106
|
|
|
107
|
-
# id_token의 nonce 클레임을 대조하기 전에 강화 JwtValidator로 서명·iss·aud·exp까지
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
# 이 흐름을 쓴다면 해당 오디언스를 id_token에도 매핑해야 한다(audience 매퍼의 "Add to ID token").
|
|
107
|
+
# id_token의 nonce 클레임을 대조하기 전에 강화 JwtValidator로 서명·iss·aud·exp까지 검증한다.
|
|
108
|
+
# aud 는 `expected_audience` 재정의가 아니라 **client_id** 다(OIDC Core §2·§3.1.3.7) — 재정의는 액세스 토큰의 것.
|
|
109
|
+
# 검증기(=키 저장소)는 `validate` 와 같은 하나를 쓴다.
|
|
111
110
|
def verify_nonce!(id_token, expected_nonce)
|
|
112
111
|
raise AuthError, "authorization_code exchange failed: missing id_token for nonce validation" if id_token.nil?
|
|
113
112
|
|
|
114
|
-
validated = @jwt_validator.validate(id_token)
|
|
113
|
+
validated = @jwt_validator.validate(id_token, audience: @config.client_id)
|
|
115
114
|
return if validated.claims["nonce"] == expected_nonce
|
|
116
115
|
|
|
117
116
|
raise AuthError, "authorization_code exchange failed: unexpected nonce"
|
|
@@ -30,8 +30,13 @@ module KeycloakSdk
|
|
|
30
30
|
algorithms: config.signature_algorithms, clock_skew: config.clock_skew)
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
# `audience:` 는 이 호출만의 기대 aud 다(기본 = 생성 시 값). 코드 교환이 id_token 을 client_id 로 검사할 때
|
|
34
|
+
# 쓴다 — 키 저장소(캐시 · 재조회 제한 · 백오프)는 그대로 이 인스턴스의 것 하나다.
|
|
35
|
+
# ⚠️ nil·공백은 생성자와 같이 ConfigError — ruby-jwt 는 `aud: nil` 이면 aud 검사를 건너뛴다.
|
|
36
|
+
def validate(token, audience: @audience)
|
|
37
|
+
raise ConfigError, "audience is required" if audience.nil? || audience.to_s.strip.empty?
|
|
38
|
+
|
|
39
|
+
payload, = JWT.decode(token, nil, true, decode_options(audience))
|
|
35
40
|
to_validated(payload)
|
|
36
41
|
rescue JWT::DecodeError => e
|
|
37
42
|
# ruby-jwt 메시지는 입력을 인용하지 않는다. 그 **원인**은 인용한다 — 조각이 JSON 이 아니면 JSON::ParserError 가
|
|
@@ -41,12 +46,12 @@ module KeycloakSdk
|
|
|
41
46
|
|
|
42
47
|
private
|
|
43
48
|
|
|
44
|
-
def decode_options
|
|
49
|
+
def decode_options(audience)
|
|
45
50
|
{
|
|
46
51
|
algorithms: @algorithms,
|
|
47
52
|
jwks: jwks_loader,
|
|
48
53
|
verify_iss: true, iss: @issuer,
|
|
49
|
-
verify_aud: true, aud:
|
|
54
|
+
verify_aud: true, aud: audience,
|
|
50
55
|
verify_expiration: true,
|
|
51
56
|
verify_not_before: true,
|
|
52
57
|
required_claims: %w[exp iss aud],
|
data/lib/keycloak_sdk/version.rb
CHANGED