lato 3.16.0 → 3.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +15 -0
- data/app/assets/javascripts/lato/controllers/lato_account_webauthn_controller.js +93 -0
- data/app/assets/javascripts/lato/controllers/lato_webauthn_auth_controller.js +110 -0
- data/app/controllers/lato/account_controller.rb +72 -16
- data/app/controllers/lato/authentication_controller.rb +95 -15
- data/app/models/lato/user.rb +131 -1
- data/app/views/lato/account/_form-authenticator.html.erb +4 -3
- data/app/views/lato/account/_form-webauthn.html.erb +66 -0
- data/app/views/lato/account/index.html.erb +17 -6
- data/app/views/lato/authentication/_form-authentication-method.html.erb +36 -0
- data/app/views/lato/authentication/_form-webauthn.html.erb +27 -0
- data/app/views/lato/authentication/authentication_method.html.erb +10 -0
- data/app/views/lato/authentication/webauthn.html.erb +10 -0
- data/app/views/layouts/lato/_action.html.erb +1 -1
- data/config/locales/en.yml +27 -3
- data/config/locales/fr.yml +27 -3
- data/config/locales/it.yml +27 -3
- data/config/locales/ro.yml +27 -3
- data/config/routes.rb +6 -1
- data/db/migrate/20251206170443_add_webauthn_id_to_user.rb +6 -0
- data/lib/lato/config.rb +11 -3
- data/lib/lato/engine.rb +11 -0
- data/lib/lato/version.rb +1 -1
- metadata +24 -2
data/app/models/lato/user.rb
CHANGED
|
@@ -14,6 +14,7 @@ module Lato
|
|
|
14
14
|
validates :accepted_privacy_policy_version, presence: true
|
|
15
15
|
validates :accepted_terms_and_conditions_version, presence: true
|
|
16
16
|
validates :web3_address, uniqueness: true, allow_blank: true
|
|
17
|
+
validates :webauthn_id, uniqueness: true, allow_blank: true
|
|
17
18
|
|
|
18
19
|
# Relations
|
|
19
20
|
##
|
|
@@ -58,6 +59,10 @@ module Lato
|
|
|
58
59
|
!authenticator_secret.blank?
|
|
59
60
|
end
|
|
60
61
|
|
|
62
|
+
def webauthn_enabled?
|
|
63
|
+
webauthn_id.present? && webauthn_public_key.present?
|
|
64
|
+
end
|
|
65
|
+
|
|
61
66
|
# Helpers
|
|
62
67
|
##
|
|
63
68
|
|
|
@@ -251,7 +256,9 @@ module Lato
|
|
|
251
256
|
c_password_update_code('')
|
|
252
257
|
|
|
253
258
|
update(params.permit(:password, :password_confirmation).merge(
|
|
254
|
-
authenticator_secret: nil # Reset authenticator secret when password is updated
|
|
259
|
+
authenticator_secret: nil, # Reset authenticator secret when password is updated
|
|
260
|
+
webauthn_id: nil, # Reset webauthn credential when password is updated
|
|
261
|
+
webauthn_public_key: nil # Reset webauthn credential when password is updated
|
|
255
262
|
))
|
|
256
263
|
end
|
|
257
264
|
|
|
@@ -320,6 +327,93 @@ module Lato
|
|
|
320
327
|
true
|
|
321
328
|
end
|
|
322
329
|
|
|
330
|
+
def webauthn_registration_options
|
|
331
|
+
WebAuthn::Credential.options_for_create(
|
|
332
|
+
user: {
|
|
333
|
+
id: Base64.strict_encode64(webauthn_user_handle),
|
|
334
|
+
name: email,
|
|
335
|
+
display_name: full_name
|
|
336
|
+
},
|
|
337
|
+
exclude: webauthn_exclude_credentials.map { |cred| Base64.strict_encode64(cred) }
|
|
338
|
+
)
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def webauthn_authentication_options
|
|
342
|
+
WebAuthn::Credential.options_for_get(
|
|
343
|
+
allow: webauthn_allow_credentials.map { |cred| Base64.strict_encode64(cred) }
|
|
344
|
+
)
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def register_webauthn_credential(credential_payload, encoded_challenge)
|
|
348
|
+
if credential_payload.blank?
|
|
349
|
+
errors.add(:base, :webauthn_payload_missing)
|
|
350
|
+
return false
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
if encoded_challenge.blank?
|
|
354
|
+
errors.add(:base, :webauthn_challenge_missing)
|
|
355
|
+
return false
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
parsed_payload = JSON.parse(credential_payload)
|
|
359
|
+
credential = WebAuthn::Credential.from_create(parsed_payload)
|
|
360
|
+
credential.verify(Base64.strict_decode64(encoded_challenge))
|
|
361
|
+
|
|
362
|
+
update(
|
|
363
|
+
webauthn_id: Base64.strict_encode64(credential.raw_id),
|
|
364
|
+
webauthn_public_key: credential.public_key
|
|
365
|
+
)
|
|
366
|
+
rescue JSON::ParserError, WebAuthn::Error => e
|
|
367
|
+
Rails.logger.error(e)
|
|
368
|
+
errors.add(:base, :webauthn_registration_failed)
|
|
369
|
+
false
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def webauthn_authentication(params, encoded_challenge)
|
|
373
|
+
return false unless webauthn_enabled?
|
|
374
|
+
|
|
375
|
+
if params[:webauthn_credential].blank?
|
|
376
|
+
errors.add(:base, :webauthn_payload_missing)
|
|
377
|
+
return false
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
if encoded_challenge.blank?
|
|
381
|
+
errors.add(:base, :webauthn_challenge_missing)
|
|
382
|
+
return false
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
parsed_payload = JSON.parse(params[:webauthn_credential])
|
|
386
|
+
|
|
387
|
+
# Converti i campi da base64url a base64 standard per il gem webauthn
|
|
388
|
+
parsed_payload['rawId'] = base64url_to_base64(parsed_payload['rawId'])
|
|
389
|
+
parsed_payload['response']['clientDataJSON'] = base64url_to_base64(parsed_payload['response']['clientDataJSON'])
|
|
390
|
+
parsed_payload['response']['authenticatorData'] = base64url_to_base64(parsed_payload['response']['authenticatorData'])
|
|
391
|
+
parsed_payload['response']['signature'] = base64url_to_base64(parsed_payload['response']['signature'])
|
|
392
|
+
parsed_payload['response']['userHandle'] = base64url_to_base64(parsed_payload['response']['userHandle']) if parsed_payload['response']['userHandle']
|
|
393
|
+
|
|
394
|
+
credential = WebAuthn::Credential.from_get(parsed_payload)
|
|
395
|
+
|
|
396
|
+
credential.verify(
|
|
397
|
+
encoded_challenge,
|
|
398
|
+
public_key: webauthn_public_key,
|
|
399
|
+
sign_count: 0
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
true
|
|
403
|
+
rescue JSON::ParserError, WebAuthn::Error => e
|
|
404
|
+
Rails.logger.error(e)
|
|
405
|
+
Rails.logger.error(e.backtrace.join("\n"))
|
|
406
|
+
errors.add(:base, :webauthn_authentication_failed)
|
|
407
|
+
false
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
def remove_webauthn_credential
|
|
411
|
+
update(
|
|
412
|
+
webauthn_id: nil,
|
|
413
|
+
webauthn_public_key: nil
|
|
414
|
+
)
|
|
415
|
+
end
|
|
416
|
+
|
|
323
417
|
def generate_authenticator_secret
|
|
324
418
|
update(authenticator_secret: ROTP::Base32.random)
|
|
325
419
|
end
|
|
@@ -367,5 +461,41 @@ module Lato
|
|
|
367
461
|
Rails.cache.write(cache_key, value, expires_in: 30.minutes)
|
|
368
462
|
value
|
|
369
463
|
end
|
|
464
|
+
|
|
465
|
+
private
|
|
466
|
+
|
|
467
|
+
def webauthn_user_handle
|
|
468
|
+
Digest::SHA256.digest("lato-user-#{id}")
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
def webauthn_exclude_credentials
|
|
472
|
+
return [] unless webauthn_id.present?
|
|
473
|
+
|
|
474
|
+
[Base64.strict_decode64(webauthn_id)]
|
|
475
|
+
rescue ArgumentError
|
|
476
|
+
[]
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
def webauthn_allow_credentials
|
|
480
|
+
return [] unless webauthn_id.present?
|
|
481
|
+
|
|
482
|
+
[Base64.strict_decode64(webauthn_id)]
|
|
483
|
+
rescue ArgumentError
|
|
484
|
+
[]
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
def base64url_to_base64(str)
|
|
488
|
+
return nil if str.nil?
|
|
489
|
+
# Converti base64url in base64 standard
|
|
490
|
+
base64 = str.tr('-_', '+/')
|
|
491
|
+
# Aggiungi padding se necessario
|
|
492
|
+
case base64.length % 4
|
|
493
|
+
when 2
|
|
494
|
+
base64 += '=='
|
|
495
|
+
when 3
|
|
496
|
+
base64 += '='
|
|
497
|
+
end
|
|
498
|
+
base64
|
|
499
|
+
end
|
|
370
500
|
end
|
|
371
501
|
end
|
|
@@ -15,14 +15,15 @@ user ||= Lato::User.new
|
|
|
15
15
|
<img class="shadow-sm rounded" src="<%= user.authenticator_qr_code_base64 %>" />
|
|
16
16
|
</div>
|
|
17
17
|
<div class="d-flex flex-column justify-content-between w-100">
|
|
18
|
-
<div class="alert alert-
|
|
18
|
+
<div class="alert alert-success">
|
|
19
|
+
<h4 class="alert-heading"><%= I18n.t('lato.account_authenticator_ready_title') %></h4>
|
|
19
20
|
<p class="mb-0">
|
|
20
21
|
<%= raw I18n.t('lato.account_authenticator_ready_qr') %>
|
|
21
22
|
</p>
|
|
22
23
|
</div>
|
|
23
24
|
|
|
24
25
|
<div class="d-flex justify-content-end">
|
|
25
|
-
<%= lato_form_submit form, I18n.t('lato.
|
|
26
|
+
<%= lato_form_submit form, I18n.t('lato.account_authenticator_disable'), class: %w[btn-danger] %>
|
|
26
27
|
</div>
|
|
27
28
|
</div>
|
|
28
29
|
</div>
|
|
@@ -33,7 +34,7 @@ user ||= Lato::User.new
|
|
|
33
34
|
<%= raw I18n.t('lato.account_authenticator_start_description') %>
|
|
34
35
|
</p>
|
|
35
36
|
<p class="mb-0">
|
|
36
|
-
<%= lato_form_submit form, I18n.t('lato.
|
|
37
|
+
<%= lato_form_submit form, I18n.t('lato.account_authenticator_generate_qr_code'), class: %w[btn-primary] %>
|
|
37
38
|
</p>
|
|
38
39
|
</div>
|
|
39
40
|
<% end %>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<%
|
|
2
|
+
|
|
3
|
+
user ||= Lato::User.new
|
|
4
|
+
registration_options = session[:webauthn_registration_options]
|
|
5
|
+
form_data = { turbo_frame: '_self', controller: 'lato-form' }
|
|
6
|
+
|
|
7
|
+
if registration_options.present?
|
|
8
|
+
form_data[:controller] += ' lato-account-webauthn'
|
|
9
|
+
form_data['lato-account-webauthn-options-value'] = registration_options.to_json
|
|
10
|
+
form_data['lato-account-webauthn-error-message-value'] = I18n.t('lato.account_webauthn_in_progress_error')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
%>
|
|
14
|
+
|
|
15
|
+
<%= turbo_frame_tag 'account_form-webauthn' do %>
|
|
16
|
+
<%= form_with model: user, url: lato.account_update_webauthn_action_path, data: form_data do |form| %>
|
|
17
|
+
<%= lato_form_notices class: %w[mb-3] %>
|
|
18
|
+
<%= lato_form_errors user, class: %w[mb-3] %>
|
|
19
|
+
|
|
20
|
+
<% if user.webauthn_enabled? %>
|
|
21
|
+
<div class="alert alert-success mb-3">
|
|
22
|
+
<h4 class="alert-heading"><%= I18n.t('lato.account_webauthn_ready_title') %></h4>
|
|
23
|
+
<p class="mb-0">
|
|
24
|
+
<%= raw I18n.t('lato.account_webauthn_ready_description') %>
|
|
25
|
+
</p>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<%= form.hidden_field :webauthn_command, value: 'remove' %>
|
|
29
|
+
|
|
30
|
+
<div class="d-flex justify-content-end">
|
|
31
|
+
<%= lato_form_submit form, I18n.t('lato.account_webauthn_disable'), class: %w[btn-danger] %>
|
|
32
|
+
</div>
|
|
33
|
+
<% elsif registration_options.present? %>
|
|
34
|
+
<%= form.hidden_field :webauthn_command, value: 'complete' %>
|
|
35
|
+
<%= form.hidden_field :webauthn_credential, data: { lato_account_webauthn_target: 'credentialInput' } %>
|
|
36
|
+
|
|
37
|
+
<div class="alert alert-light mb-3" data-lato-account-webauthn-target="status">
|
|
38
|
+
<h4 class="alert-heading"><%= I18n.t('lato.account_webauthn_in_progress_title') %></h4>
|
|
39
|
+
<p class="mb-0">
|
|
40
|
+
<%= raw I18n.t('lato.account_webauthn_in_progress_description') %>
|
|
41
|
+
</p>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<%= lato_form_submit form, I18n.t('lato.account_webauthn_finalize'), class: %w[btn-primary d-none], data: { lato_account_webauthn_target: 'submit' } %>
|
|
45
|
+
|
|
46
|
+
<div class="d-flex justify-content-between align-items-center">
|
|
47
|
+
<%= link_to I18n.t('lato.account_webauthn_cancel'), lato.account_update_webauthn_action_path(user: { webauthn_command: 'cancel' }), class: 'btn btn-link px-0 text-decoration-none', data: { turbo_frame: '_self', turbo_method: :patch } %>
|
|
48
|
+
<div class="text-muted small d-flex align-items-center">
|
|
49
|
+
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
|
50
|
+
<span><%= I18n.t('lato.account_webauthn_waiting_browser') %></span>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
<% else %>
|
|
54
|
+
<div class="alert alert-light mb-0">
|
|
55
|
+
<h4 class="alert-heading"><%= I18n.t('lato.account_webauthn_start_title') %></h4>
|
|
56
|
+
<p>
|
|
57
|
+
<%= raw I18n.t('lato.account_webauthn_start_description') %>
|
|
58
|
+
</p>
|
|
59
|
+
<p class="mb-0">
|
|
60
|
+
<%= form.hidden_field :webauthn_command, value: 'start' %>
|
|
61
|
+
<%= lato_form_submit form, I18n.t('lato.account_webauthn_enable'), class: %w[btn-primary] %>
|
|
62
|
+
</p>
|
|
63
|
+
</div>
|
|
64
|
+
<% end %>
|
|
65
|
+
<% end %>
|
|
66
|
+
<% end %>
|
|
@@ -21,24 +21,35 @@
|
|
|
21
21
|
</div>
|
|
22
22
|
</div>
|
|
23
23
|
|
|
24
|
-
<% if Lato.config.
|
|
24
|
+
<% if Lato.config.authenticator_connection %>
|
|
25
25
|
<div class="card mb-4">
|
|
26
26
|
<div class="card-header">
|
|
27
|
-
<h2 class="fs-4 mb-0"><%= I18n.t('lato.
|
|
27
|
+
<h2 class="fs-4 mb-0"><%= I18n.t('lato.account_authenticator') %></h2>
|
|
28
28
|
</div>
|
|
29
29
|
<div class="card-body">
|
|
30
|
-
<%= render 'lato/account/form-
|
|
30
|
+
<%= render 'lato/account/form-authenticator', user: @session.user %>
|
|
31
31
|
</div>
|
|
32
32
|
</div>
|
|
33
33
|
<% end %>
|
|
34
34
|
|
|
35
|
-
<% if Lato.config.
|
|
35
|
+
<% if Lato.config.webauthn_connection %>
|
|
36
36
|
<div class="card mb-4">
|
|
37
37
|
<div class="card-header">
|
|
38
|
-
<h2 class="fs-4 mb-0"><%= I18n.t('lato.
|
|
38
|
+
<h2 class="fs-4 mb-0"><%= I18n.t('lato.account_webauthn') %></h2>
|
|
39
39
|
</div>
|
|
40
40
|
<div class="card-body">
|
|
41
|
-
<%= render 'lato/account/form-
|
|
41
|
+
<%= render 'lato/account/form-webauthn', user: @session.user %>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
<% end %>
|
|
45
|
+
|
|
46
|
+
<% if Lato.config.web3_connection %>
|
|
47
|
+
<div class="card mb-4">
|
|
48
|
+
<div class="card-header">
|
|
49
|
+
<h2 class="fs-4 mb-0"><%= I18n.t('lato.account_web3') %></h2>
|
|
50
|
+
</div>
|
|
51
|
+
<div class="card-body">
|
|
52
|
+
<%= render 'lato/account/form-web3', user: @session.user %>
|
|
42
53
|
</div>
|
|
43
54
|
</div>
|
|
44
55
|
<% end %>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<%
|
|
2
|
+
|
|
3
|
+
user ||= Lato::User.new
|
|
4
|
+
|
|
5
|
+
%>
|
|
6
|
+
|
|
7
|
+
<%= turbo_frame_tag 'authentication_form-authentication-method' do %>
|
|
8
|
+
<%= form_with url: lato.authentication_authentication_method_action_path, method: :post, data: { turbo_frame: '_self' } do |form| %>
|
|
9
|
+
<%= lato_form_notices class: %w[mb-3] %>
|
|
10
|
+
<%= lato_form_errors user, class: %w[mb-3] %>
|
|
11
|
+
|
|
12
|
+
<% if user.authenticator_enabled? %>
|
|
13
|
+
<div class="mb-3">
|
|
14
|
+
<%= form.button I18n.t('lato.use_google_authenticator'),
|
|
15
|
+
type: 'submit',
|
|
16
|
+
name: 'method',
|
|
17
|
+
value: 'authenticator',
|
|
18
|
+
class: 'btn btn-primary w-100' %>
|
|
19
|
+
</div>
|
|
20
|
+
<% end %>
|
|
21
|
+
|
|
22
|
+
<% if user.webauthn_enabled? %>
|
|
23
|
+
<div class="mb-3">
|
|
24
|
+
<%= form.button I18n.t('lato.use_webauthn'),
|
|
25
|
+
type: 'submit',
|
|
26
|
+
name: 'method',
|
|
27
|
+
value: 'webauthn',
|
|
28
|
+
class: 'btn btn-primary w-100' %>
|
|
29
|
+
</div>
|
|
30
|
+
<% end %>
|
|
31
|
+
|
|
32
|
+
<div class="text-center mt-3 mb-3">
|
|
33
|
+
<%= I18n.t('lato.or').downcase %> <%= link_to I18n.t('lato.reset_password').downcase, lato.authentication_recover_password_path %>
|
|
34
|
+
</div>
|
|
35
|
+
<% end %>
|
|
36
|
+
<% end %>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<%
|
|
2
|
+
|
|
3
|
+
user ||= Lato::User.new
|
|
4
|
+
options ||= {}
|
|
5
|
+
|
|
6
|
+
%>
|
|
7
|
+
|
|
8
|
+
<%= turbo_frame_tag 'authentication_form-webauthn' do %>
|
|
9
|
+
<%= form_with model: user, url: lato.authentication_webauthn_action_path, method: :post, data: { turbo_frame: '_self', controller: 'lato-form lato-webauthn-auth', 'lato-webauthn-auth-options-value': options.to_json } do |form| %>
|
|
10
|
+
<%= lato_form_notices class: %w[mb-3] %>
|
|
11
|
+
<%= lato_form_errors user, class: %w[mb-3] %>
|
|
12
|
+
|
|
13
|
+
<div class="mb-4 text-center">
|
|
14
|
+
<p><%= raw I18n.t('lato.webauthn_help', email: h(user.email_protected)) %></p>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<%= form.hidden_field :webauthn_credential, data: { 'lato-webauthn-auth-target': 'credential' } %>
|
|
18
|
+
|
|
19
|
+
<div class="mb-3">
|
|
20
|
+
<%= lato_form_submit form, I18n.t('lato.authenticate'), class: %w[d-block w-100], data: { action: 'lato-webauthn-auth#authenticate' } %>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<div class="text-center mt-3 mb-3">
|
|
24
|
+
<%= I18n.t('lato.or').downcase %> <%= link_to I18n.t('lato.reset_password').downcase, lato.authentication_recover_password_path %>
|
|
25
|
+
</div>
|
|
26
|
+
<% end %>
|
|
27
|
+
<% end %>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<div class="w-100 h-100 d-flex justify-content-center align-items-center" style="min-height: calc(100vh - 54px - 2rem)">
|
|
2
|
+
<div class="card w-100" style="max-width: 400px">
|
|
3
|
+
<div class="card-header">
|
|
4
|
+
<h1 class="fs-3 mb-0 text-center"><%= I18n.t('lato.choose_authentication_method') %></h1>
|
|
5
|
+
</div>
|
|
6
|
+
<div class="card-body">
|
|
7
|
+
<%= render 'lato/authentication/form-authentication-method', user: @user %>
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<div class="w-100 h-100 d-flex justify-content-center align-items-center" style="min-height: calc(100vh - 54px - 2rem)">
|
|
2
|
+
<div class="card w-100" style="max-width: 400px">
|
|
3
|
+
<div class="card-header">
|
|
4
|
+
<h1 class="fs-3 mb-0 text-center"><%= I18n.t('lato.webauthn') %></h1>
|
|
5
|
+
</div>
|
|
6
|
+
<div class="card-body">
|
|
7
|
+
<%= render 'lato/authentication/form-webauthn', user: @user, options: @options %>
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<% 10.times do %>
|
|
2
2
|
|
|
3
|
-
<div class="modal fade" data-lato-action-target="modal" tabindex="-1" aria-hidden="true" data-bs-keyboard="false">
|
|
3
|
+
<div class="modal fade" data-lato-action-target="modal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
|
|
4
4
|
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable" data-lato-action-target="modalDialog">
|
|
5
5
|
<div class="modal-content">
|
|
6
6
|
<div class="modal-header">
|
data/config/locales/en.yml
CHANGED
|
@@ -53,12 +53,32 @@ en:
|
|
|
53
53
|
account_authenticator: Google Authenticator
|
|
54
54
|
account_authenticator_start_title: Enable Google Authenticator
|
|
55
55
|
account_authenticator_start_description: Generate a QR code by clicking the button below and scan it with the Google Authenticator app on your phone.<br>This will allow you to use the protect your account with a second factor authentication.
|
|
56
|
+
account_authenticator_ready_title: Google Authenticator ready
|
|
56
57
|
account_authenticator_ready_qr: Scan the QR code with the Google Authenticator app to use the account protection with a second factor authentication.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
authenticator:
|
|
58
|
+
account_authenticator_generate_qr_code: Generate QR code
|
|
59
|
+
account_authenticator_disable: Disconnect
|
|
60
|
+
authenticator: Google Authenticator
|
|
60
61
|
authenticator_code_help: Insert the code generated by the Google Authenticator app for the account <b>%{email}</b>
|
|
61
62
|
reset_password: Reset your password
|
|
63
|
+
choose_authentication_method: Choose authentication method
|
|
64
|
+
use_google_authenticator: Use Google Authenticator
|
|
65
|
+
use_webauthn: Use Passkey
|
|
66
|
+
authenticate: Authenticate
|
|
67
|
+
webauthn: Passkey Authentication
|
|
68
|
+
webauthn_help: Use your device to authenticate for account <b>%{email}</b>
|
|
69
|
+
account_webauthn: Passkey authentication
|
|
70
|
+
account_webauthn_start_title: Enable passkeys
|
|
71
|
+
account_webauthn_start_description: Use your device security (Touch ID, Face ID, Windows Hello, etc.) to approve future logins.<br>Click the button below and follow the browser prompts to register a passkey.
|
|
72
|
+
account_webauthn_enable: Register passkey
|
|
73
|
+
account_webauthn_in_progress_title: Complete the registration from your browser
|
|
74
|
+
account_webauthn_in_progress_description: Approve the prompt that appears in your browser to finish linking this passkey to your account.
|
|
75
|
+
account_webauthn_in_progress_error: Unable to register the passkey. Please restart the procedure and try again.
|
|
76
|
+
account_webauthn_waiting_browser: Waiting for your browser confirmation...
|
|
77
|
+
account_webauthn_cancel: Cancel request
|
|
78
|
+
account_webauthn_finalize: Complete registration
|
|
79
|
+
account_webauthn_ready_title: Passkey ready
|
|
80
|
+
account_webauthn_ready_description: This account now requires a WebAuthn challenge right after the classic login flow.
|
|
81
|
+
account_webauthn_disable: Disconnect
|
|
62
82
|
per_page: Per page
|
|
63
83
|
per_page_description: Number of elements per page
|
|
64
84
|
confirm_title: Confirm
|
|
@@ -114,6 +134,10 @@ en:
|
|
|
114
134
|
web3_address_invalid: The address you send is not corretly signed
|
|
115
135
|
web3_connection_error: Impossible to connect the wallet
|
|
116
136
|
authenticator_code_invalid: The code you inserted is not correct
|
|
137
|
+
webauthn_payload_missing: Passkey response missing. Please try again.
|
|
138
|
+
webauthn_challenge_missing: Passkey challenge expired. Start the registration again.
|
|
139
|
+
webauthn_registration_failed: Unable to verify the passkey response. Please restart the procedure.
|
|
140
|
+
webauthn_authentication_failed: Passkey authentication failed. Please try again or use another method.
|
|
117
141
|
password:
|
|
118
142
|
not_correct: not correct
|
|
119
143
|
email:
|
data/config/locales/fr.yml
CHANGED
|
@@ -55,12 +55,32 @@ fr:
|
|
|
55
55
|
account_authenticator: Google Authenticator
|
|
56
56
|
account_authenticator_start_title: Activer Google Authenticator
|
|
57
57
|
account_authenticator_start_description: Générez un code QR en cliquant sur le bouton ci-dessous et scannez-le avec l'application Google Authenticator sur votre téléphone.<br>Cela vous permettra de protéger votre compte avec une authentification à deux facteurs.
|
|
58
|
+
account_authenticator_ready_title: Google Authenticator prêt
|
|
58
59
|
account_authenticator_ready_qr: Scannez le code QR avec l'application Google Authenticator pour activer la protection de votre compte avec une authentification à deux facteurs.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
authenticator:
|
|
60
|
+
account_authenticator_generate_qr_code: Générer un code QR
|
|
61
|
+
account_authenticator_disable: Déconnecter
|
|
62
|
+
authenticator: Google Authenticator
|
|
62
63
|
authenticator_code_help: Saisissez le code généré par l'application Google Authenticator pour le compte <b>%{email}</b>
|
|
63
64
|
reset_password: Réinitialisez votre mot de passe
|
|
65
|
+
choose_authentication_method: Choisir la méthode d'authentification
|
|
66
|
+
use_google_authenticator: Utiliser Google Authenticator
|
|
67
|
+
use_webauthn: Utiliser Passkey
|
|
68
|
+
authenticate: Authentifier
|
|
69
|
+
webauthn: Authentification Passkey
|
|
70
|
+
webauthn_help: Utilisez votre appareil pour vous authentifier sur le compte <b>%{email}</b>
|
|
71
|
+
account_webauthn: Authentification Passkey
|
|
72
|
+
account_webauthn_start_title: Activer les passkeys
|
|
73
|
+
account_webauthn_start_description: Utilisez la sécurité de votre appareil (Touch ID, Face ID, Windows Hello, etc.) pour approuver les connexions futures.<br>Cliquez sur le bouton ci-dessous et suivez les instructions de votre navigateur pour enregistrer une passkey.
|
|
74
|
+
account_webauthn_enable: Enregistrer la passkey
|
|
75
|
+
account_webauthn_in_progress_title: Validez l'inscription depuis votre navigateur
|
|
76
|
+
account_webauthn_in_progress_description: Approuvez la demande qui apparaît dans votre navigateur pour terminer l'association de la passkey à votre compte.
|
|
77
|
+
account_webauthn_in_progress_error: Impossible d'enregistrer la passkey. Redémarrez la procédure et réessayez.
|
|
78
|
+
account_webauthn_waiting_browser: En attente de la confirmation du navigateur...
|
|
79
|
+
account_webauthn_cancel: Annuler la demande
|
|
80
|
+
account_webauthn_finalize: Terminer l'inscription
|
|
81
|
+
account_webauthn_ready_title: Passkey activée
|
|
82
|
+
account_webauthn_ready_description: Ce compte nécessite désormais un défi WebAuthn juste après le processus de connexion classique.
|
|
83
|
+
account_webauthn_disable: Supprimer la passkey
|
|
64
84
|
per_page: Par page
|
|
65
85
|
per_page_description: Éléments par page
|
|
66
86
|
confirm_title: Confirmation
|
|
@@ -122,6 +142,10 @@ fr:
|
|
|
122
142
|
web3_address_invalid: L'adresse envoyée n'est pas correctement signée
|
|
123
143
|
web3_connection_error: Impossible de connecter le portefeuille
|
|
124
144
|
authenticator_code_invalid: Le code saisi n'est pas correct
|
|
145
|
+
webauthn_payload_missing: Réponse passkey manquante. Veuillez réessayer.
|
|
146
|
+
webauthn_challenge_missing: Le défi passkey a expiré. Veuillez relancer la procédure.
|
|
147
|
+
webauthn_registration_failed: Impossible de vérifier la réponse passkey. Redémarrez la procédure.
|
|
148
|
+
webauthn_authentication_failed: Échec de l'authentification passkey. Veuillez réessayer ou utiliser une autre méthode.
|
|
125
149
|
password:
|
|
126
150
|
not_correct: incorrect
|
|
127
151
|
password_confirmation:
|
data/config/locales/it.yml
CHANGED
|
@@ -55,12 +55,32 @@ it:
|
|
|
55
55
|
account_authenticator: Google Authenticator
|
|
56
56
|
account_authenticator_start_title: Abilita Google Authenticator
|
|
57
57
|
account_authenticator_start_description: Genera un codice QR cliccando il pulsante sottostante e scansionalo con l'app Google Authenticator sul tuo telefono.<br>Questo ti permetterà di proteggere il tuo account con un'autenticazione a due fattori.
|
|
58
|
+
account_authenticator_ready_title: Google Authenticator pronto
|
|
58
59
|
account_authenticator_ready_qr: Scansiona il codice QR con l'app Google Authenticator per utilizzare la protezione dell'account con un'autenticazione a due fattori.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
authenticator:
|
|
60
|
+
account_authenticator_generate_qr_code: Genera codice QR
|
|
61
|
+
account_authenticator_disable: Disconnetti
|
|
62
|
+
authenticator: Google Authenticator
|
|
62
63
|
authenticator_code_help: Inserisci il codice generato dall'app Google Authenticator per l'account <b>%{email}</b>
|
|
63
64
|
reset_password: Reimposta la tua password
|
|
65
|
+
choose_authentication_method: Scegli il metodo di autenticazione
|
|
66
|
+
use_google_authenticator: Usa Google Authenticator
|
|
67
|
+
use_webauthn: Usa Passkey
|
|
68
|
+
authenticate: Autentica
|
|
69
|
+
webauthn: Autenticazione Passkey
|
|
70
|
+
webauthn_help: Usa il tuo dispositivo per autenticarti nell'account <b>%{email}</b>
|
|
71
|
+
account_webauthn: Autenticazione Passkey
|
|
72
|
+
account_webauthn_start_title: Abilita le Passkey
|
|
73
|
+
account_webauthn_start_description: Utilizza la sicurezza del tuo dispositivo (Touch ID, Face ID, Windows Hello, ecc.) per approvare gli accessi futuri.<br>Clicca il pulsante qui sotto e segui le istruzioni del browser per registrare una Passkey.
|
|
74
|
+
account_webauthn_enable: Registra Passkey
|
|
75
|
+
account_webauthn_in_progress_title: Completa la registrazione dal browser
|
|
76
|
+
account_webauthn_in_progress_description: Approva la richiesta che appare nel browser per terminare l'associazione della Passkey al tuo account.
|
|
77
|
+
account_webauthn_in_progress_error: Impossibile registrare la Passkey. Riavvia la procedura e riprova.
|
|
78
|
+
account_webauthn_waiting_browser: In attesa della conferma dal browser...
|
|
79
|
+
account_webauthn_cancel: Annulla richiesta
|
|
80
|
+
account_webauthn_finalize: Concludi registrazione
|
|
81
|
+
account_webauthn_ready_title: Passkey attiva
|
|
82
|
+
account_webauthn_ready_description: Questo account richiederà ora una sfida WebAuthn subito dopo il flusso di login classico.
|
|
83
|
+
account_webauthn_disable: Rimuovi Passkey
|
|
64
84
|
per_page: Per pagina
|
|
65
85
|
per_page_description: Elementi per pagina
|
|
66
86
|
confirm_title: Conferma
|
|
@@ -122,6 +142,10 @@ it:
|
|
|
122
142
|
web3_address_invalid: L'inidirizzo inviato non è correttamente firmato
|
|
123
143
|
web3_connection_error: Impossibile connettere il wallet
|
|
124
144
|
authenticator_code_invalid: Il codice inserito non è corretto
|
|
145
|
+
webauthn_payload_missing: Risposta Passkey mancante. Riprovare.
|
|
146
|
+
webauthn_challenge_missing: La sfida Passkey è scaduta. Avvia nuovamente la procedura.
|
|
147
|
+
webauthn_registration_failed: Impossibile verificare la risposta Passkey. Riavvia la procedura.
|
|
148
|
+
webauthn_authentication_failed: Autenticazione Passkey fallita. Riprova o usa un altro metodo.
|
|
125
149
|
password:
|
|
126
150
|
not_correct: non corretta
|
|
127
151
|
password_confirmation:
|
data/config/locales/ro.yml
CHANGED
|
@@ -55,12 +55,32 @@ ro:
|
|
|
55
55
|
account_authenticator: Google Authenticator
|
|
56
56
|
account_authenticator_start_title: Activează Google Authenticator
|
|
57
57
|
account_authenticator_start_description: Generează un cod QR făcând clic pe butonul de mai jos și scanează-l cu aplicația Google Authenticator de pe telefon.<br>Aceasta îți va permite să îți protejezi contul cu autentificare în doi pași.
|
|
58
|
+
account_authenticator_ready_title: Google Authenticator gata
|
|
58
59
|
account_authenticator_ready_qr: Scanează codul QR cu aplicația Google Authenticator pentru a utiliza protecția contului cu autentificare în doi pași.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
authenticator:
|
|
60
|
+
account_authenticator_generate_qr_code: Generează cod QR
|
|
61
|
+
account_authenticator_disable: Deconectează
|
|
62
|
+
authenticator: Google Authenticator
|
|
62
63
|
authenticator_code_help: Introdu codul generat de aplicația Google Authenticator pentru contul <b>%{email}</b>
|
|
63
64
|
reset_password: Resetează-ți parola
|
|
65
|
+
choose_authentication_method: Alege metoda de autentificare
|
|
66
|
+
use_google_authenticator: Folosește Google Authenticator
|
|
67
|
+
use_webauthn: Folosește Passkey
|
|
68
|
+
authenticate: Autentifică
|
|
69
|
+
webauthn: Autentificare Passkey
|
|
70
|
+
webauthn_help: Folosește dispozitivul tău pentru a te autentifica pe contul <b>%{email}</b>
|
|
71
|
+
account_webauthn: Autentificare Passkey
|
|
72
|
+
account_webauthn_start_title: Activează Passkey
|
|
73
|
+
account_webauthn_start_description: Folosește securitatea dispozitivului tău (Touch ID, Face ID, Windows Hello etc.) pentru a aproba conectările viitoare.<br>Apasă butonul de mai jos și urmează instrucțiunile din browser pentru a înregistra o Passkey.
|
|
74
|
+
account_webauthn_enable: Înregistrează Passkey
|
|
75
|
+
account_webauthn_in_progress_title: Finalizează înregistrarea din browser
|
|
76
|
+
account_webauthn_in_progress_description: Aprobă solicitarea care apare în browser pentru a termina asocierea Passkey la contul tău.
|
|
77
|
+
account_webauthn_in_progress_error: Nu am putut înregistra Passkey. Repornește procedura și încearcă din nou.
|
|
78
|
+
account_webauthn_waiting_browser: Se așteaptă confirmarea din browser...
|
|
79
|
+
account_webauthn_cancel: Anulează solicitarea
|
|
80
|
+
account_webauthn_finalize: Finalizează înregistrarea
|
|
81
|
+
account_webauthn_ready_title: Passkey activă
|
|
82
|
+
account_webauthn_ready_description: Acest cont va solicita acum o provocare WebAuthn imediat după fluxul de autentificare clasic.
|
|
83
|
+
account_webauthn_disable: Elimină Passkey
|
|
64
84
|
per_page: Pe pagină
|
|
65
85
|
per_page_description: Elemente pe pagină
|
|
66
86
|
confirm_title: Confirmă
|
|
@@ -122,6 +142,10 @@ ro:
|
|
|
122
142
|
web3_address_invalid: Adresa trimisă nu este semnată corect
|
|
123
143
|
web3_connection_error: Imposibil să se conecteze portofelul
|
|
124
144
|
authenticator_code_invalid: Codul introdus nu este corect
|
|
145
|
+
webauthn_payload_missing: Răspunsul Passkey lipsește. Te rugăm să încerci din nou.
|
|
146
|
+
webauthn_challenge_missing: Provocarea Passkey a expirat. Repornește procedura.
|
|
147
|
+
webauthn_registration_failed: Nu am putut verifica răspunsul Passkey. Repornește procedura.
|
|
148
|
+
webauthn_authentication_failed: Autentificarea Passkey a eșuat. Te rugăm să încerci din nou sau să folosești altă metodă.
|
|
125
149
|
password:
|
|
126
150
|
not_correct: incorectă
|
|
127
151
|
password_confirmation:
|
data/config/routes.rb
CHANGED
|
@@ -27,8 +27,12 @@ Lato::Engine.routes.draw do
|
|
|
27
27
|
patch 'update_password_action', to: 'authentication#update_password_action', as: :authentication_update_password_action
|
|
28
28
|
get 'accept_invitation', to: 'authentication#accept_invitation', as: :authentication_accept_invitation
|
|
29
29
|
post 'accept_invitation_action', to: 'authentication#accept_invitation_action', as: :authentication_accept_invitation_action
|
|
30
|
+
get 'authentication_method', to: 'authentication#authentication_method', as: :authentication_authentication_method
|
|
31
|
+
post 'authentication_method_action', to: 'authentication#authentication_method_action', as: :authentication_authentication_method_action
|
|
30
32
|
get 'authenticator', to: 'authentication#authenticator', as: :authentication_authenticator
|
|
31
33
|
post 'authenticator_action', to: 'authentication#authenticator_action', as: :authentication_authenticator_action
|
|
34
|
+
get 'webauthn', to: 'authentication#webauthn', as: :authentication_webauthn
|
|
35
|
+
post 'webauthn_action', to: 'authentication#webauthn_action', as: :authentication_webauthn_action
|
|
32
36
|
end
|
|
33
37
|
|
|
34
38
|
# Account
|
|
@@ -37,8 +41,9 @@ Lato::Engine.routes.draw do
|
|
|
37
41
|
scope :account do
|
|
38
42
|
get '', to: 'account#index', as: :account
|
|
39
43
|
patch 'update_user_action', to: 'account#update_user_action', as: :account_update_user_action
|
|
40
|
-
patch 'update_web3_action', to: 'account#update_web3_action', as: :account_update_web3_action
|
|
41
44
|
patch 'update_authenticator_action', to: 'account#update_authenticator_action', as: :account_update_authenticator_action
|
|
45
|
+
patch 'update_webauthn_action', to: 'account#update_webauthn_action', as: :account_update_webauthn_action
|
|
46
|
+
patch 'update_web3_action', to: 'account#update_web3_action', as: :account_update_web3_action
|
|
42
47
|
patch 'request_verify_email_action', to: 'account#request_verify_email_action', as: :account_request_verify_email_action
|
|
43
48
|
patch 'update_password_action', to: 'account#update_password_action', as: :account_update_password_action
|
|
44
49
|
delete 'destroy_action', to: 'account#destroy_action', as: :account_destroy_action
|