phcdevworks_accounts_stytch 0.1.0.pre.1 → 0.2.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +43 -4
  3. data/app/controllers/concerns/error_handler.rb +19 -0
  4. data/app/controllers/concerns/handle_service_action.rb +17 -0
  5. data/app/controllers/concerns/organization_setter.rb +11 -0
  6. data/app/controllers/phcdevworks_accounts_stytch/b2b/authenticate_controller.rb +81 -0
  7. data/app/controllers/phcdevworks_accounts_stytch/b2b/magic_links_controller.rb +57 -0
  8. data/app/controllers/phcdevworks_accounts_stytch/b2b/passwords_controller.rb +95 -0
  9. data/app/controllers/phcdevworks_accounts_stytch/b2c/authenticate_controller.rb +99 -0
  10. data/app/controllers/phcdevworks_accounts_stytch/b2c/magic_links_controller.rb +90 -0
  11. data/app/controllers/phcdevworks_accounts_stytch/b2c/passwords_controller.rb +94 -0
  12. data/app/views/layouts/phcdevworks_accounts_stytch/application.html.erb +2 -0
  13. data/app/views/phcdevworks_accounts_stytch/b2b/magic_links/invite.html.erb +12 -0
  14. data/app/views/phcdevworks_accounts_stytch/b2b/magic_links/login_or_signup.html.erb +12 -0
  15. data/app/views/phcdevworks_accounts_stytch/b2b/passwords/reset_existing_password.html.erb +22 -0
  16. data/app/views/phcdevworks_accounts_stytch/b2b/passwords/reset_password.html.erb +17 -0
  17. data/app/views/phcdevworks_accounts_stytch/b2b/passwords/reset_start.html.erb +12 -0
  18. data/app/views/phcdevworks_accounts_stytch/b2b/passwords/reset_with_session.html.erb +17 -0
  19. data/app/views/phcdevworks_accounts_stytch/b2c/magic_links/invite.html.erb +12 -0
  20. data/app/views/phcdevworks_accounts_stytch/b2c/magic_links/login_or_signup.html.erb +12 -0
  21. data/app/views/phcdevworks_accounts_stytch/b2c/passwords/reset_existing_password.html.erb +22 -0
  22. data/app/views/phcdevworks_accounts_stytch/b2c/passwords/reset_password.html.erb +17 -0
  23. data/app/views/phcdevworks_accounts_stytch/b2c/passwords/reset_start.html.erb +12 -0
  24. data/app/views/phcdevworks_accounts_stytch/b2c/passwords/reset_with_session.html.erb +17 -0
  25. data/config/routes/b2b.rb +30 -0
  26. data/config/routes/b2c.rb +27 -0
  27. data/config/routes.rb +2 -4
  28. data/lib/phcdevworks_accounts_stytch/authentication/b2b/magic_link_service.rb +63 -0
  29. data/lib/phcdevworks_accounts_stytch/authentication/b2b/password_service.rb +80 -0
  30. data/lib/phcdevworks_accounts_stytch/authentication/b2c/magic_link_service.rb +55 -0
  31. data/lib/phcdevworks_accounts_stytch/authentication/b2c/password_service.rb +76 -0
  32. data/lib/phcdevworks_accounts_stytch/engine.rb +6 -0
  33. data/lib/phcdevworks_accounts_stytch/stytch/client.rb +45 -0
  34. data/lib/phcdevworks_accounts_stytch/stytch/error.rb +26 -0
  35. data/lib/phcdevworks_accounts_stytch/stytch/method_options.rb +21 -0
  36. data/lib/phcdevworks_accounts_stytch/stytch/organization.rb +51 -0
  37. data/lib/phcdevworks_accounts_stytch/stytch/response.rb +44 -0
  38. data/lib/phcdevworks_accounts_stytch/stytch/success.rb +19 -0
  39. data/lib/phcdevworks_accounts_stytch/version.rb +1 -1
  40. data/lib/phcdevworks_accounts_stytch.rb +3 -4
  41. metadata +37 -13
  42. data/app/controllers/phcdevworks_accounts_stytch/authentication/login_controller.rb +0 -26
  43. data/app/controllers/phcdevworks_accounts_stytch/authentication/processor_controller.rb +0 -36
  44. data/lib/phcdevworks_accounts_stytch/stytch_client.rb +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c563b7ded4134aa0d9dfec2c7752d8bd906acc4e0ae5e7c0cdcba0329826ce0b
