claude-code-templates 1.14.13 → 1.14.15
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.
- package/README.md +7 -7
- package/package.json +1 -2
- package/src/health-check.js +310 -0
- package/src/index.js +1 -1
- package/templates/common/.claude/commands/git-workflow.md +0 -239
- package/templates/common/.claude/commands/project-setup.md +0 -316
- package/templates/common/.mcp.json +0 -41
- package/templates/common/CLAUDE.md +0 -109
- package/templates/common/README.md +0 -96
- package/templates/go/.mcp.json +0 -78
- package/templates/go/README.md +0 -25
- package/templates/javascript-typescript/.claude/commands/api-endpoint.md +0 -51
- package/templates/javascript-typescript/.claude/commands/debug.md +0 -52
- package/templates/javascript-typescript/.claude/commands/lint.md +0 -48
- package/templates/javascript-typescript/.claude/commands/npm-scripts.md +0 -48
- package/templates/javascript-typescript/.claude/commands/refactor.md +0 -55
- package/templates/javascript-typescript/.claude/commands/test.md +0 -61
- package/templates/javascript-typescript/.claude/commands/typescript-migrate.md +0 -51
- package/templates/javascript-typescript/.claude/settings.json +0 -142
- package/templates/javascript-typescript/.mcp.json +0 -80
- package/templates/javascript-typescript/CLAUDE.md +0 -185
- package/templates/javascript-typescript/README.md +0 -259
- package/templates/javascript-typescript/examples/angular-app/.claude/commands/components.md +0 -63
- package/templates/javascript-typescript/examples/angular-app/.claude/commands/services.md +0 -62
- package/templates/javascript-typescript/examples/node-api/.claude/commands/api-endpoint.md +0 -46
- package/templates/javascript-typescript/examples/node-api/.claude/commands/database.md +0 -56
- package/templates/javascript-typescript/examples/node-api/.claude/commands/middleware.md +0 -61
- package/templates/javascript-typescript/examples/node-api/.claude/commands/route.md +0 -57
- package/templates/javascript-typescript/examples/node-api/CLAUDE.md +0 -102
- package/templates/javascript-typescript/examples/react-app/.claude/commands/component.md +0 -29
- package/templates/javascript-typescript/examples/react-app/.claude/commands/hooks.md +0 -44
- package/templates/javascript-typescript/examples/react-app/.claude/commands/state-management.md +0 -45
- package/templates/javascript-typescript/examples/react-app/CLAUDE.md +0 -81
- package/templates/javascript-typescript/examples/react-app/agents/react-performance-optimization.md +0 -530
- package/templates/javascript-typescript/examples/react-app/agents/react-state-management.md +0 -295
- package/templates/javascript-typescript/examples/vue-app/.claude/commands/components.md +0 -46
- package/templates/javascript-typescript/examples/vue-app/.claude/commands/composables.md +0 -51
- package/templates/python/.claude/commands/lint.md +0 -111
- package/templates/python/.claude/commands/test.md +0 -73
- package/templates/python/.claude/settings.json +0 -153
- package/templates/python/.mcp.json +0 -78
- package/templates/python/CLAUDE.md +0 -276
- package/templates/python/examples/django-app/.claude/commands/admin.md +0 -264
- package/templates/python/examples/django-app/.claude/commands/django-model.md +0 -124
- package/templates/python/examples/django-app/.claude/commands/views.md +0 -222
- package/templates/python/examples/django-app/CLAUDE.md +0 -313
- package/templates/python/examples/fastapi-app/.claude/commands/api-endpoints.md +0 -513
- package/templates/python/examples/fastapi-app/.claude/commands/auth.md +0 -775
- package/templates/python/examples/fastapi-app/.claude/commands/database.md +0 -657
- package/templates/python/examples/fastapi-app/.claude/commands/deployment.md +0 -160
- package/templates/python/examples/fastapi-app/.claude/commands/testing.md +0 -927
- package/templates/python/examples/fastapi-app/CLAUDE.md +0 -229
- package/templates/python/examples/flask-app/.claude/commands/app-factory.md +0 -384
- package/templates/python/examples/flask-app/.claude/commands/blueprint.md +0 -243
- package/templates/python/examples/flask-app/.claude/commands/database.md +0 -410
- package/templates/python/examples/flask-app/.claude/commands/deployment.md +0 -620
- package/templates/python/examples/flask-app/.claude/commands/flask-route.md +0 -217
- package/templates/python/examples/flask-app/.claude/commands/testing.md +0 -559
- package/templates/python/examples/flask-app/CLAUDE.md +0 -391
- package/templates/ruby/.claude/commands/model.md +0 -360
- package/templates/ruby/.claude/commands/test.md +0 -480
- package/templates/ruby/.claude/settings.json +0 -146
- package/templates/ruby/.mcp.json +0 -83
- package/templates/ruby/CLAUDE.md +0 -284
- package/templates/ruby/examples/rails-app/.claude/commands/authentication.md +0 -490
- package/templates/ruby/examples/rails-app/CLAUDE.md +0 -376
- package/templates/rust/.mcp.json +0 -78
- package/templates/rust/README.md +0 -26
|
@@ -1,490 +0,0 @@
|
|
|
1
|
-
# Rails 8 Native Authentication Generator
|
|
2
|
-
|
|
3
|
-
Generate Rails 8's built-in authentication system with modern security practices.
|
|
4
|
-
|
|
5
|
-
## Purpose
|
|
6
|
-
|
|
7
|
-
This command helps you implement Rails 8's new native authentication system, eliminating the need for external gems like Devise for basic authentication needs.
|
|
8
|
-
|
|
9
|
-
## Usage
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
/authentication
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## What this command does
|
|
16
|
-
|
|
17
|
-
1. **Generates authentication models** with secure password handling
|
|
18
|
-
2. **Creates authentication controllers** for login/logout/signup
|
|
19
|
-
3. **Adds authentication views** with modern styling
|
|
20
|
-
4. **Implements session management** with security best practices
|
|
21
|
-
5. **Sets up authentication helpers** for controllers and views
|
|
22
|
-
|
|
23
|
-
## Rails 8 Authentication Generator
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
# Generate authentication for User model
|
|
27
|
-
bin/rails generate authentication User
|
|
28
|
-
|
|
29
|
-
# Or specify custom model name
|
|
30
|
-
bin/rails generate authentication Account
|
|
31
|
-
|
|
32
|
-
# Generate with custom attributes
|
|
33
|
-
bin/rails generate authentication User first_name:string last_name:string
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## Generated User Model
|
|
37
|
-
|
|
38
|
-
```ruby
|
|
39
|
-
# app/models/user.rb
|
|
40
|
-
class User < ApplicationRecord
|
|
41
|
-
has_secure_password
|
|
42
|
-
|
|
43
|
-
validates :email, presence: true, uniqueness: true
|
|
44
|
-
validates :password, length: { minimum: 8 }, if: -> { new_record? || !password.blank? }
|
|
45
|
-
|
|
46
|
-
normalizes :email, with: ->(email) { email.strip.downcase }
|
|
47
|
-
|
|
48
|
-
before_save :normalize_email
|
|
49
|
-
|
|
50
|
-
private
|
|
51
|
-
|
|
52
|
-
def normalize_email
|
|
53
|
-
self.email = email.downcase.strip
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Authentication Controller
|
|
59
|
-
|
|
60
|
-
```ruby
|
|
61
|
-
# app/controllers/authentication_controller.rb
|
|
62
|
-
class AuthenticationController < ApplicationController
|
|
63
|
-
skip_before_action :authenticate_user!, only: [:new, :create]
|
|
64
|
-
|
|
65
|
-
def new
|
|
66
|
-
# Login page
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def create
|
|
70
|
-
user = User.find_by(email: params[:email])
|
|
71
|
-
|
|
72
|
-
if user&.authenticate(params[:password])
|
|
73
|
-
login(user)
|
|
74
|
-
redirect_to root_path, notice: 'Logged in successfully'
|
|
75
|
-
else
|
|
76
|
-
flash.now[:alert] = 'Invalid email or password'
|
|
77
|
-
render :new, status: :unprocessable_entity
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def destroy
|
|
82
|
-
logout
|
|
83
|
-
redirect_to root_path, notice: 'Logged out successfully'
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
private
|
|
87
|
-
|
|
88
|
-
def login(user)
|
|
89
|
-
session[:user_id] = user.id
|
|
90
|
-
@current_user = user
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def logout
|
|
94
|
-
session[:user_id] = nil
|
|
95
|
-
@current_user = nil
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## Registration Controller
|
|
101
|
-
|
|
102
|
-
```ruby
|
|
103
|
-
# app/controllers/registrations_controller.rb
|
|
104
|
-
class RegistrationsController < ApplicationController
|
|
105
|
-
skip_before_action :authenticate_user!
|
|
106
|
-
|
|
107
|
-
def new
|
|
108
|
-
@user = User.new
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def create
|
|
112
|
-
@user = User.new(user_params)
|
|
113
|
-
|
|
114
|
-
if @user.save
|
|
115
|
-
login(@user)
|
|
116
|
-
redirect_to root_path, notice: 'Account created successfully'
|
|
117
|
-
else
|
|
118
|
-
render :new, status: :unprocessable_entity
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
private
|
|
123
|
-
|
|
124
|
-
def user_params
|
|
125
|
-
params.require(:user).permit(:email, :password, :password_confirmation, :first_name, :last_name)
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def login(user)
|
|
129
|
-
session[:user_id] = user.id
|
|
130
|
-
@current_user = user
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
## Application Controller Updates
|
|
136
|
-
|
|
137
|
-
```ruby
|
|
138
|
-
# app/controllers/application_controller.rb
|
|
139
|
-
class ApplicationController < ActionController::Base
|
|
140
|
-
protect_from_forgery with: :exception
|
|
141
|
-
|
|
142
|
-
before_action :authenticate_user!
|
|
143
|
-
|
|
144
|
-
private
|
|
145
|
-
|
|
146
|
-
def authenticate_user!
|
|
147
|
-
redirect_to login_path, alert: 'Please log in to continue' unless current_user
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def current_user
|
|
151
|
-
@current_user ||= User.find(session[:user_id]) if session[:user_id]
|
|
152
|
-
end
|
|
153
|
-
helper_method :current_user
|
|
154
|
-
|
|
155
|
-
def logged_in?
|
|
156
|
-
!!current_user
|
|
157
|
-
end
|
|
158
|
-
helper_method :logged_in?
|
|
159
|
-
|
|
160
|
-
def require_login
|
|
161
|
-
unless logged_in?
|
|
162
|
-
flash[:alert] = 'You must be logged in to access this page'
|
|
163
|
-
redirect_to login_path
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
## Authentication Views
|
|
170
|
-
|
|
171
|
-
### Login Form
|
|
172
|
-
```erb
|
|
173
|
-
<!-- app/views/authentication/new.html.erb -->
|
|
174
|
-
<div class="authentication-form">
|
|
175
|
-
<h1>Log In</h1>
|
|
176
|
-
|
|
177
|
-
<%= form_with url: login_path, local: true, class: "auth-form" do |form| %>
|
|
178
|
-
<div class="form-group">
|
|
179
|
-
<%= form.label :email, "Email" %>
|
|
180
|
-
<%= form.email_field :email, required: true, autofocus: true, class: "form-control" %>
|
|
181
|
-
</div>
|
|
182
|
-
|
|
183
|
-
<div class="form-group">
|
|
184
|
-
<%= form.label :password, "Password" %>
|
|
185
|
-
<%= form.password_field :password, required: true, class: "form-control" %>
|
|
186
|
-
</div>
|
|
187
|
-
|
|
188
|
-
<div class="form-actions">
|
|
189
|
-
<%= form.submit "Log In", class: "btn btn-primary" %>
|
|
190
|
-
</div>
|
|
191
|
-
<% end %>
|
|
192
|
-
|
|
193
|
-
<div class="auth-links">
|
|
194
|
-
<%= link_to "Don't have an account? Sign up", signup_path %>
|
|
195
|
-
</div>
|
|
196
|
-
</div>
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### Registration Form
|
|
200
|
-
```erb
|
|
201
|
-
<!-- app/views/registrations/new.html.erb -->
|
|
202
|
-
<div class="authentication-form">
|
|
203
|
-
<h1>Sign Up</h1>
|
|
204
|
-
|
|
205
|
-
<%= form_with model: @user, url: signup_path, local: true, class: "auth-form" do |form| %>
|
|
206
|
-
<% if @user.errors.any? %>
|
|
207
|
-
<div class="error-messages">
|
|
208
|
-
<h3><%= pluralize(@user.errors.count, "error") %> prohibited this account from being saved:</h3>
|
|
209
|
-
<ul>
|
|
210
|
-
<% @user.errors.full_messages.each do |message| %>
|
|
211
|
-
<li><%= message %></li>
|
|
212
|
-
<% end %>
|
|
213
|
-
</ul>
|
|
214
|
-
</div>
|
|
215
|
-
<% end %>
|
|
216
|
-
|
|
217
|
-
<div class="form-group">
|
|
218
|
-
<%= form.label :first_name, "First Name" %>
|
|
219
|
-
<%= form.text_field :first_name, class: "form-control" %>
|
|
220
|
-
</div>
|
|
221
|
-
|
|
222
|
-
<div class="form-group">
|
|
223
|
-
<%= form.label :last_name, "Last Name" %>
|
|
224
|
-
<%= form.text_field :last_name, class: "form-control" %>
|
|
225
|
-
</div>
|
|
226
|
-
|
|
227
|
-
<div class="form-group">
|
|
228
|
-
<%= form.label :email, "Email" %>
|
|
229
|
-
<%= form.email_field :email, required: true, class: "form-control" %>
|
|
230
|
-
</div>
|
|
231
|
-
|
|
232
|
-
<div class="form-group">
|
|
233
|
-
<%= form.label :password, "Password" %>
|
|
234
|
-
<%= form.password_field :password, required: true, minlength: 8, class: "form-control" %>
|
|
235
|
-
<small class="form-text">Minimum 8 characters</small>
|
|
236
|
-
</div>
|
|
237
|
-
|
|
238
|
-
<div class="form-group">
|
|
239
|
-
<%= form.label :password_confirmation, "Confirm Password" %>
|
|
240
|
-
<%= form.password_field :password_confirmation, required: true, class: "form-control" %>
|
|
241
|
-
</div>
|
|
242
|
-
|
|
243
|
-
<div class="form-actions">
|
|
244
|
-
<%= form.submit "Sign Up", class: "btn btn-primary" %>
|
|
245
|
-
</div>
|
|
246
|
-
<% end %>
|
|
247
|
-
|
|
248
|
-
<div class="auth-links">
|
|
249
|
-
<%= link_to "Already have an account? Log in", login_path %>
|
|
250
|
-
</div>
|
|
251
|
-
</div>
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
## Route Configuration
|
|
255
|
-
|
|
256
|
-
```ruby
|
|
257
|
-
# config/routes.rb
|
|
258
|
-
Rails.application.routes.draw do
|
|
259
|
-
# Authentication routes
|
|
260
|
-
get 'login', to: 'authentication#new'
|
|
261
|
-
post 'login', to: 'authentication#create'
|
|
262
|
-
delete 'logout', to: 'authentication#destroy'
|
|
263
|
-
|
|
264
|
-
# Registration routes
|
|
265
|
-
get 'signup', to: 'registrations#new'
|
|
266
|
-
post 'signup', to: 'registrations#create'
|
|
267
|
-
|
|
268
|
-
# User management routes
|
|
269
|
-
resources :users, except: [:new, :create]
|
|
270
|
-
|
|
271
|
-
# Root route
|
|
272
|
-
root 'dashboard#index'
|
|
273
|
-
end
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
## Migration Files
|
|
277
|
-
|
|
278
|
-
```ruby
|
|
279
|
-
# db/migrate/xxx_create_users.rb
|
|
280
|
-
class CreateUsers < ActiveRecord::Migration[8.0]
|
|
281
|
-
def change
|
|
282
|
-
create_table :users do |t|
|
|
283
|
-
t.string :email, null: false
|
|
284
|
-
t.string :password_digest, null: false
|
|
285
|
-
t.string :first_name
|
|
286
|
-
t.string :last_name
|
|
287
|
-
|
|
288
|
-
t.timestamps
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
add_index :users, :email, unique: true
|
|
292
|
-
end
|
|
293
|
-
end
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
## Advanced Authentication Features
|
|
297
|
-
|
|
298
|
-
### Password Reset
|
|
299
|
-
```ruby
|
|
300
|
-
# app/controllers/password_resets_controller.rb
|
|
301
|
-
class PasswordResetsController < ApplicationController
|
|
302
|
-
skip_before_action :authenticate_user!
|
|
303
|
-
|
|
304
|
-
def new
|
|
305
|
-
# Password reset request form
|
|
306
|
-
end
|
|
307
|
-
|
|
308
|
-
def create
|
|
309
|
-
@user = User.find_by(email: params[:email])
|
|
310
|
-
if @user
|
|
311
|
-
@user.send_password_reset
|
|
312
|
-
redirect_to root_path, notice: 'Email sent with password reset instructions'
|
|
313
|
-
else
|
|
314
|
-
flash.now[:alert] = 'Email address not found'
|
|
315
|
-
render :new
|
|
316
|
-
end
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
def edit
|
|
320
|
-
@user = User.find_by(password_reset_token: params[:id])
|
|
321
|
-
if @user.nil? || @user.password_reset_sent_at < 2.hours.ago
|
|
322
|
-
redirect_to new_password_reset_path, alert: 'Password reset has expired'
|
|
323
|
-
end
|
|
324
|
-
end
|
|
325
|
-
|
|
326
|
-
def update
|
|
327
|
-
@user = User.find_by(password_reset_token: params[:id])
|
|
328
|
-
if @user && @user.password_reset_sent_at > 2.hours.ago
|
|
329
|
-
if @user.update(password_params)
|
|
330
|
-
@user.update_columns(password_reset_token: nil, password_reset_sent_at: nil)
|
|
331
|
-
redirect_to root_path, notice: 'Password has been reset'
|
|
332
|
-
else
|
|
333
|
-
render :edit
|
|
334
|
-
end
|
|
335
|
-
else
|
|
336
|
-
redirect_to new_password_reset_path, alert: 'Password reset has expired'
|
|
337
|
-
end
|
|
338
|
-
end
|
|
339
|
-
|
|
340
|
-
private
|
|
341
|
-
|
|
342
|
-
def password_params
|
|
343
|
-
params.require(:user).permit(:password, :password_confirmation)
|
|
344
|
-
end
|
|
345
|
-
end
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
### User Model Extensions
|
|
349
|
-
```ruby
|
|
350
|
-
# app/models/user.rb (extended)
|
|
351
|
-
class User < ApplicationRecord
|
|
352
|
-
has_secure_password
|
|
353
|
-
|
|
354
|
-
validates :email, presence: true, uniqueness: true
|
|
355
|
-
validates :password, length: { minimum: 8 }, if: -> { new_record? || !password.blank? }
|
|
356
|
-
|
|
357
|
-
normalizes :email, with: ->(email) { email.strip.downcase }
|
|
358
|
-
|
|
359
|
-
# Password reset functionality
|
|
360
|
-
def send_password_reset
|
|
361
|
-
generate_token(:password_reset_token)
|
|
362
|
-
self.password_reset_sent_at = Time.zone.now
|
|
363
|
-
save!
|
|
364
|
-
UserMailer.password_reset(self).deliver_now
|
|
365
|
-
end
|
|
366
|
-
|
|
367
|
-
def full_name
|
|
368
|
-
"#{first_name} #{last_name}".strip
|
|
369
|
-
end
|
|
370
|
-
|
|
371
|
-
def initials
|
|
372
|
-
"#{first_name&.first}#{last_name&.first}".upcase
|
|
373
|
-
end
|
|
374
|
-
|
|
375
|
-
private
|
|
376
|
-
|
|
377
|
-
def generate_token(column)
|
|
378
|
-
begin
|
|
379
|
-
self[column] = SecureRandom.urlsafe_base64
|
|
380
|
-
end while User.exists?(column => self[column])
|
|
381
|
-
end
|
|
382
|
-
end
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
## Session Security Enhancements
|
|
386
|
-
|
|
387
|
-
```ruby
|
|
388
|
-
# config/application.rb
|
|
389
|
-
config.session_store :cookie_store, {
|
|
390
|
-
key: '_myapp_session',
|
|
391
|
-
secure: Rails.env.production?,
|
|
392
|
-
httponly: true,
|
|
393
|
-
same_site: :lax
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
# config/environments/production.rb
|
|
397
|
-
config.force_ssl = true
|
|
398
|
-
config.session_store :cookie_store, {
|
|
399
|
-
key: '_myapp_session',
|
|
400
|
-
secure: true,
|
|
401
|
-
httponly: true,
|
|
402
|
-
same_site: :strict
|
|
403
|
-
}
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
## Authentication Helper Methods
|
|
407
|
-
|
|
408
|
-
```ruby
|
|
409
|
-
# app/helpers/authentication_helper.rb
|
|
410
|
-
module AuthenticationHelper
|
|
411
|
-
def user_avatar(user, size: 40)
|
|
412
|
-
if user.avatar.present?
|
|
413
|
-
image_tag user.avatar, alt: user.full_name, class: "avatar", size: "#{size}x#{size}"
|
|
414
|
-
else
|
|
415
|
-
content_tag :div, user.initials, class: "avatar avatar-initials",
|
|
416
|
-
style: "width: #{size}px; height: #{size}px; line-height: #{size}px;"
|
|
417
|
-
end
|
|
418
|
-
end
|
|
419
|
-
|
|
420
|
-
def current_user_menu
|
|
421
|
-
if logged_in?
|
|
422
|
-
render 'shared/user_menu'
|
|
423
|
-
else
|
|
424
|
-
render 'shared/guest_menu'
|
|
425
|
-
end
|
|
426
|
-
end
|
|
427
|
-
end
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
## Testing Authentication
|
|
431
|
-
|
|
432
|
-
```ruby
|
|
433
|
-
# spec/models/user_spec.rb
|
|
434
|
-
RSpec.describe User, type: :model do
|
|
435
|
-
describe 'validations' do
|
|
436
|
-
it { should validate_presence_of(:email) }
|
|
437
|
-
it { should validate_uniqueness_of(:email) }
|
|
438
|
-
it { should validate_length_of(:password).is_at_least(8) }
|
|
439
|
-
it { should have_secure_password }
|
|
440
|
-
end
|
|
441
|
-
|
|
442
|
-
describe 'email normalization' do
|
|
443
|
-
it 'normalizes email to lowercase' do
|
|
444
|
-
user = User.create!(email: 'USER@EXAMPLE.COM', password: 'password123')
|
|
445
|
-
expect(user.email).to eq('user@example.com')
|
|
446
|
-
end
|
|
447
|
-
end
|
|
448
|
-
|
|
449
|
-
describe '#full_name' do
|
|
450
|
-
it 'returns combined first and last name' do
|
|
451
|
-
user = User.new(first_name: 'John', last_name: 'Doe')
|
|
452
|
-
expect(user.full_name).to eq('John Doe')
|
|
453
|
-
end
|
|
454
|
-
end
|
|
455
|
-
end
|
|
456
|
-
|
|
457
|
-
# spec/controllers/authentication_controller_spec.rb
|
|
458
|
-
RSpec.describe AuthenticationController, type: :controller do
|
|
459
|
-
describe 'POST #create' do
|
|
460
|
-
let(:user) { create(:user, password: 'password123') }
|
|
461
|
-
|
|
462
|
-
context 'with valid credentials' do
|
|
463
|
-
it 'logs in the user' do
|
|
464
|
-
post :create, params: { email: user.email, password: 'password123' }
|
|
465
|
-
expect(session[:user_id]).to eq(user.id)
|
|
466
|
-
expect(response).to redirect_to(root_path)
|
|
467
|
-
end
|
|
468
|
-
end
|
|
469
|
-
|
|
470
|
-
context 'with invalid credentials' do
|
|
471
|
-
it 'does not log in the user' do
|
|
472
|
-
post :create, params: { email: user.email, password: 'wrong' }
|
|
473
|
-
expect(session[:user_id]).to be_nil
|
|
474
|
-
expect(response).to render_template(:new)
|
|
475
|
-
end
|
|
476
|
-
end
|
|
477
|
-
end
|
|
478
|
-
end
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
## Security Best Practices Included
|
|
482
|
-
|
|
483
|
-
- **Secure password hashing** with bcrypt
|
|
484
|
-
- **Email normalization** to prevent duplicates
|
|
485
|
-
- **CSRF protection** enabled by default
|
|
486
|
-
- **Session security** with httponly and secure flags
|
|
487
|
-
- **Password strength validation** (minimum 8 characters)
|
|
488
|
-
- **Timing attack prevention** in authentication
|
|
489
|
-
- **Password reset token expiration**
|
|
490
|
-
- **SSL enforcement** in production
|