groovestack-auth 0.1.5 → 0.1.6

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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +8 -1
  3. data/Gemfile.lock +120 -20
  4. data/{lib/groovestack/auth/action_cable.rb → app/channels/groovestack/auth/action_cable/connection.rb} +2 -2
  5. data/app/controllers/concerns/groovestack/auth/graphql/controllers/auth_helpers.rb +69 -0
  6. data/app/controllers/concerns/groovestack/auth/graphql/controllers/authed_execute.rb +16 -0
  7. data/app/controllers/groovestack/auth/authenticated_api_controller.rb +10 -0
  8. data/app/controllers/groovestack/auth/omniauth_callbacks_controller.rb +138 -0
  9. data/app/controllers/groovestack/auth/passwordless/magic_links_controller.rb +58 -0
  10. data/app/controllers/groovestack/auth/passwordless/sessions_controller.rb +75 -0
  11. data/app/graphql/graphql/identity_extensions.rb +11 -0
  12. data/app/graphql/graphql/user_extensions.rb +14 -0
  13. data/app/models/concerns/groovestack/auth/authorized_fields_for_serialization.rb +21 -0
  14. data/app/models/concerns/groovestack/auth/identity.rb +39 -0
  15. data/app/models/concerns/groovestack/auth/user.rb +14 -0
  16. data/app/views/devise/mailer/magic_link.html.erb +9 -0
  17. data/config/initializers/core_config.rb +0 -6
  18. data/config/initializers/devise.rb +387 -302
  19. data/config/initializers/omniauth.rb +0 -19
  20. data/config/locales/devise.en.yml +71 -0
  21. data/db/migrate/20231103174050_add_devise_to_users_and_identities.rb +59 -0
  22. data/groovestack-auth.gemspec +7 -7
  23. data/lib/groovestack/auth/{railtie.rb → engine.rb} +13 -2
  24. data/lib/groovestack/auth/graphql/authorized_field.rb +19 -0
  25. data/lib/groovestack/auth/graphql/authorized_object.rb +11 -0
  26. data/lib/groovestack/auth/graphql/schema_visibility.rb +40 -0
  27. data/lib/groovestack/auth/graphql/visible_field.rb +21 -0
  28. data/lib/groovestack/auth/graphql/visible_object.rb +17 -0
  29. data/lib/groovestack/auth/passwordless/t_otp_tokenizer.rb +89 -0
  30. data/lib/groovestack/auth/provider.rb +7 -0
  31. data/lib/groovestack/auth/providers/apple.rb +5 -5
  32. data/lib/groovestack/auth/providers/facebook.rb +17 -0
  33. data/lib/groovestack/auth/providers/google.rb +1 -1
  34. data/lib/groovestack/auth/providers/omni_auth.rb +2 -2
  35. data/lib/groovestack/auth/routes.rb +26 -0
  36. data/lib/groovestack/auth/settings.rb +43 -0
  37. data/lib/groovestack/auth/version.rb +1 -1
  38. data/lib/groovestack/auth.rb +33 -83
  39. metadata +55 -50
  40. data/config/initializers/devise_token_auth.rb +0 -72
  41. data/config/initializers/graphql_devise.rb +0 -58
  42. data/config/routes.rb +0 -11
  43. data/db/migrate/20231103172517_create_users.rb +0 -54
  44. data/db/migrate/20231103174037_create_identities.rb +0 -19
  45. data/lib/fabricators/user_fabricator.rb +0 -17
  46. data/lib/graphql/identity/filter.rb +0 -13
  47. data/lib/graphql/identity/mutations.rb +0 -27
  48. data/lib/graphql/identity/queries.rb +0 -25
  49. data/lib/graphql/identity/type.rb +0 -22
  50. data/lib/graphql/user/filter.rb +0 -15
  51. data/lib/graphql/user/mutations.rb +0 -63
  52. data/lib/graphql/user/queries.rb +0 -40
  53. data/lib/graphql/user/type.rb +0 -30
  54. data/lib/groovestack/auth/authenticated_api_controller.rb +0 -13
  55. data/lib/groovestack/auth/omniauth_callbacks_controller.rb +0 -111
  56. data/lib/groovestack/auth/schema_plugin.rb +0 -19
  57. data/lib/identity.rb +0 -31
  58. data/lib/user.rb +0 -53
  59. data/lib/users/roles.rb +0 -42