4
- data.tar.gz: 8eb6102a8d03caf22d873c7dee953fe0e0e504ba1ddd56447b56221ea495e1a3
3
+ metadata.gz: ba334aff9b9da05bcf1eb9b82254ccb41fd0b74c3d657256d48da8d56ef6a13a
4
+ data.tar.gz: a53b0d2d60349b910aeea9c6e936a81a534eaba7977d07b933b02f200bf443e0
5
5
  SHA512:
6
- metadata.gz: 14c8913938b0b496cd581711c88e23a591687e05d05189b774c6b7ad05324db31ae9d466969ec480561d5ac00566ddb922e5934dd8911b1c1fd1bc0238dd4526
7
- data.tar.gz: '04829407918f5ce3b36dac18852065b559fdee68032fe4a2325e683127788c67474924a769f117be7cc236a0d4dab8a85c2d316977766cce5fd60abaaa3803cf'
6
+ metadata.gz: 71c1c67e177fbb66824cd19af5cc8e2f868af3ab7cbff5646034a060636ea7a70bca142caa82cb3c835bbc64cdef10388466c26810c5d25086f2fd3f81e61a9c
7
+ data.tar.gz: f150aaf721f9f82903e1033879cbba3981119c33a31962d3bddb29331be561010e9352cdbaec2676974e60eae7551a6e9d8abd74d1231f5b552b4cd2d8986916
data/README.md CHANGED
@@ -1,8 +1,47 @@
1
- # PhcdevworksAccountsStytch
2
- Short description and motivation.
1
+ # PHCDevworks Accounts Stytch
2
+
3
+ ![Build Status](https://github.com/phcdevworks/phcdevworks_accounts_stytch/actions/workflows/test.yml/badge.svg)
4
+ ![Gem Version](https://img.shields.io/gem/v/phcdevworks_accounts_stytch.svg)
5
+ ![License](https://img.shields.io/github/license/phcdevworks/phcdevworks_accounts_stytch.svg)
6
+ [![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square)](#contributors-)
7
+ ![Last Commit](https://img.shields.io/github/last-commit/phcdevworks/phcdevworks_accounts_stytch.svg)
8
+ ![Issues](https://img.shields.io/github/issues/phcdevworks/phcdevworks_accounts_stytch.svg)
9
+
10
+ ![Forks](https://img.shields.io/github/forks/phcdevworks/phcdevworks_accounts_stytch.svg?style=social)
11
+ ![Stars](https://img.shields.io/github/stars/phcdevworks/phcdevworks_accounts_stytch.svg?style=social)
12
+ [![Tweet](https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Fgithub.com%2Fphcdevworks%2Fphcdevworks_accounts_stytch)](https://twitter.com/intent/tweet?text=Check%20out%20this%20authentication%20gem%20for%20Rails%20integrating%20with%20Stytch!%20https://github.com/phcdevworks/phcdevworks_accounts_stytch)
13
+
14
+ PHCDevworks Accounts Stytch is an authentication system that integrates with the Stytch API to provide seamless B2B and B2C user authentication for Ruby on Rails apps. The project includes:
15
+
16
+ - **Magic Link Authentication**: Users can authenticate via secure magic links sent to their email.
17
+ - **Password Authentication**: Support for password-based login and reset flows, including registration and session management.
18
+ - **Password Reset Management**: Users can reset their passwords via email, token, existing password, or session token.
19
+ - **Organization Support**: The B2B module includes organization-specific user authentication with integration of organization IDs.
20
+ - **Service Handling**: All service actions are abstracted into service objects that handle interaction with Stytch’s API for better maintainability and testability.
21
+ - **Error Handling & Logging**: Robust error logging and consistent handling of service responses ensure reliability and traceability of issues.
3
22
 
4
23
  ## Usage
5
- How to use my plugin.
24
+
25
+ ### 1. Set up Stytch API credentials
26
+
27
+ You will need to store your Stytch API credentials securely in your Rails `credentials.yml.enc` file. Follow these steps to open and edit the credentials file:
28
+
29
+ 1. Ensure you have the `master.key` file located in `config/master.key`.
30
+ 2. Open the encrypted credentials file using the following command in your terminal:
31
+ ```bash
32
+ rails credentials:edit
33
+ ```
34
+ 3. This will open the credentials file in your default text editor (or VSCode if configured). In the file, add your Stytch API credentials under both b2b and b2c keys, as shown below:
35
+ ```yml
36
+ stytch:
37
+ b2b:
38
+ project_id: <your_b2b_project_id>
39
+ secret: <your_b2b_secret>
40
+ b2c:
41
+ project_id: <your_b2c_project_id>
42
+ secret: <your_b2c_secret>
43
+ ```
44
+ 4. Save and close the file. Rails will automatically re-encrypt the credentials.
6
45
 
7
46
  ## Installation
8
47
  Add this line to your application's Gemfile:
@@ -22,7 +61,7 @@ $ gem install phcdevworks_accounts_stytch
22
61
  ```
23
62
 
24
63
  ## Contributing
25
- Contribution directions go here.
64
+ [![contributors](https://contributors-img.web.app/image?repo=phcdevworks/phcdevworks_accounts_stytch)](https://github.com/phcdevworks/phcdevworks_accounts_stytch/graphs/contributors)
26
65
 
27
66
  ## License
28
67
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ErrorHandler
4
+ extend ActiveSupport::Concern
5
+
6
+ def handle_unexpected_error(exception)
7
+ log_error("Unexpected error: #{exception.message}")
8
+ render json: { error: 'An unexpected error occurred.' }, status: :internal_server_error
9
+ end
10
+
11
+ def handle_missing_params_error(message)
12
+ log_error(message)
13
+ render json: { error: message }, status: :unprocessable_entity
14
+ end
15
+
16
+ def log_error(message)
17
+ Rails.logger.error(message)
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HandleServiceAction
4
+ extend ActiveSupport::Concern
5
+
6
+ def handle_service_action(action_name)
7
+ result = yield
8
+ if result.is_a?(Hash) && result.key?(:message)
9
+ render json: { message: result[:message], data: result[:data] }, status: :ok
10
+ else
11
+ render json: { message: 'Action completed successfully', data: result }, status: :ok
12
+ end
13
+ rescue PhcdevworksAccountsStytch::Stytch::Error => e
14
+ log_error("Stytch API error during #{action_name}: #{e.message}")
15
+ render json: { error: e.message }, status: :bad_request
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OrganizationSetter
4
+ extend ActiveSupport::Concern
5
+ def set_organization
6
+ organization_service = PhcdevworksAccountsStytch::Stytch::Organization.new
7
+ @organization_id = organization_service.find_organization_id_by_slug(params[:organization_slug])
8
+ rescue PhcdevworksAccountsStytch::Stytch::Error => e
9
+ handle_missing_params_error(e.message)
10
+ end
11
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhcdevworksAccountsStytch
4
+ module B2b
5
+ class AuthenticateController < ApplicationController
6
+ include ErrorHandler
7
+ include HandleServiceAction
8
+
9
+ def authenticate
10
+ if magic_link_token_present?
11
+ handle_magic_link_authentication
12
+ elsif email_password_and_organization_present?
13
+ handle_password_authentication
14
+ else
15
+ handle_missing_credentials
16
+ end
17
+ rescue StandardError => e
18
+ handle_unexpected_error(e)
19
+ end
20
+
21
+ def process_authenticate
22
+ if magic_link_token_present?
23
+ authenticate_with_magic_link
24
+ elsif email_password_and_organization_present?
25
+ authenticate_with_password
26
+ else
27
+ handle_missing_credentials
28
+ end
29
+ rescue StandardError => e
30
+ handle_unexpected_error(e)
31
+ end
32
+
33
+ private
34
+
35
+ def magic_link_token_present?
36
+ params[:token].present?
37
+ end
38
+
39
+ def email_password_and_organization_present?
40
+ params[:email].present? && params[:password].present? && params[:organization_id].present?
41
+ end
42
+
43
+ def handle_magic_link_authentication
44
+ redirect_to b2b_process_authenticate_path(token: params[:token])
45
+ end
46
+
47
+ def handle_password_authentication
48
+ redirect_to b2b_process_authenticate_path(email: params[:email], password: params[:password],
49
+ organization_id: params[:organization_id])
50
+ end
51
+
52
+ def handle_missing_credentials
53
+ handle_missing_params_error('Magic link token or email, password, and organization ID are required.')
54
+ end
55
+
56
+ def authenticate_with_magic_link
57
+ handle_service_action(:magic_link_authenticate) do
58
+ result = magic_link_service.process_authenticate(params[:token])
59
+ Rails.logger.info("Magic Link Authentication successful: #{result.data}")
60
+ result
61
+ end
62
+ end
63
+
64
+ def authenticate_with_password
65
+ handle_service_action(:password_authenticate) do
66
+ result = password_service.authenticate_password(params[:email], params[:password], params[:organization_id])
67
+ Rails.logger.info("Password Authentication successful: #{result.inspect}")
68
+ result
69
+ end
70
+ end
71
+
72
+ def magic_link_service
73
+ PhcdevworksAccountsStytch::Authentication::B2b::MagicLinkService.new
74
+ end
75
+
76
+ def password_service
77
+ PhcdevworksAccountsStytch::Authentication::B2b::PasswordService.new
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhcdevworksAccountsStytch
4
+ module B2b
5
+ class MagicLinksController < ApplicationController
6
+ include ErrorHandler
7
+ include OrganizationSetter
8
+ include HandleServiceAction
9
+
10
+ before_action :set_organization, only: %i[process_login_or_signup process_invite]
11
+
12
+ def login_or_signup; end
13
+
14
+ def process_login_or_signup
15
+ if missing_login_or_signup_params?
16
+ handle_missing_params_error('Email and Organization Slug are required.')
17
+ return
18
+ end
19
+
20
+ handle_service_action(:login_or_signup) do
21
+ result = service.process_login_or_signup(params[:email], @organization_id)
22
+ Rails.logger.info("Login or Signup successful: #{result.data}")
23
+ result
24
+ end
25
+ end
26
+
27
+ def invite; end
28
+
29
+ def process_invite
30
+ if missing_invite_params?
31
+ handle_missing_params_error('Email and Organization Slug are required.')
32
+ return
33
+ end
34
+
35
+ handle_service_action(:invite) do
36
+ result = service.process_invite(params[:email], @organization_id, params[:session_token])
37
+ Rails.logger.info("Invite successful: #{result.data}")
38
+ result
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def missing_login_or_signup_params?
45
+ params[:email].blank? || @organization_id.blank?
46
+ end
47
+
48
+ def missing_invite_params?
49
+ params[:email].blank? || @organization_id.blank?
50
+ end
51
+
52
+ def service
53
+ PhcdevworksAccountsStytch::Authentication::B2b::MagicLinkService.new
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhcdevworksAccountsStytch
4
+ module B2b
5
+ class PasswordsController < ApplicationController
6
+ include ErrorHandler
7
+ include OrganizationSetter
8
+ include HandleServiceAction
9
+
10
+ before_action :set_organization, only: %i[process_reset_start process_reset_existing_password process_reset_with_session]
11
+
12
+ def reset_start; end
13
+
14
+ def process_reset_start
15
+ if missing_reset_start_params?
16
+ handle_missing_params_error('Email and Organization Slug are required.')
17
+ return
18
+ end
19
+
20
+ handle_service_action(:reset_start) do
21
+ result = service.reset_start(params[:email], @organization_id)
22
+ Rails.logger.info("Password Reset Start successful: #{result.data}")
23
+ result
24
+ end
25
+ end
26
+
27
+ def reset_password; end
28
+
29
+ def process_reset_password
30
+ if missing_reset_password_params?
31
+ handle_missing_params_error('Token and Password are required.')
32
+ return
33
+ end
34
+
35
+ handle_service_action(:reset_password) do
36
+ result = service.reset(params[:token], params[:password])
37
+ Rails.logger.info("Password Reset Successful: #{result.data}")
38
+ result
39
+ end
40
+ end
41
+
42
+ def reset_existing_password; end
43
+
44
+ def process_reset_existing_password
45
+ if missing_existing_password_params?
46
+ handle_missing_params_error('Email, old password, new password, and organization ID are required.')
47
+ return
48
+ end
49
+
50
+ handle_service_action(:reset_existing_password) do
51
+ result = service.reset_existing(params[:email], params[:old_password], params[:new_password], @organization_id)
52
+ Rails.logger.info("Existing Password Reset Successful: #{result.data}")
53
+ result
54
+ end
55
+ end
56
+
57
+ def reset_with_session; end
58
+
59
+ def process_reset_with_session
60
+ if missing_reset_with_session_params?
61
+ handle_missing_params_error('Session token, new password, and organization ID are required.')
62
+ return
63
+ end
64
+
65
+ handle_service_action(:reset_with_session) do
66
+ result = service.reset_with_session(params[:session_token], params[:password], @organization_id)
67
+ Rails.logger.info("Session-based Password Reset Successful: #{result.data}")
68
+ result
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def missing_reset_start_params?
75
+ params[:email].blank? || @organization_id.blank?
76
+ end
77
+
78
+ def missing_reset_password_params?
79
+ params[:token].blank? || params[:password].blank?
80
+ end
81
+
82
+ def missing_existing_password_params?
83
+ params[:email].blank? || params[:old_password].blank? || params[:new_password].blank? || @organization_id.blank?
84
+ end
85
+
86
+ def missing_reset_with_session_params?
87
+ params[:session_token].blank? || params[:password].blank? || @organization_id.blank?
88
+ end
89
+
90
+ def service
91
+ PhcdevworksAccountsStytch::Authentication::B2b::PasswordService.new
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhcdevworksAccountsStytch
4
+ module B2c
5
+ class AuthenticateController < ApplicationController
6
+ def authenticate
7
+ if magic_link_token_present?
8
+ handle_magic_link_authentication
9
+ elsif email_and_password_present?
10
+ handle_password_authentication
11
+ else
12
+ handle_missing_credentials
13
+ end
14
+ rescue StandardError => e
15
+ handle_unexpected_error(e)
16
+ end
17
+
18
+ def process_authenticate
19
+ if magic_link_token_present?
20
+ authenticate_with_magic_link
21
+ elsif email_and_password_present?
22
+ authenticate_with_password
23
+ else
24
+ handle_missing_credentials
25
+ end
26
+ rescue StandardError => e
27
+ handle_unexpected_error(e)
28
+ end
29
+
30
+ private
31
+
32
+ def magic_link_token_present?
33
+ params[:token].present?
34
+ end
35
+
36
+ def email_and_password_present?
37
+ params[:email].present? && params[:password].present?
38
+ end
39
+
40
+ def handle_magic_link_authentication
41
+ redirect_to b2c_process_authenticate_path(token: params[:token])
42
+ end
43
+
44
+ def handle_password_authentication
45
+ redirect_to b2c_process_authenticate_path(email: params[:email], password: params[:password])
46
+ end
47
+
48
+ def handle_missing_credentials
49
+ log_error('Missing credentials for authentication')
50
+ render json: { error: 'Magic link token or email and password are required.' },
51
+ status: :unprocessable_entity
52
+ end
53
+
54
+ def authenticate_with_magic_link
55
+ handle_service_action(:magic_link_authenticate) do
56
+ result = magic_link_service.process_authenticate(params[:token])
57
+ Rails.logger.info("Magic Link Authentication successful: #{result.data}")
58
+ result
59
+ end
60
+ end
61
+
62
+ def authenticate_with_password
63
+ handle_service_action(:password_authenticate) do
64
+ result = password_service.authenticate_password(params[:email], params[:password])
65
+ Rails.logger.info("Password Authentication successful: #{result.inspect}")
66
+ result
67
+ end
68
+ end
69
+
70
+ def magic_link_service
71
+ PhcdevworksAccountsStytch::Authentication::B2c::MagicLinkService.new
72
+ end
73
+
74
+ def password_service
75
+ PhcdevworksAccountsStytch::Authentication::B2c::PasswordService.new
76
+ end
77
+
78
+ def log_error(message)
79
+ Rails.logger.error(message)
80
+ end
81
+
82
+ def handle_unexpected_error(exception)
83
+ log_error("Unexpected error during authentication: #{exception.message}")
84
+ render json: { error: 'An unexpected error occurred.' }, status: :internal_server_error
85
+ end
86
+
87
+ def handle_service_action(action_name)
88
+ result = yield
89
+ render json: { message: result.message, data: result.data }, status: :ok
90
+ rescue PhcdevworksAccountsStytch::Stytch::Error => e
91
+ log_error("Stytch API error during #{action_name}: #{e.message}")
92
+ render json: { error: e.message }, status: :bad_request
93
+ rescue StandardError => e
94
+ log_error("Unexpected error during #{action_name}: #{e.message}")
95
+ render json: { error: 'An unexpected error occurred.' }, status: :internal_server_error
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhcdevworksAccountsStytch
4
+ module B2c
5
+ class MagicLinksController < ApplicationController
6
+ def login_or_signup; end
7
+
8
+ def process_login_or_signup
9
+ if params[:email].blank?
10
+ log_error('Missing email')
11
+ render json: { error: 'Email is required.' }, status: :unprocessable_entity
12
+ return
13
+ end
14
+
15
+ handle_service_action(:login_or_signup) do
16
+ result = service.process_login_or_signup(params[:email])
17
+ Rails.logger.info("Login or Signup successful: #{result.data}")
18
+ result
19
+ end
20
+ end
21
+
22
+ def invite; end
23
+
24
+ def process_invite
25
+ if missing_required_params?
26
+ handle_missing_params_error
27
+ return
28
+ end
29
+
30
+ handle_invite_action
31
+ end
32
+
33
+ def process_revoke_invite
34
+ if params[:email].blank?
35
+ log_error('Missing email')
36
+ render json: { error: 'Email is required.' }, status: :unprocessable_entity
37
+ return
38
+ end
39
+
40
+ handle_service_action(:revoke_invite) do
41
+ result = service.process_revoke_invite(params[:email])
42
+ Rails.logger.info("Revoke invite successful: #{result.data}")
43
+ result
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def handle_service_action(action_name)
50
+ result = yield
51
+ if result.is_a?(Hash) && result.key?(:message)
52
+ render json: { message: result[:message], data: result[:data] }, status: :ok
53
+ else
54
+ render json: { message: 'Action completed successfully', data: result }, status: :ok
55
+ end
56
+ rescue PhcdevworksAccountsStytch::Stytch::Error => e
57
+ log_error("Stytch API error during #{action_name}: #{e.message}")
58
+ render json: { error: e.message }, status: :bad_request
59
+ rescue StandardError => e
60
+ log_error("Unexpected error during #{action_name}: #{e.message}")
61
+ render json: { error: 'An unexpected error occurred.' }, status: :internal_server_error
62
+ end
63
+
64
+ def handle_invite_action
65
+ handle_service_action(:invite) do
66
+ result = service.process_invite(params[:email])
67
+ Rails.logger.info("Invite successful: #{result.data}")
68
+ result
69
+ end
70
+ end
71
+
72
+ def handle_missing_params_error
73
+ log_error('Missing email')
74
+ render json: { error: 'Email is required.' }, status: :unprocessable_entity
75
+ end
76
+
77
+ def missing_required_params?
78
+ params[:email].blank?
79
+ end
80
+
81
+ def service
82
+ PhcdevworksAccountsStytch::Authentication::B2c::MagicLinkService.new
83
+ end
84
+
85
+ def log_error(message)
86
+ Rails.logger.error(message)
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhcdevworksAccountsStytch
4
+ module B2c
5
+ class PasswordsController < ApplicationController
6
+ include ErrorHandler
7
+ include HandleServiceAction # Removed OrganizationSetter
8
+
9
+ # Removed before_action :set_organization
10
+
11
+ def reset_start; end
12
+
13
+ def process_reset_start
14
+ if missing_reset_start_params?
15
+ handle_missing_params_error('Email is required.')
16
+ return
17
+ end
18
+
19
+ handle_service_action(:reset_start) do
20
+ result = service.reset_start(params[:email]) # Removed @organization_id
21
+ Rails.logger.info("Password Reset Start successful: #{result.data}")
22
+ result
23
+ end
24
+ end
25
+
26
+ def reset_password; end
27
+
28
+ def process_reset_password
29
+ if missing_reset_password_params?
30
+ handle_missing_params_error('Token and Password are required.')
31
+ return
32
+ end
33
+
34
+ handle_service_action(:reset_password) do
35
+ result = service.reset(params[:token], params[:password])
36
+ Rails.logger.info("Password Reset Successful: #{result.data}")
37
+ result
38
+ end
39
+ end
40
+
41
+ def reset_existing_password; end
42
+
43
+ def process_reset_existing_password
44
+ if missing_existing_password_params?
45
+ handle_missing_params_error('Email, old password, and new password are required.')
46
+ return
47
+ end
48
+
49
+ handle_service_action(:reset_existing_password) do
50
+ result = service.reset_existing(params[:email], params[:old_password], params[:new_password]) # Removed @organization_id
51
+ Rails.logger.info("Existing Password Reset Successful: #{result.data}")
52
+ result
53
+ end
54
+ end
55
+
56
+ def reset_with_session; end
57
+
58
+ def process_reset_with_session
59
+ if missing_reset_with_session_params?
60
+ handle_missing_params_error('Session token and new password are required.')
61
+ return
62
+ end
63
+
64
+ handle_service_action(:reset_with_session) do
65
+ result = service.reset_with_session(params[:session_token], params[:password]) # Removed @organization_id
66
+ Rails.logger.info("Session-based Password Reset Successful: #{result.data}")
67
+ result
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def missing_reset_start_params?
74
+ params[:email].blank?
75
+ end
76
+
77
+ def missing_reset_password_params?
78
+ params[:token].blank? || params[:password].blank?
79
+ end
80
+
81
+ def missing_existing_password_params?
82
+ params[:email].blank? || params[:old_password].blank? || params[:new_password].blank?
83
+ end
84
+
85
+ def missing_reset_with_session_params?
86
+ params[:session_token].blank? || params[:password].blank?
87
+ end
88
+
89
+ def service
90
+ PhcdevworksAccountsStytch::Authentication::B2c::PasswordService.new
91
+ end
92
+ end
93
+ end
94
+ end
@@ -5,6 +5,8 @@
5
5
  <%= csrf_meta_tags %>
6
6
  <%= csp_meta_tag %>
7
7
 
8
+ <%= yield :head %>
9
+
8
10
  <%= stylesheet_link_tag "phcdevworks_accounts_stytch/application", media: "all" %>
9
11
  </head>
10
12
  <body>
@@ -0,0 +1,12 @@
1
+ <h1>Invite User via Magic Link</h1>
2
+
3
+ <%= form_with url: phcdevworks_accounts_stytch.b2b_magic_links_process_invite_path(organization_slug: params[:organization_slug] || 'example-slug'), method: :post do |form| %>
4
+ <div class="field">
5
+ <%= form.label :email, "Invitee's Email" %><br>
6
+ <%= form.email_field :email, required: true %>
7
+ </div>
8
+
9
+ <div class="actions">
10
+ <%= form.submit "Send Invite" %>
11
+ </div>
12
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <h1>Login or Sign Up with Magic Link</h1>
2
+
3
+ <%= form_with url: phcdevworks_accounts_stytch.b2b_magic_links_process_login_or_signup_path(organization_slug: params[:organization_slug] || 'example-slug'), method: :post do |form| %>
4
+ <div class="field">
5
+ <%= form.label :email, "Your Email" %><br>
6
+ <%= form.email_field :email, required: true %>
7
+ </div>
8
+
9
+ <div class="actions">
10
+ <%= form.submit "Send Magic Link" %>
11
+ </div>
12
+ <% end %>