Module: TwoFactorAuth

Defined in:
app/models/two_factor_auth/registration_response.rb,
lib/two_factor_auth.rb,
lib/two_factor_auth/engine.rb,
lib/two_factor_auth/version.rb,
app/models/two_factor_auth/client_data.rb,
app/models/two_factor_auth/registration.rb,
app/models/two_factor_auth/registration_request.rb,
app/helpers/two_factor_auth/registrations_helper.rb,
app/models/two_factor_auth/registration_verifier.rb,
lib/generators/two_factor_auth/install_generator.rb,
app/models/two_factor_auth/authentication_request.rb,
app/helpers/two_factor_auth/authentications_helper.rb,
app/models/two_factor_auth/authentication_response.rb,
app/models/two_factor_auth/authentication_verifier.rb,
app/models/two_factor_auth/authentication_client_data.rb,
app/controllers/two_factor_auth/registrations_controller.rb,
app/controllers/two_factor_auth/trusted_facets_controller.rb,
app/controllers/two_factor_auth/authentications_controller.rb,
app/controllers/two_factor_auth/two_factor_auth_controller.rb

Overview

See FIDO U2F “Raw Message Formats” documentation, section 4.3 “Registration Response Message: Success”

Defined Under Namespace

Modules: AuthenticationsHelper, Generators, RegistrationsHelper Classes: AuthenticationClientData, AuthenticationRequest, AuthenticationResponse, AuthenticationVerifier, AuthenticationsController, CantGenerateRandomNumbers, ClientData, Engine, InvalidFacetDomain, InvalidFacetListDomain, InvalidPublicKey, InvalidTrustedFacetListUrl, Registration, RegistrationRequest, RegistrationResponse, RegistrationVerifier, RegistrationsController, TrustedFacetsController, TwoFactorAuthController, TwoFactorAuthError

Constant Summary collapse

U2F_VERSION =
'U2F_V2'
VERSION =
"0.2.0"

Class Method Summary collapse

Class Method Details

.app_id_or_facet_listObject

We send clients the facet domain (web origin) if there are no facets, (typical for local dev), otherwise the full facet list (prohibits http)



48
49
50
51
52
53
54
# File 'lib/two_factor_auth.rb', line 48

def self.app_id_or_facet_list
  if facets.empty?
    facet_domain
  else
    "#{facet_domain}/two_factor_auth/trusted_facets"
  end
end

.decode_pubkey(raw) ⇒ Object



89
90
91
92
93
94
95
# File 'lib/two_factor_auth.rb', line 89

def self.decode_pubkey raw
  bn = OpenSSL::BN.new(raw, 2)
  group = OpenSSL::PKey::EC::Group.new('prime256v1')
  point = OpenSSL::PKey::EC::Point.new(group, bn)
rescue OpenSSL::PKey::EC::Point::Error => e
  raise InvalidPublicKey, "Invalid public key: #{e.message}"
end

.facet_domainObject



31
32
33
# File 'lib/two_factor_auth.rb', line 31

def self.facet_domain
  @facet_domain
end

.facet_domain=(facet_domain) ⇒ Object



20
21
22
23
24
25
26
27
28
29
# File 'lib/two_factor_auth.rb', line 20

def self.facet_domain= facet_domain
  if facet_domain =~ /\.dev(:\d+)?\/?$/
    raise InvalidFacetDomain, "Facet domain needs a real TLD, not .dev. Edit /etc/hosts to make a custom hostname"
  end
  if facet_domain == "https://www.example.com"
    raise InvalidFacetDomain, "You need to cusomize the facet_domain in config/initializers/two_factor_auth.rb"
  end

  @facet_domain = facet_domain.sub(/\/$/, '')
end

.facetsObject



67
68
69
# File 'lib/two_factor_auth.rb', line 67

def self.facets
  @facets ||= []
end

.facets=(facets) ⇒ Object



56
57
58
59
60
61
62
63
64
65
# File 'lib/two_factor_auth.rb', line 56

def self.facets= facets
  facets ||= []
  if !facets.empty? and facet_domain !~ /\Ahttps:/
    raise InvalidFacetDomain, "U2F requires HTTPS to use facet lists"
  end
  if facets.any? { |f| f !~ /\Ahttps:/ }
    raise InvalidFacetListDomain, "URLs in the facet list must all be HTTPS"
  end
  @facets = facets
end

.pubkey_valid?(raw) ⇒ Boolean

Returns:

  • (Boolean)


97
98
99
100
101
102
# File 'lib/two_factor_auth.rb', line 97

def self.pubkey_valid? raw
  pk = decode_pubkey raw
  pk.on_curve?
rescue InvalidPublicKey
  false
end

.random_encoded_challengeObject



82
83
84
85
86
87
# File 'lib/two_factor_auth.rb', line 82

def self.random_encoded_challenge
  random = OpenSSL::Random.pseudo_bytes(32)
  TwoFactorAuth::websafe_base64_encode(random)
rescue OpenSSL::Random::RandomError
  raise CantGenerateRandomNumbers, "Not enough entropy to generate secure challenges"
end

.trusted_facet_list_urlObject



42
43
44
# File 'lib/two_factor_auth.rb', line 42

def self.trusted_facet_list_url
  @trusted_facet_list_url or app_id_or_facet_list
end

.trusted_facet_list_url=(url) ⇒ Object



35
36
37
38
39
40
# File 'lib/two_factor_auth.rb', line 35

def self.trusted_facet_list_url= url
  if url !~ /\Ahttps:/
    raise InvalidTrustedFacetListUrl, "U2F requires HTTPS for facet urls"
  end
  @trusted_facet_list_url = url
end

.websafe_base64_decode(encoded) ⇒ Object



76
77
78
79
80
# File 'lib/two_factor_auth.rb', line 76

def self.websafe_base64_decode encoded
  # pad back out to decode
  padded = encoded.ljust((encoded.length/4.0).ceil * 4, '=')
  Base64.urlsafe_decode64(padded)
end

.websafe_base64_encode(str) ⇒ Object



71
72
73
74
# File 'lib/two_factor_auth.rb', line 71

def self.websafe_base64_encode str
  # PHP code removes trailing =s, don't know why
  Base64.urlsafe_encode64(str).sub(/=+$/,'')
end