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.
@@ -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-light">
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.cancel_authenticator'), class: %w[btn-danger] %>
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.generate_qr_code'), class: %w[btn-primary] %>
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.web3_connection %>
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.account_web3') %></h2>
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-web3', user: @session.user %>
30
+ <%= render 'lato/account/form-authenticator', user: @session.user %>
31
31
  </div>
32
32
  </div>
33
33
  <% end %>
34
34
 
35
- <% if Lato.config.authenticator_connection %>
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.account_authenticator') %></h2>
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-authenticator', user: @session.user %>
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">
@@ -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
- generate_qr_code: Generate QR code
58
- cancel_authenticator: Disconnect
59
- authenticator: Two-factor authentication
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:
@@ -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
- generate_qr_code: Générer un code QR
60
- cancel_authenticator: Déconnecter
61
- authenticator: Authentification à deux facteurs
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:
@@ -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
- generate_qr_code: Genera codice QR
60
- cancel_authenticator: Disconnetti
61
- authenticator: Autenticazione a due fattori
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:
@@ -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
- generate_qr_code: Generează cod QR
60
- cancel_authenticator: Deconectează
61
- authenticator: Autentificare în doi pași
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
@@ -0,0 +1,6 @@
1
+ class AddWebauthnIdToUser < ActiveRecord::Migration[8.1]
2
+ def change
3
+ add_column :lato_users, :webauthn_id, :string
4
+ add_column :lato_users, :webauthn_public_key, :text
5
+ end
6
+ end