authentication-zero 2.8.4 → 2.9.2

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +6 -0
  5. data/authentication-zero-api.md +210 -0
  6. data/lib/authentication_zero/version.rb +1 -1
  7. data/lib/generators/authentication/authentication_generator.rb +52 -19
  8. data/lib/generators/authentication/templates/controllers/api/authentications/events_controller.rb.tt +5 -0
  9. data/lib/generators/authentication/templates/controllers/api/sessions_controller.rb.tt +1 -5
  10. data/lib/generators/authentication/templates/controllers/html/authentications/events_controller.rb.tt +5 -0
  11. data/lib/generators/authentication/templates/controllers/html/registrations_controller.rb.tt +1 -5
  12. data/lib/generators/authentication/templates/controllers/{omniauth_controller.rb.tt → html/sessions/omniauth_controller.rb.tt} +1 -5
  13. data/lib/generators/authentication/templates/controllers/html/sessions_controller.rb.tt +1 -5
  14. data/lib/generators/authentication/templates/erb/authentications/events/index.html.erb +33 -0
  15. data/lib/generators/authentication/templates/erb/identity/password_resets/edit.html.erb.tt +2 -2
  16. data/lib/generators/authentication/templates/erb/passwords/edit.html.erb.tt +3 -3
  17. data/lib/generators/authentication/templates/erb/registrations/new.html.erb.tt +2 -2
  18. data/lib/generators/authentication/templates/erb/sessions/sudos/new.html.erb.tt +1 -1
  19. data/lib/generators/authentication/templates/migrations/create_events_migration.rb.tt +12 -0
  20. data/lib/generators/authentication/templates/migrations/create_sessions_migration.rb.tt +2 -2
  21. data/lib/generators/authentication/templates/migrations/create_table_migration.rb.tt +1 -1
  22. data/lib/generators/authentication/templates/models/current.rb.tt +1 -0
  23. data/lib/generators/authentication/templates/models/event.rb.tt +8 -0
  24. data/lib/generators/authentication/templates/models/model.rb.tt +16 -4
  25. data/lib/generators/authentication/templates/models/session.rb.tt +15 -0
  26. data/lib/generators/authentication/templates/test_unit/controllers/api/identity/email_verifications_controller_test.rb.tt +1 -1
  27. data/lib/generators/authentication/templates/test_unit/controllers/api/identity/emails_controller_test.rb.tt +1 -1
  28. data/lib/generators/authentication/templates/test_unit/controllers/api/passwords_controller_test.rb.tt +1 -1
  29. data/lib/generators/authentication/templates/test_unit/controllers/api/sessions/sudos_controller_test.rb.tt +1 -1
  30. data/lib/generators/authentication/templates/test_unit/controllers/api/sessions_controller_test.rb.tt +3 -3
  31. data/lib/generators/authentication/templates/test_unit/controllers/html/identity/email_verifications_controller_test.rb.tt +1 -1
  32. data/lib/generators/authentication/templates/test_unit/controllers/html/identity/emails_controller_test.rb.tt +1 -1
  33. data/lib/generators/authentication/templates/test_unit/controllers/html/passwords_controller_test.rb.tt +1 -1
  34. data/lib/generators/authentication/templates/test_unit/controllers/html/registrations_controller_test.rb.tt +1 -1
  35. data/lib/generators/authentication/templates/test_unit/controllers/html/sessions/sudos_controller_test.rb.tt +1 -1
  36. data/lib/generators/authentication/templates/test_unit/controllers/html/sessions_controller_test.rb.tt +3 -3
  37. data/lib/generators/authentication/templates/test_unit/system/sessions_test.rb.tt +7 -0
  38. metadata +9 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bfa3da3a6167d405cd7869b9cb90f7c61eee5ef1d03475d079d72362a3bedf04
4
- data.tar.gz: 3775ac3bd3fb334134618c9cbc8b32c25a5a3940246a554592e74415383de72b
3
+ metadata.gz: 1eb8eb320cf8198e9afebf59fd2365b2683e3b52840d8d455876a11b208e243c
4
+ data.tar.gz: 3bc190759683e6e647a597bdb57e09e69243901a3390c401e09eac72ada96726
5
5
  SHA512:
6
- metadata.gz: 4b596006ad41a57e3c3e54d21393ae2ceae9b21b36a11666f0988b2d59bda944c499d599074ced369cedbdb5cde98fc831f97691a3274d3467596904a7f2022c
7
- data.tar.gz: 7096373b36cf3c9dead056e3d0584f4e44eb97b6efa353e0b3a7f9399620eb59aa3c298e5016aef38138508c9e01d4d1c1e99d4918730e6f9d3eda0b8e035a6a
6
+ metadata.gz: 0c2996c6cf6779c22442f7155025bb99a470f26a0dc23218aea96f413fd5ae85a1ffdebd796922b77a09f60a833c4a0bb72bc9e964f5c0695066cf4074c3f4e9
7
+ data.tar.gz: 8cbf3eb8525e08fc10a5c00a88e1a82d928a107c969e7b6f4e65789d0db1d79b3a19369331f914b5baf439230a6b0e246287f6875f1f4ab7d57c4f470e96c0a0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## Authentication Zero 2.9.0 (March 2, 2022) ##
2
+
3
+ * Implement trackable
4
+
1
5
  ## Authentication Zero 2.8.0 (March 2, 2022) ##
