
Posted on • Originally published atsulmanweb.com
Rails GraphQL Auth - JWT, Email & Security
Building a secure and scalable authentication system is crucial for modern web applications. Today, I'll share insights from implementing a robust GraphQL authentication system in Rails, with detailed explanations of each component.
🏗️ Architecture Overview
The authentication system consists of three main components:
- JWT-based token authentication
- Email verification workflow
- Secure password management
Let's examine each component in detail.
🔑 JWT Authentication Implementation
Token Generation and Management
The core of our authentication relies on JWT tokens. Let's break down the key components:
classUser<ApplicationRecord# Token generation for different purposes with specific lifetimesgenerates_token_for:auth_token,expires_in:1.weekgenerates_token_for:email_confirmation,expires_in:8.hoursgenerates_token_for:password_reset,expires_in:1.hourend
This code uses a custom token generation system where:
generates_token_for
is a macro that sets up token generation for specific purposes- Each token type has its own expiration time
- Tokens are bound to specific user data for verification
For example, when we calluser.generate_token_for(:auth_token)
, it:
- Creates a JWT token with user-specific claims
- Sets an expiration time (1 week for auth tokens)
- Signs the token with the application's secret key
- Returns the encoded token for client use
Authentication Service
moduleUsersclassSignInService<ApplicationServicedefcallreturnfailure([USER_NOT_FOUND_MESSAGE])unlessuser# Generate authentication tokentoken=user.generate_token_for(:auth_token)# Log the authentication eventlog_event(user:,data:{username:user.username})# Return success response with token and user datasuccess({token:,user:})endprivatedefuser# Authenticate user using credentials@user||=User.authenticate_by(permitted_params)endendend
Key aspects of the service:
- Validates user credentials
- Generates an authentication token
- Logs the authentication event
- Returns a structured response
GraphQL Authentication Mutation
moduleMutationsclassUserSignIn<BaseMutationWithErrors# Define required input argumentsargument:password,String,required:trueargument:username,String,required:true# Define return fieldsfield:token,String,null:truefield:user,Types::UserType,null:truedefresolve(**args)result=Users::SignInService.call(args){errors:result.errors,success:result.success?,token:result.success??result.data[:token]:nil,user:result.success??result.data[:user]:nil}endendend
This mutation:
- Accepts username and password
- Calls the authentication service
- Returns token and user data on success
- Handles errors gracefully
📧 Email Verification System
Confirmation Service Implementation
moduleUsersclassSendConfirmationEmailService<ApplicationServicedefcallreturnfailure([USER_NOT_FOUND_ERROR])unlessuserreturnfailure([USER_ALREADY_CONFIRMED_ERROR])ifuser.confirmed?send_confirmation_emaillog_event(user:,data:{confirmation_sent:true})success(CONFIRMATION_SENT_MSG)endprivatedefsend_confirmation_email# Generate confirmation email with secure tokenemail=Email.create_confirmation_email!(user:)send_email(email)endendend
The confirmation flow:
- Checks user existence and confirmation status
- Generates a secure confirmation token
- Creates and sends confirmation email
- Logs the confirmation attempt
Email Token Generation
classEmail<ApplicationRecorddefself.create_confirmation_email!(user:)token=user.generate_token_for(:email_confirmation)create!(to_emails:[user.email],template_id:Rails.application.credentials.dig(:sendgrid,:confirm_template_id),substitutions:[{"confirmation_url":"#{Settings.emails.confirm_url}?token=#{token}",name:user.name}])endend
This creates a confirmation email with:
- A secure, time-limited token
- A personalized confirmation URL
- User-specific template data
🔒 Security Implementation
Authentication Middleware
moduleQueriesclassBaseQuery<GraphQL::Schema::Resolverdefauthenticate_user!returnifcurrent_userraiseGraphQL::ExecutionError.new(I18n.t('gql.errors.not_authenticated'),extensions:{code:'AUTHENTICATION_ERROR'})enddefcurrent_usercontext[:current_user]||Current.userendendend
This middleware:
- Verifies token presence and validity
- Maintains user context throughout requests
- Handles authentication errors consistently
- Provides access to current user data
Password Security
classUser<ApplicationRecord# Regular expression for password validationPASSWORD_FORMAT=/\A(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*(),.?":{}|<>])[A-Za-z\d!@#$%^&*(),.?":{}|<>]{8,72}\z/validates:password,presence:true,length:{minimum:8,maximum:72},format:{with:PASSWORD_FORMAT},if: :password_required?privatedefpassword_required?password_digest.nil?||password.present?endend
Password requirements:
- Minimum 8 characters
- Maximum 72 characters (bcrypt limitation)
- Must include lowercase and uppercase letters
- Must include numbers and special characters
- Validated only when necessary
🧪 Testing Strategy
RSpec.describeUsers::SignInServicedodescribe'#call'docontext'when credentials are valid'doit'generates an authentication token'doresult=service.callexpect(result.data[:token]).tobe_presentexpect(User.find_by_token_for(:auth_token,result.data[:token])).toeq(user)endit'logs the authentication event'doexpect{service.call}.tochange(AuditLog,:count).by(1)endendcontext'when credentials are invalid'doit'returns appropriate error messages'doresult=described_class.new(invalid_params).callexpect(result.errors).toinclude(I18n.t('services.users.sign_in.user_not_found'))endendendend
Our testing approach:
- Verifies token generation and validation
- Ensures proper error handling
- Checks audit logging
- Validates security constraints
Conclusion
By implementing these patterns, we've created a secure, maintainable authentication system that:
- Provides secure token-based authentication
- Handles email verification properly
- Maintains high security standards
- Scales well with application growth
The complete implementation demonstrates how these components work together in a production environment while maintaining security and user experience.
Happy Coding!
Originally published athttps://sulmanweb.com.
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse