@@ -37,13 +37,15 @@ defmodule Lockspire.Protocol.Jarm.ClientKeyResolver do
3737 end
3838 end
3939
40- defp do_resolve ( % Client { jwks_uri: jwks_uri } = _client , params , opts ) when is_binary ( jwks_uri ) do
40+ defp do_resolve ( % Client { jwks_uri: jwks_uri } = client , params , opts ) when is_binary ( jwks_uri ) do
4141 fetcher = Keyword . get ( opts , :jwks_fetcher , Config . jwks_fetcher ( ) )
42+ opts = Keyword . put_new ( opts , :client , client )
4243
4344 with { :ok , jwk_set } <- fetcher . get_keys ( jwks_uri , jwks_fetcher_opts ( opts ) ) ,
4445 { _modules , jwks } <- JOSE.JWK . to_map ( jwk_set ) do
4546 case select_key ( jwks , params ) do
4647 { :ok , jwk } ->
48+ clear_remote_jwks_diagnostic ( client_from_opts_or_nil ( opts ) , opts )
4749 { :ok , jwk , :jwks_uri }
4850
4951 { :error , :jarm_encryption_key_unavailable } ->
@@ -53,7 +55,9 @@ defmodule Lockspire.Protocol.Jarm.ClientKeyResolver do
5355 { :error , { :jwks_fetch_failed , _reason } = fetch_error } ->
5456 emit_remote_failure (
5557 :jarm_encryption_key_fetch_failed ,
56- RemoteJwks . classify_fetch_error ( :jarm , fetch_error )
58+ RemoteJwks . classify_fetch_error ( :jarm , fetch_error ) ,
59+ client ,
60+ opts
5761 )
5862
5963 { :error , :jarm_encryption_key_fetch_failed }
@@ -74,6 +78,7 @@ defmodule Lockspire.Protocol.Jarm.ClientKeyResolver do
7478
7579 case select_key ( jwks , params ) do
7680 { :ok , jwk } ->
81+ clear_remote_jwks_diagnostic ( client_from_opts_or_nil ( opts ) , opts )
7782 { :ok , jwk , :jwks_uri }
7883
7984 { :error , :jarm_encryption_key_unavailable } ->
@@ -86,7 +91,9 @@ defmodule Lockspire.Protocol.Jarm.ClientKeyResolver do
8691 :requested_kid_present_in_cached_set? ,
8792 requested_kid_present? ( jwks , params )
8893 )
89- )
94+ ) ,
95+ client_from_opts_or_nil ( opts ) ,
96+ opts
9097 )
9198
9299 { :error , :jarm_encryption_key_unavailable }
@@ -95,7 +102,9 @@ defmodule Lockspire.Protocol.Jarm.ClientKeyResolver do
95102 { :error , { :jwks_fetch_failed , _reason } = fetch_error } ->
96103 emit_remote_failure (
97104 :jarm_encryption_key_fetch_failed ,
98- RemoteJwks . classify_fetch_error ( :jarm , fetch_error , remote_opts )
105+ RemoteJwks . classify_fetch_error ( :jarm , fetch_error , remote_opts ) ,
106+ client_from_opts_or_nil ( opts ) ,
107+ opts
99108 )
100109
101110 { :error , :jarm_encryption_key_fetch_failed }
@@ -180,6 +189,47 @@ defmodule Lockspire.Protocol.Jarm.ClientKeyResolver do
180189 Observability . emit ( :jarm , :failed , % { } , metadata )
181190 end
182191
192+ defp emit_remote_failure ( reason_code , remote_jwks_incident , % Client { } = client , opts ) do
193+ emit_remote_failure ( reason_code , remote_jwks_incident )
194+ persist_remote_jwks_diagnostic ( client , remote_jwks_incident , opts )
195+ end
196+
197+ defp client_from_opts_or_nil ( opts ) , do: Keyword . get ( opts , :client )
198+
199+ defp persist_remote_jwks_diagnostic ( % Client { } = client , % RemoteJwks { } = incident , opts ) do
200+ with store when not is_nil ( store ) <- Keyword . get ( opts , :client_store , Config . repo! ( ) ) ,
201+ true <- function_exported? ( store , :update_client , 2 ) do
202+ metadata =
203+ client . metadata
204+ |> ensure_metadata ( )
205+ |> Map . put ( "remote_jwks_diagnostic" , RemoteJwks . snapshot ( incident ) )
206+
207+ _ = store . update_client ( client , % { metadata: metadata } )
208+ :ok
209+ else
210+ _other -> :ok
211+ end
212+ end
213+
214+ defp persist_remote_jwks_diagnostic ( _client , _incident , _opts ) , do: :ok
215+
216+ defp clear_remote_jwks_diagnostic ( % Client { } = client , opts ) do
217+ with store when not is_nil ( store ) <- Keyword . get ( opts , :client_store , Config . repo! ( ) ) ,
218+ true <- function_exported? ( store , :update_client , 2 ) ,
219+ metadata when is_map ( metadata ) <- client . metadata ,
220+ true <- Map . has_key? ( metadata , "remote_jwks_diagnostic" ) do
221+ _ = store . update_client ( client , % { metadata: Map . delete ( metadata , "remote_jwks_diagnostic" ) } )
222+ :ok
223+ else
224+ _other -> :ok
225+ end
226+ end
227+
228+ defp clear_remote_jwks_diagnostic ( _client , _opts ) , do: :ok
229+
230+ defp ensure_metadata ( metadata ) when is_map ( metadata ) , do: metadata
231+ defp ensure_metadata ( _metadata ) , do: % { }
232+
183233 defp jwks_fetcher_opts ( opts ) do
184234 Config . jwks_fetcher_opts ( )
185235 |> Keyword . merge ( Keyword . get ( opts , :jwks_fetcher_opts , [ ] ) )
0 commit comments