Kulcare Docs
General08 troubleshooting

Authentication Issues Troubleshooting

This guide covers common authentication problems in the Kulcare API backend and their solutions.

Common Authentication Issues

JWT Token Problems

Issue: "JWT::DecodeError: Invalid token"

Symptoms:

  • Users getting "Invalid token" errors
  • API returns 401 Unauthorized
  • Token validation fails

Diagnosis:

# Rails console debugging
token = "your_jwt_token_here"
decoded_token = TokenIssuer.decode_token(token)

if decoded_token.nil?
  puts "Token is invalid or corrupted"
else
  puts "Token payload: #{decoded_token}"
  puts "Expires at: #{Time.at(decoded_token['exp'])}"
  puts "User ID: #{decoded_token['user_id']}"
end

Common Causes:

  1. Secret Key Mismatch: Different secret keys between environments
  2. Token Corruption: Network issues or client-side storage problems
  3. Algorithm Mismatch: JWT algorithm not matching server configuration

Solutions:

# 1. Verify secret key configuration
Rails.application.credentials.secret_key_base

# 2. Check token generation
user = User.find(123)
test_token = TokenIssuer.issue_token(user_id: user.id)
decoded = TokenIssuer.decode_token(test_token)
puts "Test token valid: #{!decoded.nil?}"

# 3. Review client token storage
# Ensure client apps store tokens properly without modification

Issue: "Token expired"

Symptoms:

  • Valid tokens getting expired errors
  • Users forced to login frequently
  • Refresh token flow not working

Diagnosis:

# Check token expiration settings
token = TokenIssuer.decode_token(expired_token)
if token
  exp_time = Time.at(token['exp'])
  puts "Token expired at: #{exp_time}"
  puts "Current time: #{Time.current}"
  puts "Expired #{Time.current - exp_time} seconds ago"
end

# Check refresh token status
refresh_token = RefreshToken.find_by(token: encrypted_refresh_token)
puts "Refresh token exists: #{refresh_token.present?}"
puts "Refresh token expired: #{refresh_token&.expired?}"

Solutions:

# 1. Adjust token expiration time
# In TokenIssuer service
def self.issue_token(payload, expiration = 24.hours.from_now)  # Increase if needed
  # token generation logic
end

# 2. Implement proper refresh token flow
# Ensure clients implement automatic token refresh
POST /auth/refresh
{
  "refresh_token": "encrypted_refresh_token",
  "user_id": 123
}

# 3. Check refresh token cleanup
# Remove expired refresh tokens
RefreshToken.where('expires_at < ?', Time.current).destroy_all

Multi-Profile Authentication Issues

Issue: Profile Access Denied

Symptoms:

  • Users can't access specific profiles
  • "Profile not found or not accessible" errors
  • Wrong user_account_id in requests

Diagnosis:

# Check user's available profiles
user = User.find(123)
profiles = user.user_accounts.includes(:account, :role)

profiles.each do |ua|
  puts "Profile ID: #{ua.id}"
  puts "Account Type: #{ua.account.name}" 
  puts "Role: #{ua.role.name}"
  puts "Profile ID: #{ua.profile_id}"
  puts "Deleted: #{ua.deleted}"
  puts "---"
end

# Verify specific profile access
user_account_id = 456
profile = user.user_accounts.find_by(id: user_account_id)
puts "Profile exists: #{profile.present?}"
puts "Profile deleted: #{profile&.deleted?}"

Solutions:

# 1. Check profile deletion status
UserAccount.where(user_id: user_id, deleted: true).update_all(deleted: false)

# 2. Verify profile creation
# Ensure profiles are created during registration
user.create_profile  # Method in User model

# 3. Fix missing clinic-doctor relationships
doctor = user.doctor_profiles.first
if doctor && doctor.clinics.empty?
  # Create default clinic for doctor
  clinic = Clinic.create!(name: "#{doctor.name} Clinic", ...)
  # Link doctor to clinic
end

Issue: Profile Context Problems

Symptoms:

  • API calls returning wrong clinic/doctor data
  • Permission errors for valid operations
  • Data not filtered by current profile

Diagnosis:

# Check current profile context in controller
current_profile = current_user.user_accounts.find_by(id: params[:user_account_id])
puts "Current profile: #{current_profile&.account&.name}"
puts "Profile clinic ID: #{current_profile&.profile_id}" if current_profile&.account&.name == 'clinic'

