about summary refs log tree commit diff
path: root/app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb
blob: 7e2d43dcd312318c84c4199a64cc8e308ae790f6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# frozen_string_literal: true

module Settings
  module TwoFactorAuthentication
    class WebauthnCredentialsController < BaseController
      skip_before_action :require_functional!

      before_action :require_otp_enabled
      before_action :require_webauthn_enabled, only: [:index, :destroy]

      def new; end

      def index; end

      def options
        current_user.update(webauthn_id: WebAuthn.generate_user_id) unless current_user.webauthn_id

        options_for_create = WebAuthn::Credential.options_for_create(
          user: {
            name: current_user.account.username,
            display_name: current_user.account.username,
            id: current_user.webauthn_id,
          },
          exclude: current_user.webauthn_credentials.pluck(:external_id),
          authenticator_selection: { user_verification: 'discouraged' }
        )

        session[:webauthn_challenge] = options_for_create.challenge

        render json: options_for_create, status: :ok
      end

      def create
        webauthn_credential = WebAuthn::Credential.from_create(params[:credential])

        if webauthn_credential.verify(session[:webauthn_challenge])
          user_credential = current_user.webauthn_credentials.build(
            external_id: webauthn_credential.id,
            public_key: webauthn_credential.public_key,
            nickname: params[:nickname],
            sign_count: webauthn_credential.sign_count
          )

          if user_credential.save
            flash[:success] = I18n.t('webauthn_credentials.create.success')
            status = :ok

            if current_user.webauthn_credentials.size == 1
              UserMailer.webauthn_enabled(current_user).deliver_later!
            else
              UserMailer.webauthn_credential_added(current_user, user_credential).deliver_later!
            end
          else
            flash[:error] = I18n.t('webauthn_credentials.create.error')
            status = :internal_server_error
          end
        else
          flash[:error] = t('webauthn_credentials.create.error')
          status = :unauthorized
        end

        render json: { redirect_path: settings_two_factor_authentication_methods_path }, status: status
      end

      def destroy
        credential = current_user.webauthn_credentials.find_by(id: params[:id])
        if credential
          credential.destroy
          if credential.destroyed?
            flash[:success] = I18n.t('webauthn_credentials.destroy.success')

            if current_user.webauthn_credentials.empty?
              UserMailer.webauthn_disabled(current_user).deliver_later!
            else
              UserMailer.webauthn_credential_deleted(current_user, credential).deliver_later!
            end
          else
            flash[:error] = I18n.t('webauthn_credentials.destroy.error')
          end
        else
          flash[:error] = I18n.t('webauthn_credentials.destroy.error')
        end
        redirect_to settings_two_factor_authentication_methods_path
      end

      private

      def set_pack
        use_pack 'auth'
      end

      def require_otp_enabled
        unless current_user.otp_enabled?
          flash[:error] = t('webauthn_credentials.otp_required')
          redirect_to settings_two_factor_authentication_methods_path
        end
      end

      def require_webauthn_enabled
        unless current_user.webauthn_enabled?
          flash[:error] = t('webauthn_credentials.not_enabled')
          redirect_to settings_two_factor_authentication_methods_path
        end
      end
    end
  end
end