Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@

<p><%= t('.confirm_link_msg') %></p>

<p><%= link_to t('.confirm_account_link'), "#{message['schema_url']}?#{confirmation_query(resource_name: @resource.class.to_s, redirect_url: message['redirect-url'], token: @token).to_query}" %></p>
<p>
<% if message['schema_url'].present? %>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this path trigger a deprecation warning?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure we should do that here. Plus, any value can be passed to message, not sure it's necessary

<%= link_to t('.confirm_account_link'), "#{message['schema_url']}?#{confirmation_query(resource_name: @resource.class.to_s, redirect_url: message['redirect-url'], token: @token).to_query}" %>
<% else %>
<%= link_to t('.confirm_account_link'), "#{CGI.escape(message['redirect-url'].to_s)}?#{{ confirmationToken: @token }.to_query}" %>
<% end %>
</p>
20 changes: 12 additions & 8 deletions lib/graphql_devise/default_operations/mutations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,24 @@
require 'graphql_devise/mutations/send_password_reset'
require 'graphql_devise/mutations/send_password_reset_with_token'
require 'graphql_devise/mutations/sign_up'
require 'graphql_devise/mutations/register'
require 'graphql_devise/mutations/update_password'
require 'graphql_devise/mutations/update_password_with_token'
require 'graphql_devise/mutations/confirm_registration_with_token'

module GraphqlDevise
module DefaultOperations
MUTATIONS = {
login: { klass: GraphqlDevise::Mutations::Login, authenticatable: true },
logout: { klass: GraphqlDevise::Mutations::Logout, authenticatable: true },
sign_up: { klass: GraphqlDevise::Mutations::SignUp, authenticatable: true },
update_password: { klass: GraphqlDevise::Mutations::UpdatePassword, authenticatable: true },
update_password_with_token: { klass: GraphqlDevise::Mutations::UpdatePasswordWithToken, authenticatable: true },
send_password_reset: { klass: GraphqlDevise::Mutations::SendPasswordReset, authenticatable: false },
send_password_reset_with_token: { klass: GraphqlDevise::Mutations::SendPasswordResetWithToken, authenticatable: false },
resend_confirmation: { klass: GraphqlDevise::Mutations::ResendConfirmation, authenticatable: false }
login: { klass: GraphqlDevise::Mutations::Login, authenticatable: true },
logout: { klass: GraphqlDevise::Mutations::Logout, authenticatable: true },
sign_up: { klass: GraphqlDevise::Mutations::SignUp, authenticatable: true },
register: { klass: GraphqlDevise::Mutations::Register, authenticatable: true },
update_password: { klass: GraphqlDevise::Mutations::UpdatePassword, authenticatable: true },
update_password_with_token: { klass: GraphqlDevise::Mutations::UpdatePasswordWithToken, authenticatable: true },
send_password_reset: { klass: GraphqlDevise::Mutations::SendPasswordReset, authenticatable: false },
send_password_reset_with_token: { klass: GraphqlDevise::Mutations::SendPasswordResetWithToken, authenticatable: false },
resend_confirmation: { klass: GraphqlDevise::Mutations::ResendConfirmation, authenticatable: false },
confirm_registration_with_token: { klass: GraphqlDevise::Mutations::ConfirmRegistrationWithToken, authenticatable: true }
}.freeze
end
end
42 changes: 34 additions & 8 deletions lib/graphql_devise/model/with_email_updater.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ module GraphqlDevise
module Model
class WithEmailUpdater
def initialize(resource, attributes)
@attributes = attributes
@attributes = attributes.with_indifferent_access
@resource = resource
end

def call
resource_attributes = @attributes.except(:schema_url, :confirmation_success_url)
check_deprecated_attributes

resource_attributes = @attributes.except(:schema_url, :confirmation_success_url, :confirmation_url)
return @resource.update(resource_attributes) unless requires_reconfirmation?(resource_attributes)

@resource.assign_attributes(resource_attributes)
Expand All @@ -27,16 +29,31 @@ def call
else
raise(
GraphqlDevise::Error,
'Method `update_with_email` requires attributes `confirmation_success_url` and `schema_url` for email reconfirmation to work'
'Method `update_with_email` requires attribute `confirmation_url` for email reconfirmation to work'
)
end
end

private

def check_deprecated_attributes
if [@attributes[:schema_url], @attributes[:confirmation_success_url]].any?(&:present?)
ActiveSupport::Deprecation.warn(<<-DEPRECATION.strip_heredoc, caller)
Providing `schema_url` and `confirmation_success_url` to `update_with_email` is deprecated and will be
removed in a future version of this gem.

Now you must only provide `confirmation_url` and the email will contain the new format of the confirmation
url that needs to be used with the new `confirmRegistrationWithToken` on the client application.
DEPRECATION
end
end

def required_reconfirm_attributes?
@attributes[:schema_url].present? &&
(@attributes[:confirmation_success_url].present? || DeviseTokenAuth.default_confirm_success_url.present?)
if @attributes[:schema_url].present?
[@attributes[:confirmation_success_url], DeviseTokenAuth.default_confirm_success_url].any?(&:present?)
else
[@attributes[:confirmation_url], DeviseTokenAuth.default_confirm_success_url].any?(&:present?)
end
end

def requires_reconfirmation?(resource_attributes)
Expand All @@ -60,13 +77,22 @@ def email_in_database
end
end

