Class: Ksef::Auth::Client
- Inherits:
-
Object
- Object
- Ksef::Auth::Client
- Defined in:
- lib/ksef/auth/client.rb
Overview
The six authentication endpoint calls: the five HTTP steps of §4.2's flow — its
sixth step, signing, is offline — plus POST /auth/ksef-token for the other
authentication method (docs/REFERENCE.md §4.2, §4.5).
Deliberately thin: it maps requests and responses and does nothing else. Deciding which credential to present, and caching the result, belongs a layer up in Client, which does exactly that.
Namespaced Ksef::Auth::Client rather than folded into Ksef::Client so that each
subsystem's endpoints stay together, matching how the official clients are organised.
Three of these calls are unauthenticated — the challenge and both authentication
submissions declare no security in the contract. Of the rest, #status and
#redeem take the temporary authentication token and #refresh takes the refresh
token, so the bearer is passed per call rather than held by the instance.
Constant Summary collapse
- POLL_INTERVAL =
Polling defaults. There is deliberately no timeout: on DEMO and PROD the operation legitimately stays "in progress" while the certificate's status is checked with its issuer over OCSP/CRL, and the docs say the duration depends on that issuer. A client that gives up after a fixed interval reports failure for authentications that were about to succeed (§4.2).
2
Instance Method Summary collapse
-
#authenticate!(reference_number, token:) ⇒ Object
As #wait_until_complete, but insists on success.
- #challenge ⇒ Challenge
-
#initialize(connection) ⇒ Client
constructor
A new instance of Client.
-
#redeem(token:) ⇒ Tokens
Exchanges a completed authentication for the token pair.
-
#refresh(refresh_token:) ⇒ TokenInfo
A fresh access token.
- #status(reference_number, token:) ⇒ OperationStatus
-
#submit_ksef_token(request) ⇒ Initiation
Submits a KSeF-token authentication.
-
#submit_xades(signed_xml, verify_certificate_chain: nil) ⇒ Initiation
Submits the XAdES-signed
AuthTokenRequest. -
#wait_until_complete(reference_number, token:, interval: POLL_INTERVAL, sleeper: method(:sleep)) {|status| ... } ⇒ OperationStatus
Polls until the operation stops being in progress.
Constructor Details
#initialize(connection) ⇒ Client
Returns a new instance of Client.
31 32 33 |
# File 'lib/ksef/auth/client.rb', line 31 def initialize(connection) @connection = connection end |
Instance Method Details
#authenticate!(reference_number, token:) ⇒ Object
As #wait_until_complete, but insists on success.
109 110 111 112 113 114 115 116 |
# File 'lib/ksef/auth/client.rb', line 109 def authenticate!(reference_number, token:, **, &) result = wait_until_complete(reference_number, token: token, **, &) return result if result.success? detail = result.details.empty? ? "" : " (#{result.details.join("; ")})" raise AuthenticationError, "Authentication #{reference_number} failed with status #{result.code}: #{result.explain}#{detail}" end |
#challenge ⇒ Challenge
36 37 38 |
# File 'lib/ksef/auth/client.rb', line 36 def challenge Challenge.from(post("auth/challenge").body) end |
#redeem(token:) ⇒ Tokens
Exchanges a completed authentication for the token pair. Single-use — a second call with the same authentication token is a 400, so this is never auto-retried (it is a POST, which the retry policy already excludes).
81 82 83 |
# File 'lib/ksef/auth/client.rb', line 81 def redeem(token:) Tokens.from(post("auth/token/redeem", token: token).body) end |
#refresh(refresh_token:) ⇒ TokenInfo
Returns a fresh access token.
86 87 88 |
# File 'lib/ksef/auth/client.rb', line 86 def refresh(refresh_token:) TokenInfo.from(post("auth/token/refresh", token: refresh_token).body["accessToken"]) end |
#status(reference_number, token:) ⇒ OperationStatus
72 73 74 |
# File 'lib/ksef/auth/client.rb', line 72 def status(reference_number, token:) OperationStatus.from(get("auth/#{reference_number}", token: token).body) end |
#submit_ksef_token(request) ⇒ Initiation
Submits a KSeF-token authentication. Like #submit_xades this takes an already-built request — Token#authentication_request assembles it, because the encryption depends on which published key was selected and that is not this layer's decision.
A 400 here carries 21111 for a bad challenge or 21470 for a stale key
identifier; the latter is worth wrapping in
Crypto::PublicKeys#with_key_rotation.
66 67 68 |
# File 'lib/ksef/auth/client.rb', line 66 def submit_ksef_token(request) Initiation.from(post("auth/ksef-token", body: request).body) end |
#submit_xades(signed_xml, verify_certificate_chain: nil) ⇒ Initiation
Submits the XAdES-signed AuthTokenRequest. Returns 202 Accepted; the operation
is asynchronous from here.
46 47 48 49 50 51 52 53 |
# File 'lib/ksef/auth/client.rb', line 46 def submit_xades(signed_xml, verify_certificate_chain: nil) response = @connection.post("auth/xades-signature") do |request| request.params["verifyCertificateChain"] = verify_certificate_chain unless verify_certificate_chain.nil? request.headers["Content-Type"] = "application/xml" request.body = signed_xml end Initiation.from(response.body) end |
#wait_until_complete(reference_number, token:, interval: POLL_INTERVAL, sleeper: method(:sleep)) {|status| ... } ⇒ OperationStatus
Polls until the operation stops being in progress.
96 97 98 99 100 101 102 103 104 |
# File 'lib/ksef/auth/client.rb', line 96 def wait_until_complete(reference_number, token:, interval: POLL_INTERVAL, sleeper: method(:sleep)) loop do current = status(reference_number, token: token) yield current if block_given? return current if current.terminal? sleeper.call(interval) end end |