All notable changes to this project are documented here. The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[2.9.0] - 2026-10-17
Security
- Defer DPoP replay-cache claims in the resource-server Plug until after the
access-token signature and
cnf.jktbinding verify. An unauthenticated proof can no longer reserve replay identifiers or grow the replay store before its token establishes the caller's identity. - Route local JWT verification through Attesto's shared strict verifier so client-side verification applies the same canonical parsing, candidate filtering, malformed-result handling, and exact RSA-PSS salt and MGF1 checks as core protocol verifiers.
- Build DPoP proofs through
Attesto.DPoP.Proof, which requires HTTPS targets, rejects userinfo and malformed percent escapes, and preserves key-declared algorithms and identifiers. Resource metadata challenge URLs now use the same strict URI policy. - Use Attesto's IANA-registry-based public destination policy for discovery and JWKS fetches. Non-global and special-use destinations remain blocked across IPv4, IPv6, mapped, compatible, NAT64, and 6to4 forms, while the registries' applicable globally reachable exceptions remain available. ORCHIDv2 and Drone Remote ID non-locator identifiers remain blocked from fetches.
Changed
- Require Attesto 2.5.0 or later in the 2.x line.
AttestoClient.DPoPnow delegates proof building and algorithm resolution to the core implementation, and discovery delegates destination classification toAttesto.IPAddress.public_fetch_destination?/1.
[2.8.0] - 2026-10-15
Security
- Send OID4VCI Nonce Endpoint requests through the unauthenticated open-POST path. In split Credential Issuer and Authorization Server deployments this prevents forwarding the AS client secret, private-key assertion, client attestation, or DPoP proof to the Credential Issuer; endpoint screening, pinning, response bounds, and the overall deadline still apply.
- Reject malformed or duplicate
req_optionsbefore network I/O with:unsafe_transport_options, including nested connection and transport options used by unauthenticated by-reference requests. - Reject private JWK members in verification keys and reject empty, malformed, or struct-backed JWKS inputs. A malformed candidate no longer prevents a separate valid public verification key from being selected.
- Validate optional
nbfclaims in JARM, signed UserInfo, signed introspection, and resource-server tokens, and compare registered JWTtypvalues case-insensitively where required. - Validate wallet and attestation inputs before signing or network I/O,
including duplicate holder keys, malformed optional claims, protected-header
values, grant objects, credential issuer identifiers, notification IDs, and
symmetric keys. Key Attestation
certificationvalues are now limited to absolute HTTP(S) URLs.
Added
- Support anonymous OID4VCI pre-authorized-code redemption when
client_auth: :noneby allowingclient_idto be omitted. Authenticated client methods continue to require a non-empty client identifier. - Propagate
:alg,:kid, and:nowthrough DPoP-enabled authorization-code, token, credential, refresh, and nonce-retry paths. Each retry receives a freshjtiwhile retaining the selected key algorithm and identifier.
Changed
- Builders now honor a signing JWK's declared
algwhen:algis omitted and return:unsupported_algwhen an explicit algorithm conflicts with that declaration. This enables RSA DPoP keys declared for PS256 instead of silently forcing RS256. - A present
kidmust be a non-empty string. Explicitkid: nil, other malformed values, and malformed embedded JWKkidmembers now return:invalid_kidinstead of falling back to another identifier. Token.revoke/2now requires:client_id.Verifier.normalize_jwks/1returns:invalid_jwksfor empty key sets, and protected algorithms outside the accepted set return:unsupported_alginstead of:invalid_signature.- An explicit OID4VCI
credential_configuration_idmust be present in the credential offer; a mismatch fails before metadata lookup or HTTP. - Stop requiring every OpenID Provider metadata document to advertise RS256. The selected ID Token algorithm must still be advertised, allowing clients configured for ES256, EdDSA, or another accepted algorithm to use providers that intentionally omit RS256.
Fixed
- Preserve callback JWKS resolver, timeout, and response-size options while using the transaction's pinned issuer metadata.
- Parse every
WWW-Authenticatefield and combined challenge, recognize the DPoP scheme case-insensitively, and retry only for an exacterror=use_dpop_nonceparameter. - Derive canonical DPoP target URIs for IPv6 authorities and carry RSA PS256,
explicit
kid, deterministic time, and nonce options through DPoP retries. - Compute SD-JWT Key Binding
sd_hashwith the issuer-selected_sd_alg, restoring SHA-384 and SHA-512 interoperability with conforming verifiers. - Validate and expose
notification_idconsistently for immediate and deferred credential responses, reject invalid credential-offer grant values, and reject duplicate holder keys before issuing a request. - Return controlled errors for malformed builder inputs and symmetric attestation or holder keys instead of backend exceptions.
[2.7.0] - 2026-10-08
Fixed
- Retain the selected ID Token algorithm in
TokenSetthrough authorization and successive refreshes, including responses without an ID Token. FAPI's default ES256 selection now refreshes without repeatingid_token_alg. Reject conflicting explicit algorithms before HTTP and reject shared refresh results that do not match the caller's retained algorithm.
Security
- Require Attesto 2.3.1 or later for exact JWA PSS verification and fail-closed algorithm policies, including mixed allowlists. Upgrade core and client together.
- Require authenticated PAR and token requests plus locally bound DPoP in HAIP/FAPI flows. Persist profile, client/issuer identity and authentication binding through callback and refresh; reject changed or omitted material. FAPI supports private-key JWT authentication here; unverified mTLS claims cannot substitute for a DPoP key.
- Reject unsolicited DPoP token responses when no local signing key exists, including pre-authorized, authorization-code and refresh exchanges.
- Require a bounded certificate chain in
x5cfor HAIP client-attestation authentication. Bind the stable client and instance-key identity while permitting renewed attestations at callback and refresh. Authorization servers remain responsible for attester signature and trust. - Require the complete profile-bound
TokenSetfor externally supplied HAIP credential access tokens. Passaccess_token: tokenswith the retained DPoP key; a bare token string cannot establish the required binding. - Reject reserved parameters, bracket aliases and duplicate roots in discovered authorization/PAR/token endpoint queries before performing HTTP requests.
- Require signed HAIP verifier metadata to advertise both A128GCM and A256GCM; retain this profile when building or submitting presentation responses.
- Require
digitalSignaturewhen a signing certificate declares KeyUsage. Reject RSA-PSS-constrained SubjectPublicKeyInfo throughout the selected certificate path, including the used trust anchor, until its algorithm restrictions can be preserved and enforced. Reject non-NULLrsaEncryptionparameters; absent parameters remain accepted for interoperability. Additional ecosystem EKU and revocation checks remain application-owned. - Reject empty, invalid and weak wallet algorithm allowlists. HAIP and explicitly selected FAPI policy cannot be weakened through caller options.
- Require a DPoP token response whenever pre-authorized issuance requests DPoP,
before contacting nonce or credential endpoints. HAIP/FAPI authorization-code
transactions require response
isseven when discovery omits its support flag; the policy is pinned at start and cannot be weakened at callback. - Preserve DPoP across refresh: require signing material before any network request, reject Bearer responses, and compare any locally retained binding thumbprint. Legacy manually constructed token sets lack historic key provenance and acquire a binding after a successful DPoP refresh.
- Require PAR in HAIP/FAPI authorization-code flows, reject contradictory options before discovery, and reject bracketed aliases of protected extra authorization/PAR parameters.
- Require verifier format metadata and enforce advertised issuer and holder
algorithms during presentation generation and caller-built submission.
Registered authoritative
verifier_metadataoverrides signed fields; certificate-bound requests require metadata in their signed request. - Strip caller cookies, authentication, custom headers and routing options from unauthenticated by-reference request and credential-offer fetching.
- Enforce OID4VP request typing, required response mode, URL-safe nonce/state, unique nonempty bounded encryption-key IDs, and aggregate response bounds. Request URI responses require the request-object media type. Presentation response endpoints must return HTTP 200 and a JSON object with the JSON media type; malformed or duplicate JSON is rejected.
Added
- Add plain OAuth authorization-code transactions with S256 PKCE using
protocol: :oauth, RFC 8414 discovery, and no ID Token requirement. Keep issuer, browser-session, and one-time state checks; pin the selected protocol and any initiating DPoP key in the transaction. - Support Pushed Authorization Requests with client authentication and DPoP. Honor servers requiring PAR, reject explicit downgrades, and remove stored transactions when PAR fails or state expires during the operation. Require HTTP 201 and cap the returned redirect lifetime to the remaining state and PAR lifetimes. Authorization redirects carry only the client identifier and returned request URI.
- Add encrypted OID4VCI credential exchanges, bounded deferred issuance
polling, and encrypted OID4VP
direct_post.jwtresponses. These encrypted wallet features require Attesto 2.3.1 or later. - Verify HAIP
x509_hashpresentation requests against explicit DER certificate anchors, bind outer client identities and POST wallet nonces, and use the response encryption key in the mdoc session transcript. - Add opt-in
haip: trueissuer-chain verification for SD-JWT VC and mdoc credentials using explicittrusted_certificates. Verify credentials with the validated leaf key and attach only verified issuer-chain provenance. Recognize the critical mdoc document-signer usage only for mdoc issuer certificates; retain rejection of unknown critical extensions. Fixed-key trust remains the default. Status and revocation policy remain application-owned. - Reject undersized PS256 issuer keys in HAIP issuance and accept a missing SD-JWT issuer claim only after verifying certificate identity with Attesto 2.3.1 or later.
- Evaluate DCQL nested paths, claim alternatives, credential sets and trusted authority constraints before disclosure; validate explicit selections too.
- Add a pure
Wallet.Presentation.build_response/3for consent previews and application-owned transports, a progressive credential-wallet guide and an encrypted presentation example in the interactive Livebook. - Allow Client Attestations to include the trusted attester's
issuer:claim. - Add
WalletAttestation.fetch_challenge/2for proactive attestation challenges and preserve an optional DPoP nonce for subsequent PAR and token requests.
Changed
- When a DCQL query omits
claims, disclose only mandatory presentation contents, as required by final OID4VP §6.4.1. Selectively disclosable claims require an explicit query. - Ignore
issin all signed presentation requests as required by final OID4VP; retain audience, client identity, signature and certificate checks. - Parse encoded OAuth callbacks containing an issuer URL as form bodies, while retaining duplicate-parameter detection and callback URI checks.
- Reject bearer token responses when an authorization-code exchange requests DPoP, and reject a changed or omitted DPoP key when the transaction started with one.
- Bind the requested credential format and returned SD-JWT
vctor mdoc document type to the selected configuration when Credential Issuer configuration metadata is supplied. - Return
:unsupported_mdocwhen the optional CBOR dependency is absent, including before issuance makes network requests. - Use nonempty presentation arrays in both final OID4VP response modes; scalar plaintext inputs are rejected. Deferred credential responses require HTTP 202 and a positive integer interval; reject duplicate response JSON, ambiguous encryption keys, and invalid batch holder bindings.
- Use advertised optional credential encryption under
credential_encryption: :auto; unsupported or malformed advertisements fail closed.:disabledremains an explicit opt-out only when the issuer permits plaintext.
[2.6.1] - 2026-10-04
Security
- Bound compact JWS values and their protected/signature segments before splitting or decoding, require canonical Base64URL, and reject duplicate JSON members at every nesting depth before verifying signed claims.
- Accept an original callback URI or raw form body in the authorization-code client, bound the encoded input to 1,000,000 bytes, and reject repeated or percent-encoded alias parameter names before state is consumed. Map input remains available for integrations whose parser already enforces uniqueness.
- Bound encoded mdoc credentials before Base64URL decoding and normalize signing failures so exception text cannot expose private key material.
- Keep Attesto 1.x compatibility. Deploy with Attesto 2.2.2 or later in the 2.x line to include hardening of delegated core JOSE and credential verification.
[2.6.0] - 2026-10-03
Changed
- Default private-key client assertion audiences to the trusted authorization
server issuer and emit
typ: client-authentication+jwtfor rfc7523bis-11. Passissuer: metadata["issuer"]to select this profile. Calls without an issuer retain endpoint audiences in 2.x and emit deprecation telemetry. Assertiontyp:can be overridden or omitted for a legacy server. - Retry an attestation Challenge or DPoP nonce at most once each, generating fresh proofs and preserving both challenges when the server requires them.
- Validate and expose optional refresh-token expiration response durations.
- Forward DPoP and attestation Challenge callbacks during authorization-code redemption and document browser and cross-device deployment policies.
- Require patched Mint and refresh its development/test lock.
- Keep Hex dependency declarations during package tasks even when
ATTESTO_PATHis inherited from a source-development shell.
[2.5.0] - 2026-08-31
Changed
- Support both Attesto 1.x and 2.x. Attesto 2.0 changes authorization-server storage contracts that AttestoClient does not implement; the protocol and verification APIs used by this package remain compatible.
- The published package dependency is
>= 1.13.0 and < 3.0.0, so ordinary Hex resolution can select either supported Attesto major line. No AttestoClient application-code migration is required; source checkouts may opt into the coordinated Attesto 2 path withATTESTO_PATH=1.
[2.4.1] - 2026-08-27
Security
- Carry the screened IP address through every OAuth, wallet, discovery, and
JWKS request while retaining the external hostname for TLS verification,
SNI, HTTP authority, DPoP
htu, and client-assertion audiences. DPoP nonce retries reuse the same screened address. This closes the remaining DNS- rebinding interval in the shared OAuth HTTP path. - Isolate protected requests from Req application defaults and caller options that can replace routing, credentials, protocol bodies, redirects, TLS peer identity, or the screened origin. Unsafe proxies and incompatible Finch pools now fail closed; URL-keyed Req caching is disabled because different HTTPS authorities can share a pinned IP and path.
- Keep metadata and JWKS retrieval unauthenticated, force discovery requests to use GET, remove hop-by-hop request headers, and include DNS resolution and request preparation within the caller's overall deadline. OAuth POST response bodies are now bounded before decoding as well as by-reference GET bodies.
- Refresh the IPv6 special-purpose address policy from IANA's registry, rejecting the dummy, deprecated protocol-assignment, and documentation ranges and all reserved space outside conventional global unicast, while retaining ordinary globally reachable allocations.
Upgrade notes
- Protected network requests now reject caller-owned proxies, Finch pools,
adapters, and Unix sockets because they can bypass the screened destination
or its TLS identity. Applications that previously supplied one of those
routing overrides will receive
{:error, :unsafe_transport_options}and should move controlled egress routing outside the request process.
[2.4.0] - 2026-08-11
Security
- Pin discovery and JWKS connections to an address that was screened against
special-use networks, while retaining TLS SNI, certificate verification, and
the HTTP Host authority on the original hostname. This closes the DNS-
rebinding interval between the prior pre-flight lookup and the socket connect.
A host that resolves to no address is now rejected (
:blocked_host) rather than handed to the transport — the screening path is fail-closed. - Bound discovery and JWKS response bodies before JSON decoding and disable transparent response decompression on those trust-root fetches.
- Reject unsafe RSA modulus/exponent parameters from a remote JWKS before JOSE constructs bignums or attempts signature verification, preventing scheduler- pinning verification work.
- Require
attesto >= 1.13.0, carrying the matching raw-key bounds and current parser, wallet, status-list, and ID-JAG hardening into every client installation. - Make the ID-JAG builder enforce the server's signed-claim constraints before
signing: a 256-byte
jticap, validscopeandresource, exactly one supportedcnf.jktmember, and rejection of unsupportedauthorization_details.
[2.3.1] - 2026-08-03
Added
- Add an end-to-end digital wallet Livebook (
guides/digital_wallet.livemd), now rendered in the docs. Issue an SD-JWT VC PID credential, hold it, and present it to a verifier with an interactive selective-disclosure form — request onlyage_over_21and watch name and birthdate stay in the wallet. Runs entirely on the publishedattesto+attesto_clientlibraries.
[2.3.0] - 2026-08-03
Added
- Add the OID4VCI/OID4VP wallet-holder role.
AttestoClient.Walletdrives pre-authorized_code issuance end to end — credential offer, token exchange,c_nonce, holder key proof, Credential Request, and verification of each returned credential (SD-JWT VC,jwt_vc_json, or mdoc) — with support for OID4VCI 1.0 final's pluralproofs, batch issuance (a list of holder keys → one credential per key), and the §10 Notification Endpoint. - Add
AttestoClient.DPoP(RFC 9449 proof generation), threaded through the token and credential requests via a:dpopoption with ause_dpop_noncesingle retry; a DPoP-bound access token is presented with theDPoPauthentication scheme. - Add
AttestoClient.WalletAttestation(OAuth Attestation-Based Client Authentication — Client Attestation JWT + per-request PoP) and a{:client_attestation, ...}client-auth method, andAttestoClient.KeyAttestation(OID4VCI Key Attestation) carried in the holder proof'skey_attestationheader. These are the client-side mirrors of the matching attesto verifiers. - Add OID4VP presentation (
AttestoClient.Wallet.Presentation/PresentationRequest): verify a signed Authorization Request, select held credentials by DCQL, and build adirect_postvp_token(SD-JWT VC with a holder Key Binding JWT, or an ISO 18013-5 mdoc DeviceResponse).
Security
- Bind each issued credential to the holder key whose proof requested it
(thumbprint membership, one credential per proof), and fail closed with
:missing_trustedbefore any network call when no issuer trust anchor is supplied. - Scope SD-JWT claim minimisation to top-level Disclosures (digest present in
the issuer payload's
_sd), so a nested claim sharing a requested top-level name is never disclosed; refusedirect_post.jwtinsubmit/3; check the signing key against the credential's holder binding before disclosing any contents; and validate the DCQL query shape so a signed-but-malformed request cannot crash selection. - Refresh client-auth
jtis on a DPoP-nonce retry (no replay), send a single Authorization header, require an explicit audience for the client-attestation PoP, and bound by-reference fetch bodies (with decompression disabled).
[2.2.0] - 2026-07-25
Added
- Support RFC 9864
Ed25519andEd448identifiers across issuer-signed JWT verification and client artifact builders while retaining legacyEdDSA. - Add independent Node crypto parity for Ed25519 and Ed448 signatures and OIDC detached hash claims.
Security
- Bind ID Token
at_hash,c_hash, ands_hashcalculation to the verified issuer JWK. LegacyEdDSAnow selects SHA-512 with a 32-byte left half for Ed25519 and SHAKE256 with 114 bytes of output and a 57-byte left half for Ed448. - Move JARM onto the shared hardened verifier: require a unique eligible key,
honor JWK
alg,use, andkey_ops, reject RSA keys below 2048 bits, bind every algorithm to its key type and curve, and apply FAPI's Ed25519-only Edwards policy by default. An explicitaccepted_algslist remains a deliberate non-FAPI override unlessenforce_fapi_alg_policy: trueis set. - Validate every explicit outbound signing algorithm against its private key before signing, including exact Edwards curve binding.
Changed
- Require Attesto 1.3 or later for key-aware OIDC hashing and trusted-key algorithm validation.
- Keep the JOSE security floor at 1.11.12 while widening the upper-compatible range to all 1.x releases, so a future 1.x release with native OTP SHA-3 and Ed448 detection is reachable without another AttestoClient release.
[2.1.1] - 2026-07-17
Security
- Restrict the optional Plug integration to the security-patched releases on every supported minor line: Plug 1.16.6, 1.17.4, 1.18.5, 1.19.5, and 1.20.3 and later 1.x releases.
- Raise the JOSE floor to 1.11.12, excluding releases affected by
GHSA-9mg4-v392-8j68 / CVE-2023-50966, the 1.11.7-1.11.8 packages that
included development-only tooling as runtime dependencies, and the
1.11.9-1.11.10 releases with broken EC key conversion on OTP 28. JOSE
1.11.11 fixed that conversion but could encode Elixir
nilas the JSON string"nil"with OTP's built-in JSON module; 1.11.12 fixes that too. - Refresh the development lock to Mint 1.9.3, resolving EEF-CVE-2026-59249 / CVE-2026-59249.
- Extend discovery and JWKS SSRF checks across non-global IPv4, translated IPv6, deprecated site-local, and local-use NAT64 destinations while retaining globally reachable anycast and public NAT64 targets.
- Require a replay callback for every DPoP request; the former test-only acknowledgement flag can no longer disable replay protection.
- Keep internal token, DPoP, certificate, and nonce rejection reasons out of OAuth error responses.
[2.1.0] - 2026-07-16
Added
- Add
AttestoClient.ResourceServer, a supervised remote-issuer RFC 9068 JWT access-token verifier. It performs exact issuer and audience checks, strict algorithm/key selection, required claim and time validation, exact scope enforcement, optional subject/client and token-age/lifetime policy, explicit warming/readiness, and fail-closed DPoP/mTLS confirmation binding. - Add coordinated discovery/JWKS caching with bounded key count, configurable
fresh and stale intervals, transient-error-only stale-key use, single-flight
refresh, unknown-
kidrotation refresh, response-size limits, and refresh- storm throttling/backoff. - Add
AttestoClient.ResourceServer.Plugfor Bearer, DPoP, and mTLS protected resources, including fail-closed DPoP replay wiring and RFC 6750 error and insufficient-scope responses. Upstream key availability failures produce a detail-free503rather than aninvalid_tokenchallenge. - Add bidirectional PyJWT parity coverage for RFC 9068 signing and verification,
including an independently signed unknown-
kidkey-rotation flow.
Security
- Remote discovery and JWKS refreshes retain HTTPS, exact-issuer, SSRF, and redirect protections; disable Req retries and bound each coordinated refresh with an application-configured deadline.
[2.0.0] - 2026-07-16
Security
- Add a complete OpenID Connect Authorization Code flow that always uses S256 PKCE, binds callbacks to high-entropy state, nonce, and a mandatory opaque application browser-session value, validates the response issuer when supplied, atomically consumes expiring transaction state, pins the registered ID Token algorithm, and gives the code exchange a bounded deadline with retries disabled.
- ID Token verification now rejects ambiguous eligible JWKS keys and RSA keys
below 2048 bits, honors JWK
use/key_ops, validates optionalnbf, and uses constant-time nonce/subject comparisons. A missingkidremains valid only when exactly one eligible verification key exists. - Add bounded single-flight refresh-token rotation. Concurrent callers for one application key share one request and one result; timeout and worker failure wake all waiters and clear the flight.
- Require HTTPS redirect URIs except for native-client loopback HTTP redirects.
- Raise the Req dependency floor to 0.6.1, the first release patched for EEF-CVE-2026-49755 decompression-bomb denial of service.
Added
AttestoClient.AuthorizationTransaction.Storeand a bounded, single-node ETS implementation with atomic insert/consume and monotonic expiry.AttestoClient.Tokenrefresh and RFC 7009 revocation operations,AttestoClient.RefreshCoordinator,AttestoClient.TokenSet, and verified refresh ID Token results.AttestoClient.Logout.url/1for OpenID Connect RP-Initiated Logout.- Structural discovery validation for required OIDC endpoints and capability fields before an authorization flow uses them.
- Adversarial coverage for replay and concurrent state consumption, expiry, issuer/audience/nonce confusion, ambiguous or ineligible keys, weak RSA, malformed discovery, refresh races, independent refreshes, and deadlines.
- Authorization-request
max_ageis retained in the transaction and enforced against the callback ID Token'sauth_time.
Changed
- Supplying
:access_tokentoAttestoClient.IDToken.verify/2validatesat_hashwhen present but no longer requires the claim for a token-endpoint ID Token, where OIDC Core permits omission. Passrequire_at_hash: truefor front-channel or profile rules that require it. - Authorization decisions, durable token persistence, refresh-result compare-and-swap, and session-retention/termination remain application-owned.
- The Elixir floor remains 1.18: this package and its required
attestodependency both depend on Elixir's built-inJSONmodule.
Migration
AttestoClient.AuthorizationCode.start/2andcallback/3now require the same opaque:browser_binding. Generate or retain it in the initiating user agent's secure application session; callbacks with a missing or different binding consume state and fail before token exchange.- The key-selection and minimum-RSA checks intentionally reject tokens that 1.x
could accept. Applications with duplicate
kidvalues, multiple eligible kid-less keys, encryption-only verification keys, or RSA keys below 2048 bits must correct their JWKS before upgrading. - Because those security checks tighten existing public verification APIs, this release is a 2.0.0 major release.
[1.1.0] - 2026-07-07
Changed
AttestoClient.Discovery.fetch/2now compares the document'sissuerexactly against the supplied issuer (RFC 8414 §3.3 / OpenID Connect Discovery 1.0 §4.3) instead of normalising a trailing slash away. A slash-terminated path issuer (e.g. a multi-tenant issuer, or the OpenID conformance suite'shttps://.../test/a/<alias>/) was previously rejected with:issuer_mismatch; conversely, two identifiers that differ only by a trailing slash no longer match. The trailing slash is still removed when constructing the well-known request URL, as both specs require.
Added
AttestoClient.IDToken.verify/2acceptsallow_unsigned: true, an explicit opt-in for the OIDC Core §3.1.3.7 case: a client that registeredid_token_signed_response_algnoneand received the ID Token directly from the token endpoint over TLS may accept an unsigned (alg: "none") token. All claim checks still run; the signature part must be empty; signed tokens are unaffected; the default remains to reject unsigned tokens.
[1.0.0] - 2026-07-04
First stable release; the public API is now under semantic versioning. No
functional change from 0.6.0. Requires attesto ~> 1.0.
[0.6.0] - 2026-06-21
Security
AttestoClient.Discoveryhardens its discovery/JWKS fetches against SSRF: redirects are no longer followed (a 3xx surfaces as{:http_status, _}rather than being chased to itsLocation), and a URL whose host resolves to a loopback, private, link-local, or unique-local address is rejected with:blocked_host— so an attacker-influencedissuer/jwks_uricannot point a server-side fetch at an internal service or the cloud metadata endpoint. An unresolvable host is left to the transport.
Added
AttestoClient.IDToken- verify OpenID Connect ID Tokens against authorization server JWKS/discovery, including issuer, audience,azp, expiration, issued-at, nonce,max_age/auth_time, and detachedat_hash/c_hash/s_hashvalidation. Interop-tested againstAttesto.IDToken.mint/4.AttestoClient.IdentityAssertion- build Identity Assertion JWT Authorization Grant assertions (ID-JAG / EMA) with theoauth-id-jag+jwtheader and the requirediss/sub/aud/client_id/jti/iat/expclaims. Interop-tested againstAttesto.IdentityAssertion.verify/3.AttestoClient.PKCE- generate S256 PKCE verifier/challenge pairs, delegating challenge computation toAttesto.PKCE.challenge/1so generated pairs verify underAttesto.PKCE.verify/3.AttestoClient.SignedIntrospection- verify RFC 9701 signed token introspection responses against authorization-server JWKS/discovery. Interop-tested againstAttesto.SignedIntrospection.response_jwt/4.AttestoClient.UserInfo- verify signed OpenID Connect UserInfo JWT responses, including issuer/audience/subject checks and optional binding to a previously verified ID Token subject.- Internal AttestoClient.Verifier shared by the AS-signed JWT verifiers (not public API; hidden from docs).
AttestoClient.ClientAssertion- buildprivate_key_jwtclient authentication assertions (RFC 7523 / OpenID Connect Core §9), signed with the client's own key. Carries a cross-language parity test against an independent PyJWT reference verifier, plus in-family interop againstAttesto.ClientAssertion.verify/5.AttestoClient.RequestObject- build signed authorization request objects (JAR, RFC 9101 / FAPI 2.0 Message Signing §5.3.1): the caller's authorization parameters wrapped with the iss/aud/iat/nbf/exp/jti envelope and theoauth-authz-req+jwttyp, signed with the client's key. The lifetime is bounded to the FAPI 60-minute window. Parity-tested against an independent PyJWT reference and in-family againstAttesto.RequestObject.verify/3under the FAPI Message Signing policy.- Internal AttestoClient.Builder shared by the builders (not public API; hidden from docs).
AttestoClient.JARM- verify a signed authorization response (JARM, FAPI 2.0 Message Signing §5.4): JWS signature against the authorization server's JWKS (FAPI algorithm allow-list,nonerejected, kid selection), plusiss/aud/iat/exp, returning the response parameters. Parity-tested by verifying a JARM token signed by an independent PyJWT signer (the flipped external direction) and one signed byAttesto.JARM.response_jwt/4(in-family).AttestoClient.Discovery- fetch and read OAuth 2.0 / OpenID Connect authorization-server metadata and JWKS (RFC 8414 / OpenID Connect Discovery 1.0) overReq, withhttpsand RFC 8414 §3.3 issuer-match validation. Verified in-family againstAttesto.OpenIDDiscovery.metadata/2output.
Changed
- Require
attesto ~> 0.9so the client mirror can use the current ID Token, ID-JAG, PKCE, signed introspection, signing-algorithm, and hash primitives.