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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc6cc81e7537b1962108102b160aaeb30e45d1018ac533f834204dd58c69af3f
4
- data.tar.gz: 172f64d5db58105f0402ecbe1821ed2e2c2f2910139c75144b788c38fa170c5c
3
+ metadata.gz: 73ecdc82d8bb713a7529ce242c2eacfa8279d9e413ce09aa2e2e468e4fd269c3
4
+ data.tar.gz: c6670b1e3767730f49604d2215a4650d3630e34b5cac979ecc24d63517c423e3
5
5
  SHA512:
6
- metadata.gz: 19f5d3264eaec1620a68a509bcfdfe5c7180a1e7bc90867f96ffce31901ce0670e06a80d8925655e4977a13f6e1675936e59e4920cd431087372c06a0725d05c
7
- data.tar.gz: 1b534ae18c8a9e1cb7bbf492ccf9713bb9fee474afd0348d05b43b6c9cfd3e22fe773654c615f6e568609c3644603822a70330184d42fc97fb2133658fcaef89
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.1.0` is on RubyGems** — a minor release of security fixes on top of `1.0.1`; `1.0.0` was the first release carrying the stability guarantee. A bare `gem install keycloak-sdk` resolves it. An error raised on a malformed or hostile IdP response no longer prints that response or the secrets the SDK sent (present in `1.0.1`): `full_message` — what a logger prints — showed a non-JSON body's head, an echoed `error_description` and a broken status line, a lower `Faraday::ParsingError`'s `inspect` showed the request's client secret, refresh token and admin bearer, and on Ruby 3.2 an unwrapped `NoMethodError` quoted the response body. OAuth failures now carry only the error code (when it is shaped like one) and the HTTP status in their message, and a malformed `200` token response is an `AuthError`. **Added**: `KeycloakSdk::RedactedCause`. ⚠️ **An SDK error's `cause` is now a `KeycloakSdk::RedactedCause` — the lower class names (`lower_classes`) and the backtrace, no message — instead of the original Faraday, rack-oauth2 or JSON exception**, so a `rescue` or `is_a?` on `err.cause` against those types no longer matches. ⚠️ **A token or introspection response that is not a JSON object now raises `AuthError`** — an introspection body that was a string used to come back as a result.
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
- # (액세스 토큰과 id_token 모두 aud=client_id이므로 검증기를 공유해도 안전 — Kotlin/.NET 동형).
109
- # ⚠️ `config.expected_audience`를 설정하면 이 공유 검증기가 id_token에도 그 값을 요구한다 —
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
- def validate(token)
34
- payload, = JWT.decode(token, nil, true, decode_options)
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: @audience,
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],
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KeycloakSdk
4
- VERSION = "1.1.0"
4
+ VERSION = "1.2.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: keycloak-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - xzawed