we-promise/sure · error · AuthenticationError
unauthorized
unauthorized
Error message
Authentication token missing in response
What it means
POST /auth/authenticate returned 200/201 with parseable JSON, but payload[:token] is blank. This only happens in username/document/password mode - when an api_token is configured, token_auth? is true and authenticate! is never called (token = api_token directly). It means authentication 'succeeded' at HTTP level but the response envelope does not contain a usable JWT.
Source
Thrown at app/models/provider/indexa_capital.rb:187
end
def token
@token ||= token_auth? ? @api_token : authenticate!
end
def authenticate!
response = self.class.post(
"#{base_url}/auth/authenticate",
headers: base_headers,
body: {
username: username,
document: document,
password: password
}.to_json
)
payload = handle_response(response)
jwt = payload[:token]
raise AuthenticationError.new("Authentication token missing in response", :unauthorized) if jwt.blank?
jwt
end
def handle_response(response)
case response.code
when 200, 201
begin
JSON.parse(response.body, symbolize_names: true)
rescue JSON::ParserError => e
raise Error.new("Invalid JSON in response: #{e.message}", :bad_response)
end
when 400
Rails.logger.error "IndexaCapital API: Bad request - #{response.body}"
raise Error.new("Bad request: #{response.body}", :bad_request)
when 401
raise AuthenticationError.new("Invalid credentials", :unauthorized)
when 403View on GitHub (pinned to e69894adb9)
Solutions
- Switch to api_token mode: generate a token from the Indexa dashboard/env and pass api_token: - it bypasses authenticate! entirely
- Log payload.keys on failure to detect envelope changes immediately
- Verify the triple manually (username / document / password) against a curl POST to /auth/authenticate
- If the shape changed, update the jwt = payload[:token] extraction to the new field and add a regression test
Example fix
# before Provider::IndexaCapital.new(username: u, document: d, password: p) # after Provider::IndexaCapital.new(api_token: credentials.api_token.presence || begin Provider::IndexaCapital.new(username: u, document: d, password: p).tap(&:token) end) # simpler: just prefer the pre-generated token Provider::IndexaCapital.new(api_token: settings.indexa_api_token)
Defensive patterns
Strategy: fallback
Validate before calling
# prefer token mode: it never calls authenticate!, so this error cannot occur client = if settings.indexa_api_token.present? Provider::IndexaCapital.new(api_token: settings.indexa_api_token.to_s.strip) else Provider::IndexaCapital.new(username: u, document: d, password: p) end
Type guard
def indexa_token_missing?(error)
error.is_a?(Provider::IndexaCapital::AuthenticationError) &&
error.message == "Authentication token missing in response"
end Try / catch
begin client.list_accounts rescue Provider::IndexaCapital::AuthenticationError => e raise unless e.message == "Authentication token missing in response" raise unless settings.indexa_api_token.present? # fallback available? Provider::IndexaCapital.new(api_token: settings.indexa_api_token).list_accounts end
Prevention
- Default to api_token mode; username/password mode is the fragile path
- Contract-test the auth endpoint (payload includes :token) in CI to catch envelope changes
- Log payload.keys when token extraction fails - it identifies renames instantly
- Rotate to a dashboard token as the immediate mitigation whenever this fires
When it happens
Trigger: Indexa changes the auth response shape (token renamed or nested); the account is in a state that returns 200 with an error-ish body but no token (e.g. re-login required, 2FA introduced); an intermediate gateway rewriting the response.
Common situations: Silent API contract changes after Indexa deploys, credentials technically valid but account locked pending re-acceptance of terms, password-mode integrations breaking while token-mode ones keep working.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- unauthorized
- Either API token or all three username/document/password cre
- invalid_import_record
- Lunchflow provider is not configured
- Mercury provider is not configured
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/e0a8a3e895c5c2ba.
Report an issue: GitHub.