-
Notifications
You must be signed in to change notification settings - Fork 384
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Authentication API #616
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,55 @@ | |||||||||||||||||
# frozen_string_literal: true | |||||||||||||||||
|
|||||||||||||||||
module Api | |||||||||||||||||
class AuthenticationController < ApplicationController | |||||||||||||||||
def create | |||||||||||||||||
user = User.find_by(email: params[:email].downcase.strip) | |||||||||||||||||
|
|||||||||||||||||
if user&.valid_password?(params[:password]) | |||||||||||||||||
begin | |||||||||||||||||
token = user.generate_jwt | |||||||||||||||||
render json: { | |||||||||||||||||
message: "Login successful", | |||||||||||||||||
token: token | |||||||||||||||||
}, status: :ok | |||||||||||||||||
rescue JWT::EncodeError | |||||||||||||||||
render json: { error: "Authentication failed" }, status: :internal_server_error | |||||||||||||||||
end | |||||||||||||||||
else | |||||||||||||||||
render json: { error: "Invalid credentials" }, status: :unauthorized | |||||||||||||||||
end | |||||||||||||||||
end | |||||||||||||||||
|
|||||||||||||||||
def signup | |||||||||||||||||
return render json: { error: "Invalid email format" }, status: :unprocessable_entity unless | |||||||||||||||||
params.dig(:user, :email)&.match?(URI::MailTo::EMAIL_REGEXP) | |||||||||||||||||
|
|||||||||||||||||
return render json: { error: "Password must be at least 6 characters" }, status: :unprocessable_entity if | |||||||||||||||||
params.dig(:user, :password)&.length.to_i < 6 | |||||||||||||||||
|
|||||||||||||||||
user = User.new(user_params) | |||||||||||||||||
|
|||||||||||||||||
if user.save | |||||||||||||||||
begin | |||||||||||||||||
token = user.generate_jwt | |||||||||||||||||
render json: { | |||||||||||||||||
message: "Signup successful", | |||||||||||||||||
token: token | |||||||||||||||||
}, status: :created | |||||||||||||||||
rescue JWT::EncodeError | |||||||||||||||||
render json: { error: "Failed to generate authentication token" }, status: :internal_server_error | |||||||||||||||||
end | |||||||||||||||||
else | |||||||||||||||||
render json: { | |||||||||||||||||
errors: user.errors.full_messages | |||||||||||||||||
}, status: :unprocessable_entity | |||||||||||||||||
end | |||||||||||||||||
end | |||||||||||||||||
|
|||||||||||||||||
private | |||||||||||||||||
|
|||||||||||||||||
def user_params | |||||||||||||||||
params.require(:user).permit(:email, :password, :password_confirmation) | |||||||||||||||||
end | |||||||||||||||||
end | |||||||||||||||||
Comment on lines
+1
to
+54
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainAdd token revocation functionality. The controller handles token generation but lacks functionality for token revocation or logout. Consider adding a method to invalidate tokens. You could add a logout method: def logout
# If using a token blacklist or revocation strategy
current_user&.revoke_jwt
render json: { message: "Logged out successfully" }, status: :ok
end This would require implementing a 🌐 Web query:
💡 Result: JWTs (JSON Web Tokens) present unique challenges for revocation due to their stateless nature, but several best practices ensure secure token management: Core Strategies for JWT Revocation1. Short-Lived Tokens with Refresh Tokens
2. Distributed Event Systems
3. Blocklist/Denylist
4. Refresh Token Rotation
Additional Best Practices
Tradeoffs and Recommendations
For most applications, combining short-lived JWTs with refresh token rotation and event-driven revocation provides robust security. Services like Auth0 or FusionAuth simplify implementation with built-in token management and webhooks[8][12]. Citations:
Implement Token Revocation (Logout) Functionality The
For instance, you could add a logout method as follows: def logout
# Revoke the current JWT using a blocklist or similar strategy.
# Ensure that the 'revoke_jwt' method in the User model properly invalidates the token,
# such as by storing its JTI in a token blocklist.
current_user&.revoke_jwt if current_user
render json: { message: "Logged out successfully" }, status: :ok
end Action Items:
|
|||||||||||||||||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# frozen_string_literal: true | ||
|
||
class User < ApplicationRecord | ||
devise :database_authenticatable, :registerable, | ||
:recoverable, :rememberable, :validatable | ||
def generate_jwt | ||
JWT.encode( | ||
{ | ||
id: id, | ||
exp: 60.days.from_now.to_i | ||
}, | ||
Rails.application.credentials.secret_key_base | ||
) | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add rate limiting to prevent brute force attacks.
The login endpoint currently has no protection against brute force attacks. Consider implementing rate limiting based on IP address and/or account.
You could implement this by adding a before_action that checks for too many failed attempts: