Compare commits

...

11 Commits

Author SHA1 Message Date
CI
0891706489 chore: release version v1.97.5 2026-03-26 01:02:39 +00:00
Dmitry Popov
7d720dcfb5 Merge branch 'main' of github.com:wanderer-industries/wanderer 2026-03-26 02:02:08 +01:00
Dmitry Popov
63b40b9c75 fix(core): Fixed character re-auth issues 2026-03-26 02:02:04 +01:00
CI
fc167fafaf chore: [skip ci] 2026-03-26 00:11:47 +00:00
CI
b9197880f0 chore: release version v1.97.4 2026-03-26 00:11:47 +00:00
Dmitry Popov
88f027facd Merge branch 'main' of github.com:wanderer-industries/wanderer 2026-03-26 01:11:01 +01:00
Dmitry Popov
d62ad709ab fix(core): Fixed character re-auth issues 2026-03-26 01:10:57 +01:00
CI
15aeb8eb85 chore: [skip ci] 2026-03-25 23:41:47 +00:00
CI
6970db438d chore: release version v1.97.3 2026-03-25 23:41:47 +00:00
Dmitry Popov
9ab7fcc46e fix(core): Fixed character re-auth issues 2026-03-26 00:41:07 +01:00
CI
931a8e629d chore: [skip ci] 2026-03-23 11:20:01 +00:00
6 changed files with 123 additions and 21 deletions

View File

@@ -2,6 +2,33 @@
<!-- changelog -->
## [v1.97.5](https://github.com/wanderer-industries/wanderer/compare/v1.97.4...v1.97.5) (2026-03-26)
### Bug Fixes:
* core: Fixed character re-auth issues
## [v1.97.4](https://github.com/wanderer-industries/wanderer/compare/v1.97.3...v1.97.4) (2026-03-26)
### Bug Fixes:
* core: Fixed character re-auth issues
## [v1.97.3](https://github.com/wanderer-industries/wanderer/compare/v1.97.2...v1.97.3) (2026-03-25)
### Bug Fixes:
* core: Fixed character re-auth issues
## [v1.97.2](https://github.com/wanderer-industries/wanderer/compare/v1.97.1...v1.97.2) (2026-03-23)

View File

@@ -897,20 +897,43 @@ defmodule WandererApp.Esi.ApiClient do
end
defp invalidate_character_tokens(character, character_id, expires_at, scopes) do
attrs = %{access_token: nil, refresh_token: nil, expires_at: expires_at, scopes: scopes}
with {:ok, _} <- WandererApp.Api.Character.update(character, attrs) do
WandererApp.Character.update_character(character_id, attrs)
# Skip invalidation if the character was recently re-authorized via SSO.
# This protects fresh tokens from being wiped by transient invalid_grant
# errors that can occur shortly after re-auth.
if WandererApp.Cache.lookup!("character:#{character_id}:reauth_grace", false) do
Logger.info(
"[ApiClient] Skipping token invalidation for #{character_id} - within re-auth grace period"
)
else
error ->
Logger.error("Failed to clear tokens for #{character_id}: #{inspect(error)}")
end
# Re-load from DB to avoid race with concurrent re-auth
case WandererApp.Api.Character.by_id(character_id) do
{:ok, current_character} ->
# Only invalidate if tokens haven't been refreshed since we started
if current_character.access_token == character.access_token do
attrs = %{access_token: nil, refresh_token: nil, expires_at: expires_at, scopes: scopes}
Phoenix.PubSub.broadcast(
WandererApp.PubSub,
"character:#{character_id}",
:character_token_invalid
)
with {:ok, _} <- WandererApp.Api.Character.update(current_character, attrs) do
WandererApp.Character.update_character(character_id, attrs)
else
error ->
Logger.error("Failed to clear tokens for #{character_id}: #{inspect(error)}")
end
Phoenix.PubSub.broadcast(
WandererApp.PubSub,
"character:#{character_id}",
:character_token_invalid
)
else
Logger.info(
"[ApiClient] Skipping token invalidation for #{character_id} - tokens were refreshed concurrently"
)
end
{:error, _} ->
Logger.error("Failed to load character #{character_id} for token invalidation")
end
end
:ok
end

View File