@@ -1,24 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- Rails.application.config.middleware.use OmniAuth::Builder do
4
- Groovestack::Auth.configured_providers(ancestor: Groovestack::Auth::Providers::OmniAuth).each do |p|
5
- provider(*p.generate_omniauth_args)
6
- end
7
- end
8
-
9
- module Groovestack
10
- module Auth
11
- class OmniauthFailureEndpoint < OmniAuth::FailureEndpoint
12
- def call
13
- # raise_out! if OmniAuth.config.failure_raise_out_environments.include?(ENV['RACK_ENV'].to_s)
14
- redirect_to_failure
15
- end
16
- end
17
- end
18
- end
19
-
20
- OmniAuth.config.on_failure = Groovestack::Auth::OmniauthFailureEndpoint
21
-
22
3
  module OmniAuth
23
4
  module Strategies
24
5
  class Apple < OmniAuth::Strategies::OAuth2
@@ -0,0 +1,71 @@
1
+ ---
2
+ en:
3
+ devise:
4
+ confirmations:
5
+ confirmed: "Your email address has been successfully confirmed."
6
+ send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes."
7
+ send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes."
8
+ failure:
9
+ already_authenticated: "You are already signed in."
10
+ inactive: "Your account is not activated yet."
11
+ invalid: "Invalid %{authentication_keys} or password."
12
+ locked: "Your account is locked."
13
+ last_attempt: "You have one more attempt before your account is locked."
14
+ not_found_in_database: "Invalid %{authentication_keys} or password."
15
+ timeout: "Your session expired. Please sign in again to continue."
16
+ unauthenticated: "You need to sign in or sign up before continuing."
17
+ unconfirmed: "You have to confirm your email address before continuing."
18
+ magic_link_invalid: "Invalid or expired login link."
19
+ mailer:
20
+ confirmation_instructions:
21
+ subject: "Confirmation instructions"
22
+ reset_password_instructions:
23
+ subject: "Reset password instructions"
24
+ unlock_instructions:
25
+ subject: "Unlock instructions"
26
+ email_changed:
27
+ subject: "Email Changed"
28
+ password_change:
29
+ subject: "Password Changed"
30
+ magic_link:
31
+ subject: "Here's your magic login link ✨"
32
+ omniauth_callbacks:
33
+ failure: "Could not authenticate you from %{kind} because \"%{reason}\"."
34
+ success: "Successfully authenticated from %{kind} account."
35
+ passwords:
36
+ no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided."
37
+ send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes."
38
+ send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes."
39
+ updated: "Your password has been changed successfully. You are now signed in."
40
+ updated_not_active: "Your password has been changed successfully."
41
+ registrations:
42
+ destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon."
43
+ signed_up: "Welcome! You have signed up successfully."
44
+ signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated."
45
+ signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked."
46
+ signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account."
47
+ update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirmation link to confirm your new email address."
48
+ updated: "Your account has been updated successfully."
49
+ updated_but_not_signed_in: "Your account has been updated successfully, but since your password was changed, you need to sign in again."
50
+ sessions:
51
+ signed_in: "Signed in successfully."
52
+ signed_out: "Signed out successfully."
53
+ already_signed_out: "Signed out successfully."
54
+ unlocks:
55
+ send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes."
56
+ send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes."
57
+ unlocked: "Your account has been unlocked successfully. Please sign in to continue."
58
+ passwordless:
59
+ not_found_in_database: "Could not find a user for that email address"
60
+ magic_link_sent: "A login link has been sent to your email address. Please follow the link to log in to your account."
61
+ magic_link_sent_paranoid: "If your account exists, you will receive an email with a login link. Please follow the link to log in to your account."
62
+ errors:
63
+ messages:
64
+ already_confirmed: "was already confirmed, please try signing in"
65
+ confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one"
66
+ expired: "has expired, please request a new one"
67
+ not_found: "not found"
68
+ not_locked: "was not locked"
69
+ not_saved:
70
+ one: "1 error prohibited this %{resource} from being saved:"
71
+ other: "%{count} errors prohibited this %{resource} from being saved:"
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddDeviseToUsersAndIdentities < ActiveRecord::Migration[7.0]
4
+ def change # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
5
+ # Add authentication columns to the existing users table
6
+ change_column_default :users, :email, from: nil, to: ''
7
+ change_column_null :users, :email, false
8
+
9
+ change_table :users do |t|
10
+ ## Database authenticatable
11
+ if Groovestack::Auth.devise_modules.include?(:database_authenticatable)
12
+ t.string :encrypted_password, null: false,
13
+ default: ''
14
+ end
15
+
16
+ ## Recoverable
17
+ if Groovestack::Auth.devise_modules.include?(:recoverable)
18
+ t.string :reset_password_token
19
+ t.datetime :reset_password_sent_at
20
+ end
21
+
22
+ ## Rememberable
23
+ t.datetime :remember_created_at if Groovestack::Auth.devise_modules.include?(:rememberable)
24
+
25
+ ## Trackable
26
+ if Groovestack::Auth.devise_modules.include?(:trackable)
27
+ t.integer :sign_in_count, default: 0, null: false
28
+ t.datetime :current_sign_in_at
29
+ t.datetime :last_sign_in_at
30
+ t.string :current_sign_in_ip
31
+ t.string :last_sign_in_ip
32
+ end
33
+
34
+ ## Confirmable
35
+ if Groovestack::Auth.devise_modules.include?(:confirmable)
36
+ t.string :confirmation_token
37
+ t.datetime :confirmed_at
38
+ t.datetime :confirmation_sent_at
39
+ t.string :unconfirmed_email # Only if using reconfirmable
40
+ end
41
+
42
+ ## Lockable
43
+ if Groovestack::Auth.devise_modules.include?(:lockable)
44
+ t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
45
+ t.string :unlock_token # Only if unlock strategy is :email or :both
46
+ t.datetime :locked_at
47
+ end
48
+ end
49
+
50
+ add_index :users, :reset_password_token, unique: true if Groovestack::Auth.devise_modules.include?(:recoverable)
51
+ add_index :users, :confirmation_token, unique: true if Groovestack::Auth.devise_modules.include?(:confirmable)
52
+ add_index :users, :unlock_token, unique: true if Groovestack::Auth.devise_modules.include?(:lockable)
53
+
54
+ # Add Omniauth-related columns to the existing identities table
55
+ change_table :identities do |t|
56
+ t.jsonb :omniauth_data
57
+ end
58
+ end
59
+ end
@@ -26,17 +26,17 @@ Gem::Specification.new do |spec|
26
26
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
27
27
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
28
28
  end
