From 2c4e70571672b06801b988ec1a55ca91f8850aeb Mon Sep 17 00:00:00 2001 From: unrealdreamz <132005717+unrealdreamz@users.noreply.github.com> Date: Mon, 18 May 2026 20:34:13 -0400 Subject: [PATCH 1/2] Surface OAuth token exchange errors --- spec/System/TestPoEAPIAuth_spec.lua | 76 +++++++++++++++++++++++++++++ src/Classes/ImportTab.lua | 4 +- src/Classes/PoEAPI.lua | 10 +++- 3 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 spec/System/TestPoEAPIAuth_spec.lua diff --git a/spec/System/TestPoEAPIAuth_spec.lua b/spec/System/TestPoEAPIAuth_spec.lua new file mode 100644 index 0000000000..5d73485461 --- /dev/null +++ b/spec/System/TestPoEAPIAuth_spec.lua @@ -0,0 +1,76 @@ +describe("PoEAPI auth", function() + local originalLaunchSubScript + local originalDownloadPage + local originalSubScripts + + before_each(function() + originalLaunchSubScript = LaunchSubScript + originalDownloadPage = launch.DownloadPage + originalSubScripts = launch.subScripts + launch.subScripts = { } + end) + + after_each(function() + LaunchSubScript = originalLaunchSubScript + launch.DownloadPage = originalDownloadPage + launch.subScripts = originalSubScripts + end) + + it("passes token exchange errors to the auth callback", function() + local authState + LaunchSubScript = function(_, _, _, authUrl) + authState = authUrl:match("state=([^&]+)") + return 123 + end + launch.DownloadPage = function(_, url, callback) + assert.are.equals("https://www.pathofexile.com/oauth/token", url) + callback(nil, "SSL connect error") + end + + local api = new("PoEAPI") + local callbackArgs + api:FetchAuthToken(function(response, errMsg, updateSettings) + callbackArgs = { + response = response, + errMsg = errMsg, + updateSettings = updateSettings, + } + end) + + assert.is_not_nil(authState) + assert.is_not_nil(launch.subScripts[123]) + launch.subScripts[123].callback("auth-code", nil, authState, 12345) + + assert.is_nil(callbackArgs.response) + assert.are.equals("SSL connect error", callbackArgs.errMsg) + assert.True(callbackArgs.updateSettings) + assert.is_nil(api.authToken) + end) + + it("reports OAuth state mismatches without exchanging a token", function() + LaunchSubScript = function() + return 123 + end + launch.DownloadPage = function() + error("token exchange should not run for mismatched OAuth state") + end + + local api = new("PoEAPI") + local callbackArgs + api:FetchAuthToken(function(response, errMsg, updateSettings) + callbackArgs = { + response = response, + errMsg = errMsg, + updateSettings = updateSettings, + } + end) + + assert.is_not_nil(launch.subScripts[123]) + launch.subScripts[123].callback("auth-code", nil, "wrong-state", 12345) + + assert.is_nil(callbackArgs.response) + assert.are.equals("OAuth state mismatch", callbackArgs.errMsg) + assert.True(callbackArgs.updateSettings) + assert.is_nil(api.authToken) + end) +end) diff --git a/src/Classes/ImportTab.lua b/src/Classes/ImportTab.lua index c54d92bf70..3229f58783 100644 --- a/src/Classes/ImportTab.lua +++ b/src/Classes/ImportTab.lua @@ -49,7 +49,7 @@ local ImportTabClass = newClass("ImportTab", "ControlHost", "Control", function( -- Stage: Authenticate self.controls.authenticateButton = new("ButtonControl", {"TOPLEFT",self.controls.characterImportAnchor,"TOPLEFT"}, {0, 0, 200, 16}, "^7Authorize with Path of Exile", function() - self.api:FetchAuthToken(function() + self.api:FetchAuthToken(function(_, errMsg) if self.api.authToken then self.charImportMode = "GETACCOUNTNAME" self.charImportStatus = "Authenticated" @@ -59,6 +59,8 @@ local ImportTabClass = newClass("ImportTab", "ControlHost", "Control", function( main.tokenExpiry = self.api.tokenExpiry main:SaveSettings() self:DownloadCharacterList() + elseif errMsg and errMsg ~= self.api.ERROR_NO_AUTH then + self.charImportStatus = colorCodes.NEGATIVE.."Authentication failed: "..errMsg else self.charImportStatus = colorCodes.WARNING.."Not authenticated" end diff --git a/src/Classes/PoEAPI.lua b/src/Classes/PoEAPI.lua index 8fc90874dd..c1685d5bbb 100644 --- a/src/Classes/PoEAPI.lua +++ b/src/Classes/PoEAPI.lua @@ -57,6 +57,7 @@ local function base64_encode(secret) return base64.encode(secret):gsub("+","-"):gsub("/","_"):gsub("=$", "") end +-- func callback(response, errorMsg, updateSettings) function PoEAPIClass:FetchAuthToken(callback) math.randomseed(os.time()) local secret = math.random(2^32-1) @@ -86,11 +87,16 @@ function PoEAPIClass:FetchAuthToken(callback) self.authToken = nil self.refreshToken = nil self.tokenExpiry = nil - callback(nil, self.ERROR_NO_AUTH, true) + callback(nil, errMsg or self.ERROR_NO_AUTH, true) return end if initialState ~= state then + ConPrintf("OAuth state mismatch during authentication") + self.authToken = nil + self.refreshToken = nil + self.tokenExpiry = nil + callback(nil, "OAuth state mismatch", true) return end local formText = "client_id=pob&grant_type=authorization_code&code=" .. code .. "&redirect_uri=http://localhost:" .. port .. "&scope=" .. table.concat(scopesOAuth, " ") .. "&code_verifier=" .. code_verifier @@ -100,7 +106,7 @@ function PoEAPIClass:FetchAuthToken(callback) self.authToken = nil self.refreshToken = nil self.tokenExpiry = nil - callback() + callback(nil, errMsg, true) return end local responseLua = dkjson.decode(response.body) From f61bd8223a74e1bf728ec7934ae084c36518dfab Mon Sep 17 00:00:00 2001 From: Wires77 Date: Thu, 21 May 2026 10:34:58 -0500 Subject: [PATCH 2/2] Fix LaunchSubScript to properly override the global function --- spec/System/TestPoEAPIAuth_spec.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/System/TestPoEAPIAuth_spec.lua b/spec/System/TestPoEAPIAuth_spec.lua index 5d73485461..c1f63ed258 100644 --- a/spec/System/TestPoEAPIAuth_spec.lua +++ b/spec/System/TestPoEAPIAuth_spec.lua @@ -4,21 +4,21 @@ describe("PoEAPI auth", function() local originalSubScripts before_each(function() - originalLaunchSubScript = LaunchSubScript + originalLaunchSubScript = _G.LaunchSubScript originalDownloadPage = launch.DownloadPage originalSubScripts = launch.subScripts launch.subScripts = { } end) after_each(function() - LaunchSubScript = originalLaunchSubScript + _G.LaunchSubScript = originalLaunchSubScript launch.DownloadPage = originalDownloadPage launch.subScripts = originalSubScripts end) - it("passes token exchange errors to the auth callback", function() + it("passes token exchange errors to the auth callback #auth", function() local authState - LaunchSubScript = function(_, _, _, authUrl) + _G.LaunchSubScript = function(_, _, _, authUrl) authState = authUrl:match("state=([^&]+)") return 123 end @@ -48,7 +48,7 @@ describe("PoEAPI auth", function() end) it("reports OAuth state mismatches without exchanging a token", function() - LaunchSubScript = function() + _G.LaunchSubScript = function() return 123 end launch.DownloadPage = function()