Class: Darrrr::RecoveryProvider

Inherits:
Object
  • Object
show all
Includes:
CryptoHelper, Provider
Defined in:
lib/darrrr/recovery_provider.rb

Constant Summary collapse

INTEGER_FIELDS =
[:token_max_size]
BASE64_FIELDS =
[:countersign_pubkeys_secp256r1]
URL_FIELDS =
[
  :issuer, :save_token,
  :recover_account, :privacy_policy
]
REQUIRED_FIELDS =
URL_FIELDS + INTEGER_FIELDS + BASE64_FIELDS

Constants included from Constants

Constants::CLOCK_SKEW, Constants::COUNTERSIGNED_RECOVERY_TOKEN_TYPE, Constants::DIGEST, Constants::GROUP, Constants::PRIME_256_V1, Constants::PROTOCOL_VERSION, Constants::RECOVERY_TOKEN_TYPE, Constants::TOKEN_ID_BYTE_LENGTH, Constants::WELL_KNOWN_CONFIG_PATH

Constants included from Provider

Provider::MAX_RECOVERY_PROVIDER_CACHE_LENGTH, Provider::RECOVERY_PROVIDER_CACHE_LENGTH, Provider::REQUIRED_CRYPTO_OPS

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from CryptoHelper

#seal, #unseal

Methods included from Provider

#custom_encryptor=, #encryptor, included, #initialize, #load, #with_encryptor

Instance Attribute Details

#icon_152pxObject

optional field



24
25
26
# File 'lib/darrrr/recovery_provider.rb', line 24

def icon_152px
  @icon_152px
end

#save_token_async_api_iframeObject

optional



19
20
21
# File 'lib/darrrr/recovery_provider.rb', line 19

def save_token_async_api_iframe
  @save_token_async_api_iframe
end

#signing_private_key=(value) ⇒ Object (writeonly)

Sets the attribute signing_private_key

Parameters:

  • value

    the value to set the attribute signing_private_key to.



18
19
20
# File 'lib/darrrr/recovery_provider.rb', line 18

def signing_private_key=(value)
  @signing_private_key = value
end

#token_max_size=(value) ⇒ Object (writeonly)

Sets the attribute token_max_size

Parameters:

  • value

    the value to set the attribute token_max_size to.



18
19
20
# File 'lib/darrrr/recovery_provider.rb', line 18

def token_max_size=(value)
  @token_max_size = value
end

Instance Method Details

#countersign_token(token:, context: nil, options: 0x00) ⇒ Object

Takes a binary representation of a token and signs if for a given account provider. Do not pass in a RecoveryToken object. The wrapping data structure is identical to the structure it’s wrapping in format.

token: the to_binary_s or binary representation of the recovery token context: an arbitrary object that is passed to lower level crypto operations options: the value to set in the options byte field of the recovery

token (defaults to 0x00)

returns a Base64 encoded representation of the countersigned token and the signature over the token.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/darrrr/recovery_provider.rb', line 75

def countersign_token(token:, context: nil, options: 0x00)
  begin
     = RecoveryToken.(token)
  rescue RecoveryTokenSerializationError, UnknownProviderError
    raise TokenFormatError, "Could not determine provider"
  end

  counter_recovery_token = RecoveryToken.build(
    issuer: self,
    audience: ,
    type: COUNTERSIGNED_RECOVERY_TOKEN_TYPE,
    options: options,
  )

  counter_recovery_token.data = token
  seal(counter_recovery_token, context)
end

#encryptor_keyObject



60
61
62
# File 'lib/darrrr/recovery_provider.rb', line 60

def encryptor_key
  :darrrr_recovery_provider_encryptor
end

#recovery_url(token_id) ⇒ Object

The URL representing the location of the token. Used to initiate a recovery.

token_id: the shared ID representing a token.



56
57
58
# File 'lib/darrrr/recovery_provider.rb', line 56

def recovery_url(token_id)
  [self., "?token_id=", URI.escape(token_id)].join
end

#to_hObject

Used to serve content at /.well-known/delegated-account-recovery/configuration



27
28
29
30
31
32
33
34
35
36
37
# File 'lib/darrrr/recovery_provider.rb', line 27

def to_h
  {
    "issuer" => self.issuer,
    "countersign-pubkeys-secp256r1" => self.unseal_keys.dup,
    "token-max-size" => self.token_max_size,
    "save-token" => self.save_token,
    "recover-account" => self.,
    "save-token-async-api-iframe" => self.save_token_async_api_iframe,
    "privacy-policy" => self.privacy_policy
  }
end

#unseal_keys(context = nil) ⇒ Object

The CryptoHelper defines an ‘unseal` method that requires us to define a `unseal_keys` method that will return the set of keys that are valid when verifying the signature on a sealed key.

returns the value of ‘countersign_pubkeys_secp256r1` or executes a proc passing `self` as the first argument.



45
46
47
48
49
50
51
# File 'lib/darrrr/recovery_provider.rb', line 45

def unseal_keys(context = nil)
  if @countersign_pubkeys_secp256r1.respond_to?(:call)
    @countersign_pubkeys_secp256r1.call(context)
  else
    @countersign_pubkeys_secp256r1
  end
end

#validate_recovery_token!(token, context = {}) ⇒ Object

Validate the token according to the processing instructions for the save-token endpoint.

Returns a validated token



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/darrrr/recovery_provider.rb', line 97

def validate_recovery_token!(token, context = {})
  errors = []

  # 1. Authenticate the User. The exact nature of how the Recovery Provider authenticates the User is beyond the scope of this specification.
  # handled in before_filter

  # 4. Retrieve the Account Provider configuration as described in Section 2 using the issuer field of the token as the subject.
  begin
     = RecoveryToken.(token)
  rescue RecoveryTokenSerializationError, UnknownProviderError, TokenFormatError => e
    raise RecoveryTokenError, "Could not determine provider: #{e.message}"
  end

  # 2. Parse the token.
  # 3. Validate that the version value is 0.
  # 5. Validate the signature over the token according to processing rules for the algorithm implied by the version.
  begin
    recovery_token = .unseal(token, context)
  rescue CryptoError => e
    raise RecoveryTokenError.new("Unable to verify signature of token")
  rescue TokenFormatError => e
    raise RecoveryTokenError.new(e.message)
  end

  # 6. Validate that the audience field of the token identifies an origin which the provider considers itself authoritative for. (Often the audience will be same-origin with the Recovery Provider, but other values may be acceptable, e.g. "https://mail.example.com" and "https://social.example.com" may be acceptable audiences for "https://recovery.example.com".)
  unless self.origin == recovery_token.audience
    raise RecoveryTokenError.new("Unnacceptable audience")
  end

  if DateTime.parse(recovery_token.issued_time).utc < (Time.now - CLOCK_SKEW).utc
    raise RecoveryTokenError.new("Issued at time is too far in the past")
  end

  recovery_token
end