29
+ spec.files += Dir['{app}/**/*']
30
+
29
31
  spec.bindir = 'exe'
30
32
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
33
  spec.require_paths = ['lib']
32
34
 
33
- spec.add_dependency 'groovestack-base', '~> 0.1', '>= 0.1.10'
34
- spec.add_dependency 'groovestack-config', '~> 0.1', '>= 0.1.2'
35
- spec.add_dependency 'jsonb_accessor', '~>1.4'
36
-
37
- # spec.add_development_dependency 'graphql_devise'
38
- spec.add_dependency 'omniauth-apple'
39
- spec.add_dependency 'omniauth-google-oauth2'
35
+ spec.add_dependency 'devise'
36
+ spec.add_dependency 'devise-passwordless'
37
+ spec.add_dependency 'groovestack-config', '~> 0.1', '>= 0.1.4'
38
+ spec.add_dependency 'groovestack-identities', '~> 0.1', '>= 0.1.3'
39
+ spec.add_dependency 'rotp'
40
40
 
41
41
  spec.metadata['rubygems_mfa_required'] = 'true'
42
42
  end
@@ -2,10 +2,10 @@
2
2
 
3
3
  require_relative 'provider'
4
4
 
5
- if defined?(Rails)
5
+ if defined?(Rails::Engine)
6
6
  module Groovestack
7
7
  module Auth
8
- class Railtie < ::Rails::Engine
8
+ class Engine < ::Rails::Engine
9
9
  include ::Groovestack::Base::CoreRailtie
10
10
 
11
11
  def dx_validations
@@ -47,9 +47,20 @@ if defined?(Rails)
47
47
  append_initializers app