2
6
 
3
7
  * Organize controllers in identity and sessions namespaces
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- authentication-zero (2.8.4)
4
+ authentication-zero (2.9.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -20,7 +20,9 @@ The purpose of authentication zero is to generate a pre-built authentication sys
20
20
  - Send e-mail confirmation when your email has been changed
21
21
  - Send e-mail notification when someone has logged into your account
22
22
  - Manage multiple sessions & devices
23
+ - Activity log (--trackable)
23
24
  - Log out
25
+ - [API documentation](https://github.com/lazaronixon/authentication-zero/blob/master/authentication-zero-api.md)
24
26
 
25
27
  ## Security and best practices
26
28
 
@@ -73,6 +75,10 @@ Add these lines to your `app/views/home/index.html.erb`:
73
75
  <%= link_to "Devices & Sessions", sessions_path %>
74
76
  </div>
75
77
 
78
+ <div>
79
+ <%# link_to "Activity Log", authentications_events_path %>
80
+ </div>
81
+
76
82
  <br>
77
83
 
78
84
  <%= button_to "Log out", Current.session, method: :delete %>
@@ -0,0 +1,210 @@
1
+ # Authentication Zero API
2
+
3
+ This document describe the api endpoints available in authentication-zero.
4
+
5
+ ## Making a request
6
+
7
+ To make a sign in request for example, append sign_in to the base URL to form something like http://localhost:3000/sign_in, also notice you have to include the Content-Type header and the JSON data: In cURL, it looks like this:
8
+
9
+ ``` shell
10
+ curl -H "Authorization: Bearer $ACCESS_TOKEN" \
11
+ -H 'Content-Type: application/json' \
12
+ -H 'User-Agent: MyApp (yourname@example.com)' \
13
+ -d '{ "email": "lazaronixon@hotmail.com", "password": "secret", "password_confirmation": "secret" }' \
14
+ http://localhost:3000/sign_in
15
+ ```
16
+
17
+ ## API endpoints
18
+
19
+ - [Sign up](#sign-up)
20
+ - [Sign in](#sign-in)
21
+ - [Get your sessions](#get-your-sessions)
22
+ - [Get a session](#get-a-session)
23
+ - [Destroy a session](#destroy-a-session)
24
+ - [Execute sudo](#execute-sudo)
25
+ - [Update your password](#update-your-password)
26
+ - [Update your email](#update-your-email)
27
+ - [Send verification email](#send-verification-email)
28
+ - [Verify email](#verify-email)
29
+ - [Send password reset email](#send-password-reset-email)
30
+ - [Reset password](#reset-password)
31
+
32
+ ## Registrations
33
+
34
+ ### Sign up
35
+
36
+ * `POST /sign_up` creates a user on database.
37
+
38
+ ###### Example JSON Request
39
+
40
+ ``` json
41
+ {
42
+ "email": "lazaronixon@hotmail.com",
43
+ "password": "Secret1*2*3*4*5*6",
44
+ "password_confirmation": "Secret1*2*3*4*5*6"
45
+ }
46
+ ```
47
+
48
+ This endpoint will return `201 Created` with the current JSON representation of the user if the creation was a success.
49
+
50
+ ## Sessions
51
+
52
+ ### Sign in
53
+
54
+ * `POST /sign_in` creates a session on database.
55
+
56
+ ###### Example JSON Request
57
+
58
+ ``` json
59
+ {
60
+ "email": "lazaronixon@hotmail.com",
61
+ "password": "Secret1*2*3*4*5*6"
62
+ }
63
+ ```
64
+
65
+ This endpoint will return `201 Created` with the current JSON representation of the session if the creation was a success, also you will receive a `X-Session-Token` that you will use as your authorization token.
66
+
67
+
68
+ ### Get your sessions
69
+
70
+ * `GET /sessions` will return a list of sessions.
71
+
72
+ ###### Example JSON Response
73
+
74
+ ``` json
75
+ [
76
+ {
77
+ "id": 2,
78
+ "user_id": 1,
79
+ "user_agent": "insomnia/2022.1.0",
80
+ "ip_address": "127.0.0.1",
81
+ "sudo_at": "2022-03-04T17:20:33.632Z",
82
+ "created_at": "2022-03-04T17:20:33.632Z",
83
+ "updated_at": "2022-03-04T17:20:33.632Z"
84
+ },
85
+ {
86
+ "id": 1,
87
+ "user_id": 1,
88
+ "user_agent": "insomnia/2022.1.0",
89
+ "ip_address": "127.0.0.1",
90
+ "sudo_at": "2022-03-04T17:14:03.386Z",
91
+ "created_at": "2022-03-04T17:14:03.386Z",
92
+ "updated_at": "2022-03-04T17:14:03.386Z"
93
+ }
94
+ ]
95
+ ```
96
+
97
+ ### Get a session
98
+
99
+ * `GET /sessions/1` will return the session with an ID of 1.
100
+
101
+ ###### Example JSON Response
102
+
103
+ ``` json
104
+ {
105
+ "id": 1,
106
+ "user_id": 1,
107
+ "user_agent": "insomnia/2022.1.0",
108
+ "ip_address": "127.0.0.1",
109
+ "sudo_at": "2022-03-04T17:14:03.386Z",
110
+ "created_at": "2022-03-04T17:14:03.386Z",
111
+ "updated_at": "2022-03-04T17:14:03.386Z"
112
+ }
113
+ ```
114
+
115
+ ### Destroy a session
116
+
117
+ * `DELETE /sessions/1` will destroy the session with an ID of 1.
118
+
119
+ Returns `204 No Content` if successful.
120
+
121
+
122
+ ### Execute sudo
123
+
124
+ * `POST /sessions/sudo` will grant temporary access to sensitive information.
125
+
126
+ ###### Example JSON Request
127
+
128
+ ``` json
129
+ {
130
+ "password": "Secret1*2*3*4*5*6",
131
+ }
132
+ ```
133
+
134
+ Returns `204 No Content` if successful.
135
+
136
+ ## Password
137
+
138
+ ### Update your password
139
+
140
+ * `PUT /password` allows changing your password.
141
+
142
+ ###### Example JSON Request
143
+
144
+ ``` json
145
+ {
146
+ "current_password": "Secret1*2*3*4*5*6",
147
+ "password": "NewPassword12$34$56$7",
148
+ "password_confirmation": "NewPassword12$34$56$7"
149
+ }
150
+ ```
151
+
152
+ This endpoint will return 200 OK with the current JSON representation of the user if the update was a success.
153
+
154
+ ## Email
155
+
156
+ ### Update your email
157
+
158
+ * `PUT /identity/email` allows changing your email. **(requires sudo)**.
159
+
160
+ ###### Example JSON Request
161
+
162
+ ``` json
163
+ {
164
+ "email": "new_email@hey.com"
165
+ }
166
+ ```
167
+
168
+ This endpoint will return 200 OK with the current JSON representation of the user if the update was a success.
169
+
170
+ ## Email verification
171
+
172
+ ### Send verification email
173
+
174
+ * `POST /identity/email_verification` sends an email verification with the instructions and link to proceed with the verification.
175
+
176
+ Returns `204 No Content` if successful.
177
+
178
+ ### Verify email
179
+
180
+ * `GET /identity/email_verification` verify your email using a temporary token.
181
+
182
+ **Required parameters:** `email` and `token`.
183
+
184
+ Example: `/identity/email_verification?email=lazaronixon@hotmail.com&token=eyJfcmFpbHMiOnsibWVzc2FnZSI6Ik1nPT0iLCJleHAiOm51bGwsInB1ciI6InNlc3Npb24ifX0=--1a277b4a5576c6e371144a22476979a18d3e45fb8515a79e815cd4b95eb5fb6b`
185
+
186
+ Returns `204 No Content` if successful.
187
+
188
+ ## Password reset
189
+
190
+ ### Send password reset email
191
+
192
+ * `POST /identity/password_reset` sends a password reset email with the instructions and link to proceed reset.
193
+
194
+ Returns `204 No Content` if successful.
195
+
196
+ ### Reset password
197
+
198
+ * `PUT /identity/password_reset` allows changing your password through a email token.
199
+
200
+ ##### Example JSON Request
201
+
202
+ ``` json
203
+ {
204
+ "password": "NewPassword12$34$56$7",
205
+ "password_confirmation": "NewPassword12$34$56$7",
206
+ "token": "eyJfcmFpbHMiOnsibWVzc2FnZSI6Ik1nPT0iLCJleHAiOm51bGwsInB1ciI6InNlc3Npb24ifX0=--1a277b4a5576c6e371144a22476979a18d3e45fb8515a79e815cd4b95eb5fb6b",
207
+ }
208
+ ```
209
+
210
+ This endpoint will return 200 OK with the current JSON representation of the user if the update was a success.
@@ -1,3 +1,3 @@
1
1
  module AuthenticationZero
2
- VERSION = "2.8.4"
2
+ VERSION = "2.9.2"
3
3
  end
@@ -8,6 +8,7 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
8
8
  class_option :lockable, type: :boolean, desc: "Add password reset locking"
9
9
  class_option :ratelimit, type: :boolean, desc: "Add request rate limiting"
10
10
  class_option :omniauthable, type: :boolean, desc: "Add social login support"
11
+ class_option :trackable, type: :boolean, desc: "Add activity log support"
11
12
 
12
13
  source_root File.expand_path("templates", __dir__)
13
14
 
@@ -47,6 +48,7 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
47
48
  def create_migrations
48
49
  migration_template "migrations/create_table_migration.rb", "#{db_migrate_path}/create_#{table_name}.rb"
49
50
  migration_template "migrations/create_sessions_migration.rb", "#{db_migrate_path}/create_sessions.rb"
51
+ migration_template "migrations/create_events_migration.rb", "#{db_migrate_path}/create_events.rb" if options.trackable?
50
52
  end
51
53
 
52
54
  def create_models
@@ -54,6 +56,7 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
54
56
  template "models/session.rb", "app/models/session.rb"
55
57
  template "models/current.rb", "app/models/current.rb"
56
58
  template "models/locking.rb", "app/models/locking.rb" if options.lockable?
59
+ template "models/event.rb", "app/models/event.rb" if options.trackable?
57
60
  end
58
61
 
59
62
  def create_fixture_file
@@ -64,39 +67,53 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
64
67
  api_code = <<~CODE
65
68
  include ActionController::HttpAuthentication::Token::ControllerMethods
66
69
 
70
+ before_action :set_current_request_details
67
71
  before_action :authenticate
68
72
 
69
- def authenticate
70
- if session = authenticate_with_http_token { |token, _| Session.find_signed(token) }
71
- Current.session = session
72
- else
73
- request_http_token_authentication
74
- end
75
- end
76
-
77
73
  def require_sudo
78
74
  if Current.session.sudo_at < 30.minutes.ago
79
75
  render json: { error: "Enter your password to continue" }, status: :forbidden
80
76
  end
81
77
  end
78
+
79
+ private
80
+ def authenticate
81
+ if session = authenticate_with_http_token { |token, _| Session.find_signed(token) }
82
+ Current.session = session
83
+ else
84
+ request_http_token_authentication
85
+ end
86
+ end
87
+
88
+ def set_current_request_details
89
+ Current.user_agent = request.user_agent
90
+ Current.ip_address = request.ip
91
+ end
82
92
  CODE
83
93
 
84
94
  html_code = <<~CODE
95
+ before_action :set_current_request_details
85
96
  before_action :authenticate
86
97
 
87
- def authenticate
88
- if session = Session.find_by_id(cookies.signed[:session_token])
89
- Current.session = session
90
- else
91
- redirect_to sign_in_path
92
- end
93
- end
94
-
95
98
  def require_sudo
96
99
  if Current.session.sudo_at < 30.minutes.ago
97
100
  redirect_to new_sessions_sudo_path(proceed_to_url: request.url)
98
101
  end
99
102
  end
103
+
104
+ private
105
+ def authenticate
106
+ if session = Session.find_by_id(cookies.signed[:session_token])
107
+ Current.session = session
108
+ else
109
+ redirect_to sign_in_path
110
+ end
111
+ end
112
+
113
+ def set_current_request_details
114
+ Current.user_agent = request.user_agent
115
+ Current.ip_address = request.ip
116
+ end
100
117
  CODE
101
118
 
102
119
  inject_code = options.api? ? api_code : html_code
@@ -104,8 +121,13 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
104
121
  end
105
122
 
106
123
  def create_controllers
107
- directory "controllers/#{format_folder}", "app/controllers"
108
- template "controllers/omniauth_controller.rb", "app/controllers/sessions/omniauth_controller.rb" if omniauthable?
124
+ directory "controllers/#{format_folder}/identity", "app/controllers/identity"
125
+ template "controllers/#{format_folder}/passwords_controller.rb", "app/controllers/passwords_controller.rb"
126
+ template "controllers/#{format_folder}/registrations_controller.rb", "app/controllers/registrations_controller.rb"
127
+ template "controllers/#{format_folder}/sessions_controller.rb", "app/controllers/sessions_controller.rb"
128
+ template "controllers/#{format_folder}/sessions/sudos_controller.rb", "app/controllers/sessions/sudos_controller.rb"
129
+ template "controllers/#{format_folder}/sessions/omniauth_controller.rb", "app/controllers/sessions/omniauth_controller.rb" if omniauthable?
130
+ template "controllers/#{format_folder}/authentications/events_controller.rb", "app/controllers/authentications/events_controller.rb" if options.trackable?
109
131
  end
110
132
 
111
133
  def create_views
@@ -113,7 +135,14 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
113
135
  directory "erb/identity_mailer", "app/views/identity_mailer"
114
136
  directory "erb/session_mailer", "app/views/session_mailer"
115
137
  else
116
- directory "erb", "app/views"
138
+ directory "erb/identity_mailer", "app/views/identity_mailer"
139
+ directory "erb/session_mailer", "app/views/session_mailer"
140
+
141
+ directory "erb/identity", "app/views/identity"
142
+ directory "erb/passwords", "app/views/passwords"
143
+ directory "erb/registrations", "app/views/registrations"
144
+ directory "erb/sessions", "app/views/sessions"
145
+ directory "erb/authentications/events", "app/views/authentications/events" if options.trackable?
117
146
  end
118
147
  end
119
148
 
@@ -128,6 +157,10 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
128
157
  route "get '/auth/failure', to: 'sessions/omniauth#failure'"
129
158
  end
130
159
 
160
+ if options.trackable?
161
+ route "resources :events, only: :index", namespace: :authentications
162
+ end
163
+
131
164
  route "resource :password_reset, only: [:new, :edit, :create, :update]", namespace: :identity
132
165
  route "resource :email_verification, only: [:edit, :create]", namespace: :identity
133
166
  route "resource :email, only: [:edit, :update]", namespace: :identity
@@ -0,0 +1,5 @@
1
+ class Authentications::EventsController < ApplicationController
2
+ def index
3
+ render json: Current.<%= singular_table_name %>.events.order(created_at: :desc)
4
+ end
5
+ end
@@ -15,7 +15,7 @@ class SessionsController < ApplicationController
15
15
  <%= singular_table_name %> = <%= class_name %>.find_by(email: params[:email])
16
16
 
17
17
  if <%= singular_table_name %> && <%= singular_table_name %>.authenticate(params[:password])
18
- @session = <%= singular_table_name %>.sessions.create!(session_params)
18
+ @session = <%= singular_table_name %>.sessions.create!
19
19
  response.set_header("X-Session-Token", @session.signed_id)
20
20
 
21
21
  render json: @session, status: :created
@@ -32,8 +32,4 @@ class SessionsController < ApplicationController
32
32
  def set_session
33
33
  @session = Current.<%= singular_table_name %>.sessions.find(params[:id])
34
34
  end
35
-
36
- def session_params
37
- { user_agent: request.user_agent, ip_address: request.remote_ip, sudo_at: Time.current }
38
- end
39
35
  end
@@ -0,0 +1,5 @@
1
+ class Authentications::EventsController < ApplicationController
2
+ def index
3
+ @events = Current.<%= singular_table_name %>.events.order(created_at: :desc)
4
+ end
5
+ end
@@ -9,7 +9,7 @@ class RegistrationsController < ApplicationController
9
9
  @<%= singular_table_name %> = <%= class_name %>.new(<%= "#{singular_table_name}_params" %>)
10
10
 
11
11
  if @<%= singular_table_name %>.save
12
- session = @<%= singular_table_name %>.sessions.create!(session_params)
12
+ session = @<%= singular_table_name %>.sessions.create!
13
13
  cookies.signed.permanent[:session_token] = { value: session.id, httponly: true }
14
14
 
15
15
  redirect_to root_path, notice: "Welcome! You have signed up successfully"
@@ -22,8 +22,4 @@ class RegistrationsController < ApplicationController
22
22
  def <%= "#{singular_table_name}_params" %>
23
23
  params.permit(:email, :password, :password_confirmation)
24
24
  end
25
-
26
- def session_params
27
- { user_agent: request.user_agent, ip_address: request.remote_ip, sudo_at: Time.current }
28
- end
29
25
  end
@@ -6,7 +6,7 @@ class Sessions::OmniauthController < ApplicationController
6
6
  @<%= singular_table_name %> = <%= class_name %>.where(omniauth_params).first_or_initialize(<%= "#{singular_table_name}_params" %>)
7
7
 
8
8
  if @<%= singular_table_name %>.save
9
- session = @<%= singular_table_name %>.sessions.create!(session_params)
9
+ session = @<%= singular_table_name %>.sessions.create!
10
10
  cookies.signed.permanent[:session_token] = { value: session.id, httponly: true }
11
11
 
12
12
  redirect_to root_path, notice: "Signed in successfully"
@@ -28,10 +28,6 @@ class Sessions::OmniauthController < ApplicationController
28
28
  { email: omniauth.info.email, password: SecureRandom::base58, verified: true }
29
29
  end
30
30
 
31
- def session_params
32
- { user_agent: request.user_agent, ip_address: request.remote_ip, sudo_at: Time.current }
33
- end
34
-
35
31
  def omniauth
36
32
  request.env["omniauth.auth"]
37
33
  end
@@ -15,7 +15,7 @@ class SessionsController < ApplicationController
15
15
  <%= singular_table_name %> = <%= class_name %>.find_by(email: params[:email])
16
16
 
17
17
  if <%= singular_table_name %> && <%= singular_table_name %>.authenticate(params[:password])
18
- @session = <%= singular_table_name %>.sessions.create!(session_params)
18
+ @session = <%= singular_table_name %>.sessions.create!
19
19
  cookies.signed.permanent[:session_token] = { value: @session.id, httponly: true }
20
20
 
21
21
  redirect_to root_path, notice: "Signed in successfully"
@@ -32,8 +32,4 @@ class SessionsController < ApplicationController
32
32
  def set_session
33
33
  @session = Current.<%= singular_table_name %>.sessions.find(params[:id])
34
34
  end
35
-
36
- def session_params
37
- { user_agent: request.user_agent, ip_address: request.remote_ip, sudo_at: Time.current }
38
- end
39
35
  end
@@ -0,0 +1,33 @@
1
+ <h1>Activity Log</h1>
2
+
3
+ <div id="sessions">
4
+ <% @events.each do |event| %>
5
+ <div id="<%= dom_id event %>">
6
+ <p>
7
+ <strong>User Agent:</strong>
8
+ <%= event.user_agent %>
9
+ </p>
10
+
11
+ <p>
12
+ <strong>Action:</strong>
13
+ <%= event.action %>
14
+ </p>
15
+
16
+ <p>
17
+ <strong>Ip Address:</strong>
18
+ <%= event.ip_address %>
19
+ </p>
20
+
21
+ <p>
22
+ <strong>Created at:</strong>
23
+ <%= event.created_at %>
24
+ </p>
25
+ </div>
26
+ <% end %>
27
+ </div>
28
+
29
+ <br>
30
+
31
+ <div>
32
+ <%= link_to "Back", root_path %>
33
+ </div>
@@ -17,13 +17,13 @@
17
17
 
18
18
  <div>
19
19
  <%%= form.label :password, "New password", style: "display: block" %>
20
- <%%= form.password_field :password, autofocus: true, autocomplete: "new-password" %>
20
+ <%%= form.password_field :password, required: true, autofocus: true, autocomplete: "new-password" %>
21
21
  <div>12 characters minimum.</div>
22
22
  </div>
23
23
 
24
24
  <div>
25
25
  <%%= form.label :password_confirmation, "Confirm new password", style: "display: block" %>
26
- <%%= form.password_field :password_confirmation, autocomplete: "new-password" %>
26
+ <%%= form.password_field :password_confirmation, required: true, autocomplete: "new-password" %>
27
27
  </div>
28
28
 
29
29
  <div>
@@ -17,18 +17,18 @@
17
17
 
18
18
  <div>
19
19
  <%%= label_tag :current_password, nil, style: "display: block" %>
20
- <%%= password_field_tag :current_password, nil, autofocus: true, autocomplete: "current-password" %>
20
+ <%%= password_field_tag :current_password, nil, required: true, autofocus: true, autocomplete: "current-password" %>
21
21
  </div>
22
22
 
23
23
  <div>
24
24
  <%%= form.label :password, "New password", style: "display: block" %>
25
- <%%= form.password_field :password, autocomplete: "new-password" %>
25
+ <%%= form.password_field :password, required: true, autocomplete: "new-password" %>
26
26
  <div>12 characters minimum.</div>
27
27
  </div>
28
28
 
29
29
  <div>
30
30
  <%%= form.label :password_confirmation, "Confirm new password", style: "display: block" %>
31
- <%%= form.password_field :password_confirmation, autocomplete: "new-password" %>
31
+ <%%= form.password_field :password_confirmation, required: true, autocomplete: "new-password" %>
32
32
  </div>
33
33
 
34
34
  <div>
@@ -20,13 +20,13 @@
20
20
 
21
21
  <div>
22
22
  <%%= form.label :password, style: "display: block" %>
23
- <%%= form.password_field :password, autocomplete: "new-password" %>
23
+ <%%= form.password_field :password, required: true, autocomplete: "new-password" %>
24
24
  <div>12 characters minimum.</div>
25
25
  </div>
26
26
 
27
27
  <div>
28
28
  <%%= form.label :password_confirmation, style: "display: block" %>
29
- <%%= form.password_field :password_confirmation, autocomplete: "new-password" %>
29
+ <%%= form.password_field :password_confirmation, required: true, autocomplete: "new-password" %>
30
30
  </div>
31
31
 
32
32
  <div>
@@ -7,7 +7,7 @@
7
7
  <%%= hidden_field_tag :proceed_to_url, params[:proceed_to_url] %>
8
8
 
9
9
  <div>
10
- <%%= password_field_tag :password, nil, autofocus: true, autocomplete: "current-password" %>
10
+ <%%= password_field_tag :password, nil, required: true, autofocus: true, autocomplete: "current-password" %>
11
11
  </div>
12
12
 
13
13
  <div>
@@ -0,0 +1,12 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
+ def change
3
+ create_table :events do |t|
4
+ t.references :<%= singular_table_name %>, null: false, foreign_key: true
5
+ t.string :action, null: false
6
+ t.string :user_agent
7
+ t.string :ip_address
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -3,8 +3,8 @@ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Mi
3
3
  create_table :sessions do |t|
4
4
  t.references :<%= singular_table_name %>, null: false, foreign_key: true
5
5
 
6
- t.string :user_agent, null: false
7
- t.string :ip_address, null: false
6
+ t.string :user_agent
7
+ t.string :ip_address
8
8
 
9
9
  t.datetime :sudo_at, null: false
10
10
 
@@ -6,7 +6,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Mi
6
6
 
7
7
  t.boolean :verified, null: false, default: false
8
8
  <% if omniauthable? %>
9
- t.string :provide
9
+ t.string :provider
10
10
  t.string :uid
11
11
  <% end -%>
12
12
 
@@ -1,5 +1,6 @@
1
1
  class Current < ActiveSupport::CurrentAttributes
2
2
  attribute :session, :<%= singular_table_name %>
3
+ attribute :user_agent, :ip_address
3
4
 
4
5
  def session=(session)
5
6
  super; self.<%= singular_table_name %> = session.<%= singular_table_name %>
@@ -0,0 +1,8 @@
1
+ class Event < ApplicationRecord
2
+ belongs_to :<%= singular_table_name %>
3
+
4
+ before_create do
5
+ self.user_agent = Current.user_agent
6
+ self.ip_address = Current.ip_address
7
+ end
8
+ end
@@ -2,6 +2,9 @@ class <%= class_name %> < ApplicationRecord
2
2
  has_secure_password
3
3
 
4
4
  has_many :sessions, dependent: :destroy
5
+ <% if options.trackable? -%>
6
+ has_many :events, dependent: :destroy
7
+ <% end -%>
5
8
 
6
9
  validates :email, presence: true, uniqueness: true
7
10
  validates_format_of :email, with: /\A[^@\s]+@[^@\s]+\z/
@@ -24,11 +27,20 @@ class <%= class_name %> < ApplicationRecord
24
27
  sessions.where.not(id: Current.session).destroy_all
25
28
  end
26
29
 
27
- after_create_commit do
28
- IdentityMailer.with(<%= singular_table_name %>: self).email_verify_confirmation.deliver_later
30
+ after_save_commit if: :email_previously_changed? do
31
+ IdentityMailer.with(user: self).email_verify_confirmation.deliver_later
32
+ end
33
+ <% if options.trackable? %>
34
+ after_save_commit if: :email_previously_changed? do
35
+ events.create! action: "email_verification_requested"
36
+ end
37
+
38
+ after_update if: :password_digest_previously_changed? do
39
+ events.create! action: "password_changed"
29
40
  end
30
41
 
31
- after_update_commit if: :email_previously_changed? do
32
- IdentityMailer.with(<%= singular_table_name %>: self).email_verify_confirmation.deliver_later
42
+ after_update if: :verified_previously_changed? do
43
+ events.create! action: "email_verified" if verified?
33
44
  end
45
+ <% end -%>
34
46
  end
@@ -1,7 +1,22 @@
1
1
  class Session < ApplicationRecord
2
2
  belongs_to :<%= singular_table_name %>
3
3
 
4
+ before_create do
5
+ self.user_agent = Current.user_agent
6
+ self.ip_address = Current.ip_address
7
+ self.sudo_at = Time.current
8
+ end
9
+
4
10
  after_create_commit do
5
11
  SessionMailer.with(session: self).signed_in_notification.deliver_later
6
12
  end
13
+ <% if options.trackable? %>
14
+ after_create do
15
+ <%= singular_table_name %>.events.create! action: "signed_in"
16
+ end
17
+
18
+ after_destroy do
19
+ <%= singular_table_name %>.events.create! action: "signed_out"
20
+ end
21
+ <% end -%>
7
22
  end
@@ -39,6 +39,6 @@ class Identity::EmailVerificationsControllerTest < ActionDispatch::IntegrationTe
39
39
  end
40
40
 
41
41
  def sign_in_as(<%= singular_table_name %>)
42
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "App iOS" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
42
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
43
43
  end
44
44
  end
@@ -20,6 +20,6 @@ class Identity::EmailsControllerTest < ActionDispatch::IntegrationTest
20
20
  end
21
21
 
22
22
  def sign_in_as(<%= singular_table_name %>)
23
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "App iOS" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
23
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
24
24
  end
25
25
  end
@@ -18,6 +18,6 @@ class PasswordsControllerTest < ActionDispatch::IntegrationTest
18
18
  end
19
19
 
20
20
  def sign_in_as(<%= singular_table_name %>)
21
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "App iOS" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
21
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
22
22
  end
23
23
  end
@@ -19,6 +19,6 @@ class Sessions::SudosControllerTest < ActionDispatch::IntegrationTest
19
19
  end
20
20
 
21
21
  def sign_in_as(<%= singular_table_name %>)
22
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "App iOS" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
22
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
23
23
  end
24
24
  end
@@ -16,14 +16,14 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
16
16
  end
17
17
 
18
18
  test "should sign in" do
19
- post sign_in_url, params: { email: @<%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "App iOS" }
19
+ post sign_in_url, params: { email: @<%= singular_table_name %>.email, password: "Secret1*3*5*" }
20
20
 
21
21
  assert_enqueued_email_with SessionMailer, :signed_in_notification, args: { session: @<%= singular_table_name %>.sessions.last }
22
22
  assert_response :created
23
23
  end
24
24
 
25
25
  test "should not sign in with wrong credentials" do
26
- post sign_in_url, params: { email: @<%= singular_table_name %>.email, password: "SecretWrong1*3" }, headers: { "User-Agent" => "App iOS" }
26
+ post sign_in_url, params: { email: @<%= singular_table_name %>.email, password: "SecretWrong1*3" }
27
27
  assert_response :unauthorized
28
28
  end
29
29
 
@@ -33,6 +33,6 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
33
33
  end
34
34
 
35
35
  def sign_in_as(<%= singular_table_name %>)
36
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "App iOS" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
36
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
37
37
  end
38
38
  end
@@ -39,6 +39,6 @@ class Identity::EmailVerificationsControllerTest < ActionDispatch::IntegrationTe
39
39
  end
40
40
 
41
41
  def sign_in_as(<%= singular_table_name %>)
42
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "Firefox" }); <%= singular_table_name %>
42
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); <%= singular_table_name %>
43
43
  end
44
44
  end
@@ -30,6 +30,6 @@ class Identity::EmailsControllerTest < ActionDispatch::IntegrationTest
30
30
  end
31
31
 
32
32
  def sign_in_as(<%= singular_table_name %>)
33
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "Firefox" }); <%= singular_table_name %>
33
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); <%= singular_table_name %>
34
34
  end
35
35
  end
@@ -23,6 +23,6 @@ class PasswordsControllerTest < ActionDispatch::IntegrationTest
23
23
  end
24
24
 
25
25
  def sign_in_as(<%= singular_table_name %>)
26
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "Firefox" }); <%= singular_table_name %>
26
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); <%= singular_table_name %>
27
27
  end
28
28
  end
@@ -8,7 +8,7 @@ class RegistrationsControllerTest < ActionDispatch::IntegrationTest
8
8
 
9
9
  test "should sign up" do
10
10
  assert_difference("<%= class_name %>.count") do
11
- post sign_up_url, params: { email: "lazaronixon@hey.com", password: "Secret1*3*5*", password_confirmation: "Secret1*3*5*" }, headers: { "User-Agent" => "Firefox" }
11
+ post sign_up_url, params: { email: "lazaronixon@hey.com", password: "Secret1*3*5*", password_confirmation: "Secret1*3*5*" }
12
12
  end
13
13
 
14
14
  assert_redirected_to root_url
@@ -21,6 +21,6 @@ class Sessions::SudosControllerTest < ActionDispatch::IntegrationTest
21
21
  end
22
22
 
23
23
  def sign_in_as(<%= singular_table_name %>)
24
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "Firefox" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
24
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
25
25
  end
26
26
  end
@@ -18,7 +18,7 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
18
18
  end
19
19
 
20
20
  test "should sign in" do
21
- post sign_in_url, params: { email: @<%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "Firefox" }
21
+ post sign_in_url, params: { email: @<%= singular_table_name %>.email, password: "Secret1*3*5*" }
22
22
  assert_enqueued_email_with SessionMailer, :signed_in_notification, args: { session: @<%= singular_table_name %>.sessions.last }
23
23
 
24
24
  assert_redirected_to root_url
@@ -28,7 +28,7 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
28
28
  end
29
29
 
30
30
  test "should not sign in with wrong credentials" do
31
- post sign_in_url, params: { email: @<%= singular_table_name %>.email, password: "SecretWrong1*3" }, headers: { "User-Agent" => "Firefox" }
31
+ post sign_in_url, params: { email: @<%= singular_table_name %>.email, password: "SecretWrong1*3" }
32
32
  assert_redirected_to sign_in_url(email_hint: @<%= singular_table_name %>.email)
33
33
  assert_equal "That email or password is incorrect", flash[:alert]
34
34
 
@@ -47,6 +47,6 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
47
47
  end
48
48
 
49
49
  def sign_in_as(<%= singular_table_name %>)
50
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "Firefox" }); <%= singular_table_name %>
50
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); <%= singular_table_name %>
51
51
  end
52
52
  end
@@ -21,6 +21,13 @@ class SessionsTest < ApplicationSystemTestCase
21
21
  assert_text "Signed in successfully"
22
22
  end
23
23
 
24
+ test "signing out" do
25
+ sign_in_as @<%= singular_table_name %>
26
+
27
+ click_on "Log out"
28
+ assert_text "That session has been logged out"
29
+ end
30
+
24
31
  def sign_in_as(<%= singular_table_name %>)
25
32
  visit sign_in_url
26
33
  fill_in :email, with: <%= singular_table_name %>.email
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: authentication-zero
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.4
4
+ version: 2.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nixon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-04 00:00:00.000000000 Z
11
+ date: 2022-03-08 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -26,6 +26,7 @@ files:
26
26
  - LICENSE.txt
27
27
  - README.md
28
28
  - Rakefile
29
+ - authentication-zero-api.md
29
30
  - authentication-zero.gemspec
30
31
  - lib/authentication-zero.rb
31
32
  - lib/authentication_zero.rb
@@ -34,6 +35,7 @@ files:
34
35
  - lib/generators/authentication/authentication_generator.rb
35
36
  - lib/generators/authentication/templates/config/initializers/omniauth.rb
36
37
  - lib/generators/authentication/templates/config/redis/shared.yml
38
+ - lib/generators/authentication/templates/controllers/api/authentications/events_controller.rb.tt
37
39
  - lib/generators/authentication/templates/controllers/api/identity/email_verifications_controller.rb.tt
38
40
  - lib/generators/authentication/templates/controllers/api/identity/emails_controller.rb.tt
39
41
  - lib/generators/authentication/templates/controllers/api/identity/password_resets_controller.rb.tt
@@ -41,14 +43,16 @@ files:
41
43
  - lib/generators/authentication/templates/controllers/api/registrations_controller.rb.tt
42
44
  - lib/generators/authentication/templates/controllers/api/sessions/sudos_controller.rb.tt
43
45
  - lib/generators/authentication/templates/controllers/api/sessions_controller.rb.tt
46
+ - lib/generators/authentication/templates/controllers/html/authentications/events_controller.rb.tt
44
47
  - lib/generators/authentication/templates/controllers/html/identity/email_verifications_controller.rb.tt
45
48
  - lib/generators/authentication/templates/controllers/html/identity/emails_controller.rb.tt
46
49
  - lib/generators/authentication/templates/controllers/html/identity/password_resets_controller.rb.tt
47
50
  - lib/generators/authentication/templates/controllers/html/passwords_controller.rb.tt
48
51
  - lib/generators/authentication/templates/controllers/html/registrations_controller.rb.tt
52
+ - lib/generators/authentication/templates/controllers/html/sessions/omniauth_controller.rb.tt
49
53
  - lib/generators/authentication/templates/controllers/html/sessions/sudos_controller.rb.tt
50
54
  - lib/generators/authentication/templates/controllers/html/sessions_controller.rb.tt
51
- - lib/generators/authentication/templates/controllers/omniauth_controller.rb.tt
55
+ - lib/generators/authentication/templates/erb/authentications/events/index.html.erb
52
56
  - lib/generators/authentication/templates/erb/identity/emails/edit.html.erb.tt
53
57
  - lib/generators/authentication/templates/erb/identity/password_resets/edit.html.erb.tt
54
58
  - lib/generators/authentication/templates/erb/identity/password_resets/new.html.erb.tt
@@ -65,9 +69,11 @@ files:
65
69
  - lib/generators/authentication/templates/erb/sessions/sudos/new.html.erb.tt
66
70
  - lib/generators/authentication/templates/mailers/identity_mailer.rb.tt
67
71
  - lib/generators/authentication/templates/mailers/session_mailer.rb.tt
72
+ - lib/generators/authentication/templates/migrations/create_events_migration.rb.tt
68
73
  - lib/generators/authentication/templates/migrations/create_sessions_migration.rb.tt
69
74
  - lib/generators/authentication/templates/migrations/create_table_migration.rb.tt
70
75
  - lib/generators/authentication/templates/models/current.rb.tt
76
+ - lib/generators/authentication/templates/models/event.rb.tt
71
77
  - lib/generators/authentication/templates/models/locking.rb.tt
72
78
  - lib/generators/authentication/templates/models/model.rb.tt
73
79
  - lib/generators/authentication/templates/models/session.rb.tt