Module: PlatformSdk::Identity::ErrorHandleable
- Included in:
- AuthClient, Client, Zitadel::AuthClient, Zitadel::Client
- Defined in:
- lib/platform_sdk/identity/error_handleable.rb
Overview
Error handling for Identity SDK
Constant Summary collapse
- SENSITIVE_KEYS =
The logged payload is the raw request body, which for token endpoints carries client credentials; redact those fields (form-encoded and JSON shapes) so an auth outage can't write secrets to consumer logs.
'client_secret|client_assertion|refresh_token|password'- FORM_SECRET_PATTERN =
/(#{SENSITIVE_KEYS})=[^&]*/i- JSON_SECRET_PATTERN =
/("(?:#{SENSITIVE_KEYS})"\s*:\s*")[^"]*(")/i
Instance Method Summary collapse
-
#error_log_payload(exception_class, error) ⇒ Object
No ActiveSupport here (String#demodulize): this must work for non-Rails consumers, where the log line is skipped but this hash is still built.
- #raise_error_with_payload(exception_class, error) ⇒ Object
-
#scrub_headers(headers) ⇒ Object
dup (not a rebuilt Hash) so a Faraday::Utils::Headers keeps its class and case-insensitive lookup on the scrubbed copy.
- #scrub_request(request) ⇒ Object
-
#scrub_response(response) ⇒ Object
Returns a scrubbed copy of the Faraday error response hash; the original (and its nested :request hash) are left untouched.
- #scrub_secrets(body) ⇒ Object
- #with_rescue ⇒ Object
Instance Method Details
#error_log_payload(exception_class, error) ⇒ Object
No ActiveSupport here (String#demodulize): this must work for non-Rails consumers, where the log line is skipped but this hash is still built.
36 37 38 39 40 41 42 43 |
# File 'lib/platform_sdk/identity/error_handleable.rb', line 36 def error_log_payload(exception_class, error) { exception: exception_class.name.split('::').last, payload: scrub_secrets(error.response.dig(:request, :body)), response_body: error.response[:body], status: error.response[:status] }.compact end |
#raise_error_with_payload(exception_class, error) ⇒ Object
24 25 26 27 28 29 30 31 |
# File 'lib/platform_sdk/identity/error_handleable.rb', line 24 def raise_error_with_payload(exception_class, error) Rails.logger.info(error_log_payload(exception_class, error).to_json) if defined?(Rails) # The exception itself carries error.response on to consumer # instrumentation (Sentry, custom error handlers), so the raw # request body/headers - which can hold a client_secret or bearer # token - must be scrubbed here too, not just in the log line above. raise exception_class, scrub_response(error.response) end |
#scrub_headers(headers) ⇒ Object
dup (not a rebuilt Hash) so a Faraday::Utils::Headers keeps its class and case-insensitive lookup on the scrubbed copy.
75 76 77 78 79 80 81 |
# File 'lib/platform_sdk/identity/error_handleable.rb', line 75 def scrub_headers(headers) scrubbed = headers.dup scrubbed.each_key do |key| scrubbed[key] = '[REDACTED]' if key.to_s.casecmp('authorization').zero? end scrubbed end |
#scrub_request(request) ⇒ Object
66 67 68 69 70 71 |
# File 'lib/platform_sdk/identity/error_handleable.rb', line 66 def scrub_request(request) scrubbed_request = request.dup scrubbed_request[:body] = scrub_secrets(request[:body]) if request[:body].is_a?(String) scrubbed_request[:headers] = scrub_headers(request[:headers]) if request[:headers].is_a?(Hash) scrubbed_request end |
#scrub_response(response) ⇒ Object
Returns a scrubbed copy of the Faraday error response hash; the original (and its nested :request hash) are left untouched. Only [:request][:body] and [:request][:headers] are touched - [:status] and [:body] (the response body) are read elsewhere (e.g. e.response&.dig(:status), e.response&.dig(:body, 'paramName')) and must reach consumers unchanged.
58 59 60 61 62 63 64 |
# File 'lib/platform_sdk/identity/error_handleable.rb', line 58 def scrub_response(response) return response unless response.is_a?(Hash) scrubbed = response.dup scrubbed[:request] = scrub_request(response[:request]) if response[:request].is_a?(Hash) scrubbed end |
#scrub_secrets(body) ⇒ Object
45 46 47 48 49 50 |
# File 'lib/platform_sdk/identity/error_handleable.rb', line 45 def scrub_secrets(body) return body unless body.is_a?(String) body.gsub(FORM_SECRET_PATTERN) { "#{Regexp.last_match(1)}=[REDACTED]" } .gsub(JSON_SECRET_PATTERN, '\1[REDACTED]\2') end |
#with_rescue ⇒ Object
14 15 16 17 18 19 20 21 22 |
# File 'lib/platform_sdk/identity/error_handleable.rb', line 14 def with_rescue yield rescue Faraday::TimeoutError => e raise TimeoutError, e rescue Faraday::ServerError => e raise_error_with_payload(ServerError, e) rescue Faraday::ClientError => e raise_error_with_payload(ClientError, e) end |