def confirmation_method_params
if @attributes[:schema_url].present?
{
redirect_url: @attributes[:confirmation_success_url] || DeviseTokenAuth.default_confirm_success_url,
schema_url: @attributes[:schema_url]
}
else
{ redirect_url: @attributes[:confirmation_url] || DeviseTokenAuth.default_confirm_success_url }
end
end

def send_confirmation_instructions(saved)
return unless saved

@resource.send_confirmation_instructions(
redirect_url: @attributes[:confirmation_success_url] || DeviseTokenAuth.default_confirm_success_url,
template_path: ['graphql_devise/mailer'],
schema_url: @attributes[:schema_url]
confirmation_method_params.merge(template_path: ['graphql_devise/mailer'])
)
end
end
Expand Down
30 changes: 30 additions & 0 deletions lib/graphql_devise/mutations/confirm_registration_with_token.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

module GraphqlDevise
module Mutations
class ConfirmRegistrationWithToken < Base
argument :confirmation_token, String, required: true

field :credentials,
GraphqlDevise::Types::CredentialType,
null: true,
description: 'Authentication credentials. Null unless user is signed in after confirmation.'

def resolve(confirmation_token:)
resource = resource_class.confirm_by_token(confirmation_token)

if resource.errors.empty?
yield resource if block_given?

response_payload = { authenticatable: resource }

response_payload[:credentials] = set_auth_headers(resource) if resource.active_for_authentication?

response_payload
else
raise_user_error(I18n.t('graphql_devise.confirmations.invalid_token'))
end
end
end
end
end
60 changes: 60 additions & 0 deletions lib/graphql_devise/mutations/register.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# frozen_string_literal: true

module GraphqlDevise
module Mutations
class Register < Base
argument :email, String, required: true
argument :password, String, required: true
argument :password_confirmation, String, required: true
argument :confirm_url, String, required: false

field :credentials,
GraphqlDevise::Types::CredentialType,
null: true,
description: 'Authentication credentials. Null if after signUp resource is not active for authentication (e.g. Email confirmation required).'

def resolve(confirm_url: nil, **attrs)
resource = build_resource(attrs.merge(provider: provider))
raise_user_error(I18n.t('graphql_devise.resource_build_failed')) if resource.blank?

redirect_url = confirm_url || DeviseTokenAuth.default_confirm_success_url
if confirmable_enabled? && redirect_url.blank?
raise_user_error(I18n.t('graphql_devise.registrations.missing_confirm_redirect_url'))
end

check_redirect_url_whitelist!(redirect_url)

resource.skip_confirmation_notification! if resource.respond_to?(:skip_confirmation_notification!)

if resource.save
yield resource if block_given?

unless resource.confirmed?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this consider skip_confirmation_notification ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really related, skip_confirmation_notification simply prevents regular devise email from going out. DTA does the same

resource.send_confirmation_instructions(
redirect_url: redirect_url,
template_path: ['graphql_devise/mailer']
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Starting to feel we can have a common place for this path

)
end

response_payload = { authenticatable: resource }

response_payload[:credentials] = set_auth_headers(resource) if resource.active_for_authentication?

response_payload
else
resource.try(:clean_up_passwords)
raise_user_error_list(
I18n.t('graphql_devise.registration_failed'),
errors: resource.errors.full_messages
)
end
end

private

def build_resource(attrs)
resource_class.new(attrs)
end
end
end
end
2 changes: 1 addition & 1 deletion lib/graphql_devise/mutations/sign_up.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def resolve(confirm_success_url: nil, **attrs)

unless resource.confirmed?
resource.send_confirmation_instructions(
redirect_url: confirm_success_url,
redirect_url: redirect_url,
template_path: ['graphql_devise/mailer'],
schema_url: controller.full_url_without_params
)
Expand Down
14 changes: 14 additions & 0 deletions spec/dummy/app/graphql/mutations/register.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# frozen_string_literal: true

module Mutations
class Register < GraphqlDevise::Mutations::Register
argument :name, String, required: false

field :user, Types::UserType, null: true

def resolve(email:, **attrs)
original_payload = super
original_payload.merge(user: original_payload[:authenticatable])
end
end
end
9 changes: 5 additions & 4 deletions spec/dummy/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

Rails.application.routes.draw do
mount_graphql_devise_for 'User', at: '/api/v1/graphql_auth', operations: {
login: Mutations::Login,
sign_up: Mutations::SignUp
login: Mutations::Login,
sign_up: Mutations::SignUp,
register: Mutations::Register
}, additional_mutations: {
register_confirmed_user: Mutations::RegisterConfirmedUser
}, additional_queries: {
Expand All @@ -13,7 +14,7 @@
mount_graphql_devise_for(
Admin,
authenticatable_type: Types::CustomAdminType,
skip: [:sign_up, :check_password_token],
skip: [:sign_up, :register, :check_password_token],
operations: {
confirm_account: Resolvers::ConfirmAdminAccount,
update_password_with_token: Mutations::ResetAdminPasswordWithToken
Expand All @@ -23,7 +24,7 @@

mount_graphql_devise_for(
'Guest',
only: [:login, :logout, :sign_up],
only: [:login, :logout, :sign_up, :register],
at: '/api/v1/guest/graphql_auth'
)

Expand Down
Loading