@@ -46,14 +46,18 @@ defmodule WandererApp.Ueberauth.Strategy.Eve do
|> with_param(:hl, conn)
|> with_state_param(conn)
opts = oauth_client_options_from_conn(conn, with_wallet, is_admin?)
WandererApp.Cache.put(
"eve_auth_#{params[:state]}",
[with_wallet: with_wallet, is_admin?: is_admin?],
[
with_wallet: with_wallet,
is_admin?: is_admin?,
tracking_pool: Keyword.get(opts, :tracking_pool)
],
ttl: :timer.minutes(30)
)
opts = oauth_client_options_from_conn(conn, with_wallet, is_admin?)
redirect!(conn, WandererApp.Ueberauth.Strategy.Eve.OAuth.authorize_url!(params, opts))
false ->

View File

@@ -10,6 +10,12 @@ defmodule WandererAppWeb.AuthController do
def callback(%{assigns: %{ueberauth_auth: auth, current_user: user} = _assigns} = conn, _params) do
active_tracking_pool = WandererApp.Character.TrackingConfigUtils.get_active_pool!()
Logger.info(
"[AuthController] SSO callback SUCCESS for eve_id=#{auth.info.email}, " <>
"has_token=#{not is_nil(auth.credentials.token)}, " <>
"has_refresh=#{not is_nil(auth.credentials.refresh_token)}"
)
character_data = %{
eve_id: "#{auth.info.email}",
name: auth.info.name,
@@ -40,8 +46,25 @@ defmodule WandererAppWeb.AuthController do
character
|> WandererApp.Api.Character.update(character_update)
Logger.info(
"[AuthController] Character #{character.id} tokens updated in DB, " <>
"access_token_present=#{not is_nil(character.access_token)}"
)
WandererApp.Character.update_character(character.id, character_update)
# Clear the invalid_grant counter so stale failures don't cause
# premature token invalidation after a successful re-auth
WandererApp.Cache.delete("character:#{character.id}:invalid_grant_count")
# Set a grace period to protect fresh tokens from being wiped by
# in-flight or immediately-subsequent invalid_grant errors
WandererApp.Cache.put(
"character:#{character.id}:reauth_grace",
true,
ttl: :timer.minutes(5)
)
# Update corporation/alliance data from ESI to ensure access control is current
update_character_affiliation(character)
@@ -96,7 +119,16 @@ defmodule WandererAppWeb.AuthController do
end
def callback(conn, _params) do
# This runs when Ueberauth auth FAILED — tokens are NOT updated
ueberauth_failure = conn.assigns[:ueberauth_failure]
Logger.warning(
"[AuthController] SSO callback FAILED - no ueberauth_auth in assigns. " <>
"Failure: #{inspect(ueberauth_failure)}"
)
conn
|> put_flash(:error, "Authorization failed. Please try again.")
|> redirect(to: "/characters")
end

View File

@@ -22,6 +22,11 @@ defmodule WandererAppWeb.CharactersLive do
"character:#{character_id}:corporation"
)
Phoenix.PubSub.subscribe(
WandererApp.PubSub,
"character:#{character_id}"
)
:ok = WandererApp.Character.TrackerManager.start_tracking(character_id)
end)
@@ -83,12 +88,11 @@ defmodule WandererAppWeb.CharactersLive do
{:ok, _} = WandererApp.MapCharacterSettingsRepo.untrack(settings)
end)
{:ok, updated_character} =
socket.assigns.characters
|> Enum.find(&(&1.id == character_id))
|> WandererApp.Api.Character.mark_as_deleted()
# Load character from DB instead of using plain map from assigns
{:ok, character} = WandererApp.Api.Character.by_id(character_id)
{:ok, _updated_character} = WandererApp.Api.Character.mark_as_deleted(character)
WandererApp.Character.update_character(character_id, updated_character)
WandererApp.Character.update_character(character_id, %{deleted: true, user_id: nil})
{:ok, characters} =
WandererApp.Api.Character.active_by_user(%{user_id: socket.assigns.user_id})
@@ -148,6 +152,18 @@ defmodule WandererAppWeb.CharactersLive do
{:noreply, socket |> assign(characters: characters |> Enum.map(&map_ui_character/1))}
end
@impl true
def handle_info(
event,
socket
)
when event in [:character_token_invalid, :token_updated] do
{:ok, characters} =
WandererApp.Api.Character.active_by_user(%{user_id: socket.assigns.user_id})
{:noreply, socket |> assign(characters: characters |> Enum.map(&map_ui_character/1))}
end
@impl true
def handle_info(
_event,

View File

@@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do
@source_url "https://github.com/wanderer-industries/wanderer"
@version "1.97.2"
@version "1.97.5"
def project do
[