48
48
  end
49
49
 
50
+ initializer :append_locales do |app|
51
+ append_locales app
52
+ end
53
+
50
54
  config.after_initialize do
51
55
  after_init
52
56
  end
57
+
58
+ config.to_prepare do
59
+ # Include Auth concerns after Rails is fully loaded to ensure Devise is loaded
60
+
61
+ ::User.include ::Groovestack::Auth::User
62
+ ::Identity.include ::Groovestack::Auth::Identity
63
+ end
53
64
  end
54
65
  end
55
66
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Groovestack
4
+ module Auth
5
+ module GraphQL
6
+ class AuthorizedField < ::Groovestack::Base::GraphQL::Base::Field
7
+ def authorized?(obj, args, ctx)
8
+ authorized_fields = begin
9
+ obj.authorized_fields_for_serialization(ctx[:current_user])
10
+ rescue StandardError
11
+ []
12
+ end
13
+
14
+ super && authorized_fields.include?(name.to_sym)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Groovestack
4
+ module Auth
5
+ module GraphQL
6
+ class AuthorizedObject < ::Groovestack::Base::GraphQL::Base::Object
7
+ field_class ::Groovestack::Auth::GraphQL::AuthorizedField
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Groovestack
4
+ module Auth
5
+ module GraphQL
6
+ module SchemaVisibility
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ use ::GraphQL::Schema::Visibility
11
+
12
+ # NOTE: currently, this is user role based
13
+ # visibility_profiles enumerate the permissions each
14
+ # role has access to
15
+
16
+ @visibility_profiles = [
17
+ { key: :anon, permissions: [:public] },
18
+ { key: :user, permissions: %i[public basic] },
19
+ { key: ::User::Role::STAFF, permissions: [:public, :basic, ::User::Role::STAFF] },
20
+ { key: ::User::Role::EXEC, permissions: [:public, :basic, ::User::Role::STAFF, ::User::Role::EXEC] },
21
+ { key: ::User::Role::ADMIN,
22
+ permissions: [:public, :basic, ::User::Role::STAFF, ::User::Role::EXEC, ::User::Role::ADMIN] }
23
+ ].each_with_object({}) do |profile, acc|
24
+ acc[profile[:key].to_sym] = profile[:permissions].map(&:to_sym)
25
+ end.freeze
26
+
27
+ class << self
28
+ attr_reader :visibility_profiles
29
+
30
+ def visibility_profile_for_context(context)
31
+ return [] if context[:visibility_profile].blank?
32
+
33
+ visibility_profiles[context[:visibility_profile].to_sym] || []
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Groovestack
4
+ module Auth
5
+ module GraphQL
6
+ class VisibleField < ::Groovestack::Base::GraphQL::Base::Field
7
+ def visible?(context)
8
+ return super unless @visibility_permission
9
+
10
+ # visibility profile are the visibility levels the
11
+ # current user is authorized for
12
+ visibility_profile = context.schema.visibility_profile_for_context(context).map(&:to_sym)
13
+
14
+ return super unless visibility_profile
15
+
16
+ super && visibility_profile.include?(@visibility_permission.to_sym)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Groovestack
4
+ module Auth
5
+ module GraphQL
6
+ class VisibleObject < ::Groovestack::Base::GraphQL::Base::Object
7
+ field_class ::Groovestack::Auth::GraphQL::VisibleField
8
+
9
+ def self.visible?(context)
10
+ any_visible_fields = fields.values.any? { |field| field.visible?(context) }
11
+
12
+ super && any_visible_fields
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rotp'
4
+ # require 'base32' # You can use any Base32 encoder
5
+
6
+ module Groovestack
7
+ module Auth
8
+ module Passwordless
9
+ module TOtpTokenizer
10
+ module_function
11
+
12
+ # Validity period (in seconds)
13
+ def token_validity_seconds
14
+ Devise.passwordless_login_within.seconds
15
+ end
16
+
17
+ # Generate a TOTP instance for the resource.
18
+ #
19
+ # We derive a per‑resource secret from a combination of the resource’s id and Rails’ secret.
20
+ def totp_for(resource, digits: 4, interval: token_validity_seconds)
21
+ # add last sign in at to prevent same otp being generated multiple times
22
+ identifier = resource.id
23
+ identifier += ":#{resource.current_sign_in_at.to_i}" if resource.current_sign_in_at.present?
24
+
25
+ raw_secret = OpenSSL::HMAC.digest('sha1', Rails.application.secret_key_base, identifier)
26
+ base32_secret = ROTP::Base32.encode raw_secret
27
+
28
+ # Create a TOTP object configured for 4 digits and the given time interval.
29
+ ROTP::TOTP.new(base32_secret, digits: digits, interval: interval)
30
+ end
31
+
32
+ # Generates the token for the current time window.
33
+ def encode(resource, _extra: nil, _expires_at: nil)
34
+ # otp = totp_for(resource).now
35
+
36
+ # otp_as_formatted_code(otp)
37
+ totp_for(resource).now
38
+ end
39
+
40
+ # Verifies that the provided token is valid (i.e. within the current time window).
41
+ #
42
+ # Since the token is time‐dependent, expiration is enforced by the TOTP algorithm.
43
+ def decode(code, email, resource_class, _as_of: nil, _expire_duration: nil)
44
+ raise ::Devise::Passwordless::InvalidTokenError if code.blank?
45
+
46
+ # For TOTP, we look up the resource and then verify the code.
47
+ # Note: How you locate the resource is up to your application.
48
+ # For example, if your code is combined with an email or user id in the URL, you would look it up there.
49
+ #
50
+ resource = resource_class.find_by(email: email)
51
+
52
+ raise 'Resource not found' if resource.blank?
53
+
54
+ # # Remove the hyphen to get the plain digits.
55
+ # otp = otp_from_formatted_code(code)
56
+ otp = code
57
+
58
+ verify_opts = {}
59
+ # by default, ROTP is fixed window interval. This code enables a sliding window
60
+ now = Time.zone.now
61
+ verify_opts[:at] = now
62
+ verify_opts[:drift_behind] = token_validity_seconds / 2 # (now.sec % token_validity_seconds) - 1
63
+ if resource_class.passwordless_expire_old_tokens_on_sign_in && resource.current_sign_in_at.present?
64
+ # support login / logout / login again within same token validity period
65
+ verify_opts[:after] = (resource.current_sign_in_at - token_validity_seconds).to_i
66
+ end
67
+ # end sliding window logic
68
+ decrypted_data = totp_for(resource).verify(otp, **verify_opts)
69
+
70
+ raise ::Devise::Passwordless::InvalidOrExpiredTokenError if decrypted_data.blank?
71
+
72
+ [resource, { data: decrypted_data }]
73
+ end
74
+
75
+ # # Helper: Formats an 4‑digit code as "xxxx".
76
+ # def otp_as_formatted_code(otp)
77
+ # otp = otp.to_s
78
+ # code = otp.length == 4 ? otp.dup.insert(2, '-') : otp
79
+ # code
80
+ # end
81
+
82
+ # def otp_from_formatted_code(formatted_code)
83
+ # otp = formatted_code.delete('-')
84
+ # otp
85
+ # end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -54,6 +54,13 @@ module Groovestack
54
54
 
