# 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:
1. JWT-based token authentication
2. Email verification workflow
3. 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:

```ruby
class User < ApplicationRecord
  # Token generation for different purposes with specific lifetimes
  generates_token_for :auth_token, expires_in: 1.week
  generates_token_for :email_confirmation, expires_in: 8.hours
  generates_token_for :password_reset, expires_in: 1.hour
end
```

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 call `user.generate_token_for(:auth_token)`, it:
1. Creates a JWT token with user-specific claims
2. Sets an expiration time (1 week for auth tokens)
3. Signs the token with the application's secret key
4. Returns the encoded token for client use

### Authentication Service

```ruby
module Users
  class SignInService < ApplicationService
    def call
      return failure([USER_NOT_FOUND_MESSAGE]) unless user

      # Generate authentication token
      token = user.generate_token_for(:auth_token)
      
      # Log the authentication event
      log_event(user:, data: { username: user.username })

      # Return success response with token and user data
      success({ token:, user: })
    end

    private

    def user
      # Authenticate user using credentials
      @user ||= User.authenticate_by(permitted_params)
    end
  end
end
```

Key aspects of the service:
1. Validates user credentials
2. Generates an authentication token
3. Logs the authentication event
4. Returns a structured response

### GraphQL Authentication Mutation

```ruby
module Mutations
  class UserSignIn < BaseMutationWithErrors
    # Define required input arguments
    argument :password, String, required: true
    argument :username, String, required: true

    # Define return fields
    field :token, String, null: true
    field :user, Types::UserType, null: true

    def resolve(**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
      }
    end
  end
end
```

This mutation:
1. Accepts username and password
2. Calls the authentication service
3. Returns token and user data on success
4. Handles errors gracefully

## 📧 Email Verification System

### Confirmation Service Implementation

```ruby
module Users
  class SendConfirmationEmailService < ApplicationService
    def call
      return failure([USER_NOT_FOUND_ERROR]) unless user
      return failure([USER_ALREADY_CONFIRMED_ERROR]) if user.confirmed?

      send_confirmation_email
      log_event(user:, data: { confirmation_sent: true })
      success(CONFIRMATION_SENT_MSG)
    end

    private

    def send_confirmation_email
      # Generate confirmation email with secure token
      email = Email.create_confirmation_email!(user:)
      send_email(email)
    end
  end
end
```

The confirmation flow:
1. Checks user existence and confirmation status
2. Generates a secure confirmation token
3. Creates and sends confirmation email
4. Logs the confirmation attempt

### Email Token Generation

```ruby
class Email < ApplicationRecord
  def self.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 
      }]
    )
  end
end
```

This creates a confirmation email with:
1. A secure, time-limited token
2. A personalized confirmation URL
3. User-specific template data

## 🔒 Security Implementation

### Authentication Middleware

```ruby
module Queries
  class BaseQuery < GraphQL::Schema::Resolver
    def authenticate_user!
      return if current_user

      raise GraphQL::ExecutionError.new(
        I18n.t('gql.errors.not_authenticated'),
        extensions: { code: 'AUTHENTICATION_ERROR' }
      )
    end

    def current_user
      context[:current_user] || Current.user
    end
  end
end
```

This middleware:
1. Verifies token presence and validity
2. Maintains user context throughout requests
3. Handles authentication errors consistently
4. Provides access to current user data

### Password Security

```ruby
class User < ApplicationRecord
  # Regular expression for password validation
  PASSWORD_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?

  private

  def password_required?
    password_digest.nil? || password.present?
  end
end
```

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

```ruby
RSpec.describe Users::SignInService do
  describe '#call' do
    context 'when credentials are valid' do
      it 'generates an authentication token' do
        result = service.call
        expect(result.data[:token]).to be_present
        expect(User.find_by_token_for(:auth_token, result.data[:token])).to eq(user)
      end

      it 'logs the authentication event' do
        expect { service.call }.to change(AuditLog, :count).by(1)
      end
    end

    context 'when credentials are invalid' do
      it 'returns appropriate error messages' do
        result = described_class.new(invalid_params).call
        expect(result.errors).to include(I18n.t('services.users.sign_in.user_not_found'))
      end
    end
  end
end
```

Our testing approach:
1. Verifies token generation and validation
2. Ensures proper error handling
3. Checks audit logging
4. 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!
