Skip to content

Commit 7278744

Browse files
authored
Merge pull request #574 from code0-tech/559-setup-email-verification
Setup email verification
2 parents 7327ae2 + 1c19680 commit 7278744

File tree

24 files changed

+349
-2
lines changed

24 files changed

+349
-2
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# frozen_string_literal: true
2+
3+
module Mutations
4+
module Users
5+
class EmailVerification < BaseMutation
6+
description 'Verify your email when changing it or signing up'
7+
8+
field :user, Types::UserType, null: true, description: 'The user whose email was verified'
9+
10+
argument :token, String, required: true, description: 'The email verification token'
11+
12+
def resolve(token:)
13+
::Users::EmailVerificationService.new(
14+
current_authentication,
15+
token
16+
).execute.to_mutation_response(success_key: :user)
17+
end
18+
end
19+
end
20+
end

app/graphql/types/mutation_type.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class MutationType < Types::BaseObject
3535
mount_mutation Mutations::Users::Mfa::BackupCodes::Rotate
3636
mount_mutation Mutations::Users::Mfa::Totp::GenerateSecret
3737
mount_mutation Mutations::Users::Mfa::Totp::ValidateSecret
38+
mount_mutation Mutations::Users::EmailVerification
3839
mount_mutation Mutations::Users::Login
3940
mount_mutation Mutations::Users::Logout
4041
mount_mutation Mutations::Users::Register

app/mailers/user_mailer.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# frozen_string_literal: true
2+
3+
class UserMailer < ApplicationMailer
4+
def email_verification
5+
@user = params[:user]
6+
@verification_code = params[:verification_code]
7+
8+
mail(to: @user.email, subject: 'Email verification')
9+
end
10+
end

app/models/audit_event.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ class AuditEvent < ApplicationRecord
3535
flow_created: 31,
3636
flow_updated: 32,
3737
flow_deleted: 33,
38+
email_verification_sent: 34,
39+
email_verified: 35,
3840
}.with_indifferent_access
3941

4042
# rubocop:disable Lint/StructNewOverride

app/models/user.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,8 @@ def validate_mfa!(mfa)
5555
end
5656
[mfa_passed, mfa_type]
5757
end
58+
59+
generates_token_for :email_verification, expires_in: 15.minutes do
60+
email
61+
end
5862
end

app/policies/user_policy.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,7 @@ class UserPolicy < BasePolicy
1717
enable :manage_mfa
1818
enable :update_user
1919
enable :update_attachment_avatar
20+
enable :verify_email
21+
enable :send_verification_email
2022
end
2123
end
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# frozen_string_literal: true
2+
3+
module Users
4+
class EmailVerificationSendService
5+
include Sagittarius::Database::Transactional
6+
7+
attr_reader :current_authentication, :user
8+
9+
def initialize(current_authentication, user)
10+
@current_authentication = current_authentication
11+
@user = user
12+
end
13+
14+
def execute
15+
unless Ability.allowed?(current_authentication, :send_verification_email, user)
16+
return ServiceResponse.error(message: 'Missing permission', payload: :missing_permission)
17+
end
18+
19+
transactional do |t|
20+
user.email_verified_at = nil
21+
unless user.save
22+
t.rollback_and_return! ServiceResponse.error(message: 'Failed to set email to unverified',
23+
payload: user.errors)
24+
end
25+
26+
UserMailer.with(
27+
user: user,
28+
verification_code: user.generate_token_for(:email_verification)
29+
).email_verification.deliver_later
30+
31+
AuditService.audit(
32+
:email_verification_sent,
33+
author_id: current_authentication.user.id,
34+
entity: user,
35+
target: user,
36+
details: {
37+
email: user.email,
38+
}
39+
)
40+
41+
ServiceResponse.success(message: 'Successfully sent email verification', payload: user)
42+
end
43+
end
44+
end
45+
end
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# frozen_string_literal: true
2+
3+
module Users
4+
class EmailVerificationService
5+
include Sagittarius::Database::Transactional
6+
7+
attr_reader :current_authentication, :verification_code
8+
9+
def initialize(current_authentication, verification_code)
10+
@current_authentication = current_authentication
11+
@verification_code = verification_code
12+
end
13+
14+
def execute
15+
user = User.find_by_token_for(:email_verification, verification_code)
16+
17+
unless Ability.allowed?(current_authentication, :verify_email, user)
18+
return ServiceResponse.error(message: 'Missing permission', payload: :missing_permission)
19+
end
20+
21+
transactional do |t|
22+
user.email_verified_at = Time.zone.now
23+
unless user.save
24+
t.rollback_and_return! ServiceResponse.error(message: 'Failed to set email to verified', payload: user.errors)
25+
end
26+
27+
AuditService.audit(
28+
:email_verified,
29+
author_id: current_authentication.user.id,
30+
entity: user,
31+
target: user,
32+
details: {
33+
email: user.email,
34+
}
35+
)
36+
37+
ServiceResponse.success(message: 'Successfully verified email', payload: user)
38+
end
39+
end
40+
end
41+
end

app/services/users/register_service.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ def execute
2828
payload: user_session.errors)
2929
end
3030

31+
email_verification_response = EmailVerificationSendService.new(
32+
Sagittarius::Authentication.new(:session, user_session),
33+
user
34+
).execute
35+
36+
unless email_verification_response.success?
37+
t.rollback_and_return! ServiceResponse.error(message: 'Failed to send verification email',
38+
payload: email_verification_response.payload)
39+
end
40+
3141
AuditService.audit(
3242
:user_registered,
3343
author_id: user.id,

app/services/users/update_service.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ def execute
6060
)
6161
end
6262

63+
if params.key?(:email)
64+
response = EmailVerificationSendService.new(current_authentication, user).execute
65+
66+
unless response.success?
67+
t.rollback_and_return! ServiceResponse.error(message: 'Failed to send verification email',
68+
payload: response.payload)
69+
end
70+
end
71+
6372
AuditService.audit(
6473
:user_updated,
6574
author_id: current_authentication.user.id,

0 commit comments

Comments
 (0)