55
55
  verbose.slice(*keys)
56
56
  end
57
+
58
+ def self.credential_for(key)
59
+ provider_credentials = Rails.application.credentials.groovestack.auth.send(k)
60
+ return if provider_credentials.blank?
61
+
62
+ provider_credentials.send(key)
63
+ end
57
64
  end
58
65
  end
59
66
  end
@@ -5,17 +5,17 @@ module Groovestack
5
5
  module Providers
6
6
  class Apple < OmniAuth
7
7
  PROVIDER = :apple
8
- REQUIRED_CREDENTIALS = %i[APPLE_CLIENT_ID APPLE_TEAM_ID APPLE_KEY_ID APPLE_PEM_CONTENT].freeze
8
+ REQUIRED_CREDENTIALS = %i[client_id team_id key_id pem_content].freeze
9
9
 
10
10
  def self.generate_omniauth_args
11
11
  [
12
12
  provider,
13
- Rails.application.credentials.APPLE_CLIENT_ID,
13
+ credential_for(:client_id),
14
14
  '',
15
15
  {
16
- team_id: Rails.application.credentials.APPLE_TEAM_ID,
17
- key_id: Rails.application.credentials.APPLE_KEY_ID,
18
- pem: Rails.application.credentials.APPLE_PEM_CONTENT,
16
+ team_id: credential_for(:team_id),
17
+ key_id: credential_for(:key_id),
18
+ pem: credential_for(:pem_content),
19
19
  origin_param: 'return_to'
20
20
  }
21
21
  ]
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Groovestack
4
+ module Auth
5
+ module Providers
6
+ class Facebook < OmniAuth
7
+ PROVIDER = :facebook
8
+ K = :facebook
9
+ REQUIRED_CREDENTIALS = %i[app_id app_secret].freeze
10
+
11
+ def self.generate_omniauth_args
12
+ [*super, { client_options: { site: 'https://graph.facebook.com/v22.0' } }]
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -6,7 +6,7 @@ module Groovestack
6
6
  class Google < OmniAuth
