Module: Lich::Common::Authentication

Defined in:
documented/common/authentication/cli.rb,
documented/common/authentication/gui.rb,
documented/common/authentication/eaccess.rb,
documented/common/authentication/entry_store.rb,
documented/common/authentication/launch_data.rb,
documented/common/authentication/cli_password.rb,
documented/common/authentication/authenticator.rb,
documented/common/authentication/login_helpers.rb

Overview

Handles authentication processes within the CLI module.

Defined Under Namespace

Modules: CLI, CLIPassword, EAccess, EntryStore, GUI, LaunchData, LoginHelpers Classes: FatalAuthError

Constant Summary collapse

MAX_AUTH_RETRIES =

Retry configuration for transient SSL/network errors These errors are often temporary and resolve on retry:

  • SSL_read: unexpected eof while reading (server closed connection)
  • Connection reset by peer
  • Connection timed out
3
AUTH_RETRY_BASE_DELAY =

seconds, doubles each retry: 5s, 10s, 20s

5
FATAL_ERROR_CODES =

Known fatal error codes that should not be retried REJECT = bad credentials, NORECORD = account not found, INVALID = invalid request PASSWORD = wrong password, CHARACTER_NOT_FOUND = character not in account

%w[REJECT NORECORD INVALID PASSWORD CHARACTER_NOT_FOUND].freeze

Class Method Summary collapse

Class Method Details

.authenticate(account:, password:, character: nil, game_code: nil, legacy: false) ⇒ Boolean

Authenticates a user with the provided credentials.

Parameters:

  • account (String)

    the account name

  • password (String)

    the account password

  • character (String, nil) (defaults to: nil)

    optional character name for authentication

  • game_code (String, nil) (defaults to: nil)

    optional game code for authentication

  • legacy (Boolean) (defaults to: false)

    whether to use legacy authentication method

Returns:

  • (Boolean)

    true if authentication is successful

Raises:

  • FatalAuthError if a fatal authentication error occurs



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'documented/common/authentication/authenticator.rb', line 35

def self.authenticate(account:, password:, character: nil, game_code: nil, legacy: false)
  with_retry do
    if character && game_code
      EAccess.auth(
        account: ,
        password: password,
        character: character,
        game_code: game_code
      )
    elsif legacy
      EAccess.auth(
        account: ,
        password: password,
        legacy: true
      )
    else
      EAccess.auth(
        account: ,
        password: password
      )
    end
  end
end

.with_retry { ... } ⇒ Object

Retries the given block of code for transient authentication errors.

Yields:

  • block of code to execute

Returns:

  • (Object)

    the result of the block if successful

Raises:

  • FatalAuthError if a fatal authentication error occurs

  • StandardError if all retries are exhausted



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'documented/common/authentication/authenticator.rb', line 65

def self.with_retry
  last_error = nil

  MAX_AUTH_RETRIES.times do |attempt|
    begin
      result = yield

      # Success - log if this was a retry
      if attempt.positive?
        Lich.log "info: Authentication succeeded on attempt #{attempt + 1}"
      end

      return result
    rescue EAccess::AuthenticationError => e
      # Check if this is a fatal auth failure
      if FATAL_ERROR_CODES.any? { |code| e.error_code&.include?(code) }
        Lich.log "error: Authentication fatally failed: #{e.message}"
        raise FatalAuthError, e.message
      end

      # Transient auth error - allow retry
      last_error = e

      if attempt < MAX_AUTH_RETRIES - 1
        delay = AUTH_RETRY_BASE_DELAY * (2**attempt)
        Lich.log "warn: Authentication attempt #{attempt + 1}/#{MAX_AUTH_RETRIES} failed: " \
                 "#{e.message}, retrying in #{delay}s..."
        sleep(delay)
      end
    rescue FatalAuthError
      # Don't retry fatal auth failures - re-raise immediately
      raise
    rescue StandardError => e
      last_error = e

      if attempt < MAX_AUTH_RETRIES - 1
        delay = AUTH_RETRY_BASE_DELAY * (2**attempt)
        Lich.log "warn: Authentication attempt #{attempt + 1}/#{MAX_AUTH_RETRIES} failed: " \
                 "#{e.message}, retrying in #{delay}s..."
        sleep(delay)
      end
    end
  end

  # All retries exhausted - re-raise the last error
  Lich.log "error: Authentication failed after #{MAX_AUTH_RETRIES} attempts: #{last_error&.message}"
  raise last_error
end