Class: Ksef::Auth::TokenRequest

Inherits:
Object
  • Object
show all
Defined in:
lib/ksef/auth/token_request.rb

Overview

The AuthTokenRequest document — step 2 of the authentication flow (docs/REFERENCE.md §4.2). Built here, then XAdES-signed and submitted to POST /auth/xades-signature. #document exposes the mutable tree because an enveloped signature has to be inserted into the document — Signer cannot assemble a signed request from a string afterwards.

Defaults to the 2.0 namespace, matching both official clients and every upstream example; schema_version: selects 2.1. See NAMESPACES for why, and §14.4 for why validation nonetheless uses v2.1's rules.

Constant Summary collapse

ROOT =
"AuthTokenRequest"
SUBJECT_IDENTIFIER_TYPES =
%w[certificateSubject certificateFingerprint].freeze
CONTEXT_TYPES =

A choice of four, in schema order. The last two cannot hold their real-world values because of an upstream regex defect (§14.4); they are still offered, because the server looks up the actual identifier and emitting the absurd value the facet wants would be worse than failing a local check.

%i[nip internal_id nip_vat_ue peppol_id].freeze
CONTEXT_ELEMENTS =
{
  nip: "Nip", internal_id: "InternalId", nip_vat_ue: "NipVatUe", peppol_id: "PeppolId"
}.freeze
VALIDATABLE_CONTEXT_TYPES =

Context types whose schema pattern is intact, and for which #validate! is therefore meaningful rather than advisory.

%i[nip internal_id].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(challenge:, context_type:, context_value:, subject_identifier_type: "certificateSubject", allowed_ips: nil, schema_version: DEFAULT_SCHEMA_VERSION) ⇒ TokenRequest

Returns a new instance of TokenRequest.

Parameters:

  • challenge (String) —

    verbatim from POST /auth/challenge

  • context_type (Symbol) —
  • context_value (String) —

    the identifier itself

  • subject_identifier_type (String) (defaults to: "certificateSubject") —

    "certificateSubject" or "certificateFingerprint" — how KSeF should read the signer's identity out of the signing certificate (§4.4)

  • allowed_ips (Hash, AuthorizationPolicy, nil) (defaults to: nil) —

    optional client-IP whitelist, with any of :addresses, :ranges, :masks

  • schema_version (String) (defaults to: DEFAULT_SCHEMA_VERSION) —

    "2.0" (default) or "2.1"



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/ksef/auth/token_request.rb', line 47

def initialize(challenge:, context_type:, context_value:,
               subject_identifier_type: "certificateSubject", allowed_ips: nil,
               schema_version: DEFAULT_SCHEMA_VERSION)
  @namespace = NAMESPACES.fetch(schema_version) do
    raise ValidationError,
          "Unknown schema version #{schema_version.inspect}. " \
          "Expected one of #{NAMESPACES.keys.map(&:inspect).join(", ")}."
  end
  @schema_version = schema_version
  @challenge = Challenge.validate_format!(challenge)
  @context_type = validate_context_type(context_type)
  @context_value = context_value
  @subject_identifier_type = validate_subject_identifier_type(subject_identifier_type)
  @allowed_ips = AuthorizationPolicy.coerce(allowed_ips)
  freeze
end

Instance Attribute Details

#allowed_ips ⇒ Object (readonly)

Returns the value of attribute allowed_ips.



35
36
37
# File 'lib/ksef/auth/token_request.rb', line 35

def allowed_ips
  @allowed_ips
end

#challenge ⇒ Object (readonly)

Returns the value of attribute challenge.



35
36
37
# File 'lib/ksef/auth/token_request.rb', line 35

def challenge
  @challenge
end

#context_type ⇒ Object (readonly)

Returns the value of attribute context_type.



35
36
37
# File 'lib/ksef/auth/token_request.rb', line 35

def context_type
  @context_type
end

#context_value ⇒ Object (readonly)

Returns the value of attribute context_value.



35
36
37
# File 'lib/ksef/auth/token_request.rb', line 35

def context_value
  @context_value
end

#namespace ⇒ Object (readonly)

Returns the value of attribute namespace.



35
36
37
# File 'lib/ksef/auth/token_request.rb', line 35

def namespace
  @namespace
end

#schema_version ⇒ Object (readonly)

Returns the value of attribute schema_version.



35
36
37
# File 'lib/ksef/auth/token_request.rb', line 35

def schema_version
  @schema_version
end

#subject_identifier_type ⇒ Object (readonly)

Returns the value of attribute subject_identifier_type.



35
36
37
# File 'lib/ksef/auth/token_request.rb', line 35

def subject_identifier_type
  @subject_identifier_type
end

Instance Method Details

#document ⇒ Nokogiri::XML::Document

Returns the unsigned document, for Signer to sign in place — an enveloped signature has to be added to the tree, not to a string.

Returns:

  • (Nokogiri::XML::Document) —

    the unsigned document, for Signer to sign in place — an enveloped signature has to be added to the tree, not to a string



70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/ksef/auth/token_request.rb', line 70

def document
  Nokogiri::XML::Document.new.tap do |doc|
    doc.encoding = "UTF-8"
    root = doc.create_element(ROOT)
    root.default_namespace = namespace
    doc.root = root

    add_text(doc, root, "Challenge", challenge)
    root.add_child(context_element(doc))
    add_text(doc, root, "SubjectIdentifierType", subject_identifier_type)
    root.add_child(policy_element(doc)) if allowed_ips
  end
end

#to_xml ⇒ Object



64
65
66
# File 'lib/ksef/auth/token_request.rb', line 64

def to_xml
  document.to_xml(indent: 2, encoding: "UTF-8")
end

#valid? ⇒ Boolean

Returns:

  • (Boolean)


87
# File 'lib/ksef/auth/token_request.rb', line 87

def valid? = Validator.valid?(to_xml)

#validatable? ⇒ Boolean

True when #validate! is a real check rather than one the upstream schema cannot express (§14.4).

Returns:

  • (Boolean)


91
# File 'lib/ksef/auth/token_request.rb', line 91

def validatable? = VALIDATABLE_CONTEXT_TYPES.include?(context_type)

#validate! ⇒ Object

Raises:



85
# File 'lib/ksef/auth/token_request.rb', line 85

def validate! = Validator.validate!(to_xml, advisory: advisory)