7
7
  PROVIDER = :google_oauth2
8
8
  K = :google
9
- REQUIRED_CREDENTIALS = %i[GOOGLE_CLIENT_ID GOOGLE_CLIENT_SECRET].freeze
9
+ REQUIRED_CREDENTIALS = %i[client_id client_secret].freeze
10
10
 
11
11
  def self.generate_omniauth_args
12
12
  super << { name: k, origin_param: 'return_to' }
@@ -11,7 +11,7 @@ module Groovestack
11
11
  end
12
12
 
13
13
  def self.required_credentials_present?
14
- required_credentials.all? { |credential| Rails.application.credentials.send(credential).present? }
14
+ required_credentials.all? { |credential| credential_for(credential).present? }
15
15
  end
16
16
 
17
17
  def self.configured?
@@ -22,7 +22,7 @@ module Groovestack
22
22
  # different providers require different arguments
23
23
  [
24
24
  provider,
25
- *required_credentials.map { |c| Rails.application.credentials.send(c) }
25
+ *required_credentials.map { |credential| credential_for(credential) }
26
26
  ]
27
27
  end
28
28
 
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Groovestack
4
+ module Auth
5
+ module Routes
6
+ extend ActiveSupport::Concern
7
+
8
+ class_methods do
9
+ def draw_routes(mapper)
10
+ mapper.instance_exec do
11
+ devise_for :users, ::Groovestack::Auth.devise_for_routes_kwargs
12
+
13
+ devise_scope :user do
14
+ post 'users/magic_link', to: 'groovestack/auth/passwordless/magic_links#show'
15
+
16
+ if defined?(OmniAuth)
17
+ match '/users/auth/:provider/callback', to: 'groovestack/auth/omniauth_callbacks#verified', via: %i[get post],
18
+ as: :omniauth_callback
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Groovestack
4
+ module Auth
5
+ module Settings
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ extend Dry::Configurable
10
+
11
+ # NOTE: this impact user devise migration changes. Be sure
12
+ # to set / override this when initialling integrating with groovestack-auth
13
+ setting :devise_modules, default: [
14
+ :database_authenticatable,
15
+ :registerable,
16
+ :recoverable,
17
+ :rememberable,
18
+ :validatable,
19
+ :confirmable,
20
+ :lockable,
21
+ :timeoutable,
22
+ :trackable,
23
+ :magic_link_authenticatable,
24
+ :omniauthable,
25
+ { omniauth_providers: %i[apple facebook google] }
26
+ ], reader: true
27
+
28
+ setting :devise_for_routes_kwargs, default: {
29
+ controllers: {
30
+ omniauth_callbacks: 'groovestack/auth/omniauth_callbacks',
31
+ sessions: 'groovestack/auth/passwordless/sessions'
32
+ }
33
+ }, reader: true
34
+
35
+ setting :disabled_providers, default: [], reader: true
36
+
37
+ setting :omniauth_origin_url, default: nil, reader: true
38
+ setting :after_sign_in_path, default: nil, reader: true
39
+ setting :allow_other_host_redirects, default: false, reader: true
40
+ end
41
+ end
42
+ end
43
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Groovestack
4
4
  module Auth
5
- VERSION = '0.1.5'
5
+ VERSION = '0.1.6'
6
6
  end
7
7
  end