# Verify data access permissions
case current_profile&.account&.name
when 'clinic'
  clinic = Clinic.find(current_profile.profile_id)
  puts "Clinic patients count: #{clinic.clinic_patients.count}"
when 'doctor'
  doctor = Doctor.find(current_profile.profile_id) 
  puts "Doctor clinics: #{doctor.clinics.pluck(:name)}"
end

Solutions:

# 1. Ensure user_account_id is passed in requests
# Frontend should include user_account_id in API calls
# Backend should validate and use this for context

# 2. Implement proper profile authorization
class ApplicationController
  before_action :set_current_profile
  
  private
  
  def set_current_profile
    if params[:user_account_id].present?
      @current_profile = current_user.user_accounts.find_by(id: params[:user_account_id])
      unless @current_profile
        render json: { error: 'Profile not found' }, status: :forbidden
        return
      end
    end
  end
end

# 3. Use profile context in queries
# Always filter data by current profile context
appointments = current_clinic.appointments  # Not all appointments

OTP Verification Issues

Issue: OTP Not Delivered

Symptoms:

  • Users not receiving verification codes
  • SMS/WhatsApp delivery failures
  • OTP timeout errors

Diagnosis:

# Check recent OTP messages
user = User.find_by(phone_number: '919876543210')
puts "User verification status: #{user&.verified?}"
puts "Last verification code sent: #{user&.verification_code_sent_at}"

# Check message logs
recent_messages = MsgLog.where(phone_number: '919876543210')
                        .where('created_at > ?', 1.hour.ago)
                        
recent_messages.each do |msg|
  puts "Type: #{msg.msg_type}, Status: #{msg.msg_status}, Text: #{msg.msg_text[0..50]}"
end

# Check SMS service health
begin
  response = HTTParty.post('https://api.msg91.com/api/v2/sendsms', {
    # test message
  })
  puts "MSG91 API Status: #{response.code}"
rescue => e
  puts "MSG91 API Error: #{e.message}"
end

Solutions:

# 1. Resend verification code manually
user.send_verification_code

# 2. Check SMS provider configuration
# Verify MSG91/Twilio credentials
Rails.application.credentials.msg91_auth_key
Rails.application.credentials.twilio_account_sid

# 3. Test with different phone number format
# Ensure proper country code formatting
phone = "+91#{phone_number}" unless phone_number.start_with?('+')

Issue: Invalid OTP Error

Symptoms:

  • Correct OTP showing as invalid
  • OTP expiration issues
  • Case sensitivity problems

Diagnosis:

# Check OTP details
user = User.find_by(phone_number: '919876543210')
puts "Stored OTP: #{user.verification_code}"
puts "OTP sent at: #{user.verification_code_sent_at}"
puts "Current time: #{Time.current}"
puts "Minutes since sent: #{(Time.current - user.verification_code_sent_at) / 60}"

# Test OTP verification
test_code = "12345"
puts "OTP valid: #{user.verify_code(test_code)}"

Solutions:

# 1. Check OTP expiration window
def verify_code(code)
  return false if verification_code.blank?
  return false if verification_code_sent_at < 10.minutes.ago  # Adjust window
  
  verification_code == code.to_s  # Ensure string comparison
end

# 2. Implement case-insensitive comparison if needed
verification_code.downcase == code.to_s.downcase

# 3. Add OTP retry mechanism
def resend_verification_code
  return false if verification_code_sent_at > 1.minute.ago  # Rate limiting
  send_verification_code
end

Session Management Issues

Issue: User Session Expired

Symptoms:

  • Frequent forced logouts
  • Session timeout errors
  • Device registration problems

Diagnosis:

# Check user devices
user = User.find(123)
devices = user.devices.includes(:refresh_tokens)

devices.each do |device|
  puts "Device: #{device.device_name} (#{device.platform})"
  puts "Last active: #{device.last_active_at}"
  puts "Active refresh tokens: #{device.refresh_tokens.active.count}"
end

# Check session duration settings
puts "Token expiration: #{24.hours.to_i} seconds"
puts "Refresh token expiration: #{30.days.to_i} seconds"

Solutions:

# 1. Update device activity tracking
class ApplicationController
  after_action :update_device_activity
  
  private
  
  def update_device_activity
    if current_user && device_identifier.present?
      device = current_user.devices.find_by(device_id: device_identifier)
      device&.update_activity!
    end
  end
end

# 2. Clean up expired tokens
RefreshToken.where('expires_at < ?', Time.current).destroy_all

# 3. Extend session duration if needed
# Adjust expiration times in TokenIssuer

Social Authentication Issues

Issue: OAuth Connection Failures

Symptoms:

  • Social login not working
  • OAuth provider errors
  • Missing user information

Diagnosis:

# Check OAuth configuration
puts "Facebook App ID: #{Rails.application.credentials.facebook_app_id}"
puts "Google Client ID: #{Rails.application.credentials.google_client_id}"

# Check existing social connections
user = User.find(123)
auth_services = user.auth_services

auth_services.each do |auth_service|
  puts "Provider: #{auth_service.auth_type}"
  puts "UID: #{auth_service.uid}"
  puts "Created: #{auth_service.created_at}"
end

Solutions:

# 1. Verify OAuth app configuration
# Check redirect URLs in provider dashboards
# Ensure client IDs and secrets are correct

# 2. Handle OAuth errors gracefully
begin
  auth_service = AuthService.create!(oauth_params)
rescue ActiveRecord::RecordInvalid => e
  puts "OAuth error: #{e.message}"
  # Handle duplicate account scenario
end

# 3. Test OAuth flow manually
# Use provider's OAuth playground to test tokens

Password Reset Issues

Issue: Reset Password Not Working

Symptoms:

  • Reset password emails not sent
  • Invalid reset tokens
  • Reset links not working

Diagnosis:

# Check password reset request
user = User.find_by(email: 'user@example.com')
puts "User found: #{user.present?}"
puts "Reset token: #{user&.reset_password_token}"
puts "Reset sent at: #{user&.reset_password_sent_at}"

# Check email delivery
# Review ActionMailer logs for delivery status

Solutions:

# 1. Resend password reset manually
user.send_reset_password_instructions!

# 2. Check email configuration
# Verify SMTP settings in config/environments/production.rb
config.action_mailer.smtp_settings = {
  # SMTP configuration
}

# 3. Implement backup reset mechanism
# SMS-based password reset for users without email

Prevention Strategies

Monitoring Authentication Health

# Authentication health check endpoint
def auth_health_check
  {
    total_active_sessions: RefreshToken.active.count,
    failed_logins_last_hour: login_failures_count,
    otp_delivery_rate: otp_success_rate,
    token_expiration_issues: expired_token_count,
    multi_profile_errors: profile_access_errors
  }
end

Automated Testing

# Authentication system tests
RSpec.describe 'Authentication System' do
  describe 'JWT Token Flow' do
    it 'issues and validates tokens correctly' do
      user = create(:user, :verified)
      token = TokenIssuer.issue_token(user_id: user.id)
      decoded = TokenIssuer.decode_token(token)
      
      expect(decoded['user_id']).to eq(user.id)
      expect(decoded['exp']).to be > Time.current.to_i
    end
  end
  
  describe 'Multi-Profile Access' do
    it 'allows access to user profiles' do
      user = create(:user, :with_clinic_profile)
      profile = user.user_accounts.first
      
      expect(profile).to be_present
      expect(profile.deleted).to be_falsey
    end
  end
end

Security Best Practices

Token Security

  • Use strong secret keys for JWT signing
  • Implement proper token rotation
  • Monitor for token reuse attacks
  • Set appropriate expiration times

Session Security

  • Implement device fingerprinting
  • Monitor for concurrent sessions
  • Log suspicious authentication patterns
  • Implement account lockout for failed attempts

Multi-Profile Security

  • Validate profile access on every request
  • Audit profile switching activities
  • Implement role-based permissions
  • Monitor cross-profile data access

Quick Fixes

Emergency Authentication Reset

# Rails console commands for emergency fixes

# Reset user verification status
User.find_by(phone_number: '919876543210').update!(verified: true)

# Clear expired tokens
RefreshToken.where('expires_at < ?', Time.current).destroy_all

# Regenerate user profile
user = User.find(123)
user.create_profile if user.user_accounts.empty?

# Reset OAuth connections
user.auth_services.destroy_all  # User will need to reconnect

Database Fixes

-- SQL commands for direct database fixes

-- Fix deleted user accounts
UPDATE user_accounts SET deleted = false WHERE user_id = 123 AND deleted = true;

-- Reset OTP codes that are stuck
UPDATE users SET verification_code = NULL, verification_code_sent_at = NULL 
WHERE verification_code_sent_at < NOW() - INTERVAL '1 hour';

-- Clean up orphaned refresh tokens
DELETE FROM refresh_tokens WHERE expires_at < NOW();

On this page