使用Basic4Android通过Refresh Token获取新Token时遇503错误排查
问题:使用Refresh Token获取MS Graph新访问令牌时返回Error 503(AADSTS40008)
我在Basic4Android中编写了通过Refresh Token获取新访问令牌的代码,但始终返回Error 503,错误代码为AADSTS40008。已尝试更换不同端点和重定向URL,问题依旧。请求检查POST数据格式和请求端点是否正确。
返回的错误信息
{"error":"server_error","error_description":"AADSTS40008: There was an unexpected error from the external identity provider.\r\nTrace ID: 866cab93-5042-43d1-9331-fe5f9b005100\r\nCorrelation ID: 06250287-46ed-4511-9610-fea73d2ea737\r\nTimestamp: 2023-10-20 17:19:41Z","error_codes":[40008],"timestamp":"2023-10-20 17:19:41Z","trace_id":"866cab93-5042-43d1-9331-fe5f9b005100","correlation_id":"06250287-46ed-4511-9610-fea73d2ea737","error_uri":"https://login.microsoftonline.com/error?code=40008"}
现有代码
Sub RefreshAccessToken(refToken As String) As ResumableSub Log("RefreshAccessToken Called with refToken: " & refToken) If CheckConnection = False Then Log("No network connection. Aborted.") Return Null End If Dim params As Map params.Initialize params.Put("client_id", ConfigData.clientID) params.Put("client_secret", ConfigData.clientSecret) params.Put("refresh_token", refToken) params.Put("grant_type", "refresh_token") 'params.Put("scope", ConfigData.clientID & "/.default openid profile offline_access") params.Put("scope", "openid profile offline_access https://graph.microsoft.com/.default") params.Put("tenant", ConfigData.clientTenant) params.Put("redirect_uri", ConfigData.redirectURI) Log("Parameters set up.") Dim SB As StringBuilder SB.Initialize For Each key As String In params.Keys SB.Append(key).Append("=").Append(params.Get(key)).Append("&") Next SB.Remove(SB.Length - 1, SB.Length) Log("POST data prepared.") Try 'hc.Initialize("hc") Log("POST Data: " & SB.ToString) req.InitializePost2("https://login.microsoftonline.com/" & ConfigData.clientTenant & "/oauth2/v2.0/token", SB.ToString.GetBytes("UTF8")) req.SetContentType("application/x-www-form-urlencoded") hc.Execute(req, 1) Log("HTTP request executed.") 'Log("Full URL: " & "https://login.microsoftonline.com/" & ConfigData.clientTenant & "/oauth2/v2.0/token" & SB.ToString.GetBytes("UTF8")) Wait For hc_ResponseSuccess (Response As OkHttpResponse, TaskId As Int) Wait For hc_ResponseError (Response As OkHttpResponse, Reason As String, StatusCode As Int, TaskId As Int) Log("HTTP Response Status: " & Response & " Status Code =" & StatusCode & "Task ID = " & TaskId) Log("hc_ResponseSuccess triggered.") If TaskId = 1 Then Response.GetAsynchronously("response", File.OpenOutput(File.DirInternalCache, "response.txt", False), True, TaskId) Log("Response being gotten asynchronously.") Wait For response_StreamFinish (Success As Boolean, TaskId As Int) Log("response_StreamFinish triggered.") If Success Then Try Dim parser As JSONParser parser.Initialize(File.ReadString(File.DirInternalCache, "response.txt")) Dim root As Map = parser.NextObject Log("JSON parsed successfully.") Dim newAccessToken As String = root.Get("access_token") Dim newRefreshToken As String = root.Get("refresh_token") Dim newExpiresIn As Long = root.Get("expires_in") SaveTokens(newAccessToken, newRefreshToken, newExpiresIn, SubFolderID, MainFolderID) Log("Tokens saved successfully.") Return Success ' Successfully refreshed the token Catch Log("JSON Parsing failed: " & LastException.Message) Return Null ' Failed due to JSON parsing End Try Else Log("Error in response_StreamFinish: " & LastException.Message) Return False ' Failed due to stream finish End If Else Log("Unexpected TaskId: " & TaskId) Return Null ' TaskId doesn't match, ignore End If Catch Log("HTTP Request failed: " & LastException.Message) Return Null ' Failed due to HTTP request End Try End Sub
问题分析与修复建议
1. POST参数问题
- 冗余的
tenant参数:URL路径中已经包含租户ID,POST参数无需重复提交,直接删除params.Put("tenant", ConfigData.clientTenant)。 - 参数未做URL编码:代码直接拼接参数值,若
client_secret、refresh_token等包含&、=、%等特殊字符,会导致服务端解析参数错误,这是核心问题。需用B4A的StringUtils.EncodeUrl方法对每个参数值进行URL编码。 redirect_uri验证:若提交该参数,必须和获取refresh token时使用的redirect_uri完全一致;若是机密客户端(使用client_secret),可省略该参数。
2. HTTP请求逻辑问题
- 同时监听Success和Error事件:当前逻辑会导致事件触发混乱,应使用
Wait For (hc.Execute(req, 1)) Complete统一处理成功/失败情况,避免空指针异常。
修改后的代码片段
Sub RefreshAccessToken(refToken As String) As ResumableSub Log("RefreshAccessToken Called with refToken: " & refToken) If CheckConnection = False Then Log("No network connection. Aborted.") Return Null End If Dim params As Map params.Initialize params.Put("client_id", ConfigData.clientID) params.Put("client_secret", ConfigData.clientSecret) params.Put("refresh_token", refToken) params.Put("grant_type", "refresh_token") params.Put("scope", "openid profile offline_access https://graph.microsoft.com/.default") params.Put("redirect_uri", ConfigData.redirectURI) ' 确保和授权时的URI完全一致 Log("Parameters set up.") Dim SB As StringBuilder SB.Initialize Dim su As StringUtils For Each key As String In params.Keys ' 对参数值进行URL编码 Dim encodedValue As String = su.EncodeUrl(params.Get(key), "UTF8") SB.Append(key).Append("=").Append(encodedValue).Append("&") Next SB.Remove(SB.Length - 1, SB.Length) Log("POST Data: " & SB.ToString) Try req.InitializePost2("https://login.microsoftonline.com/" & ConfigData.clientTenant & "/oauth2/v2.0/token", SB.ToString.GetBytes("UTF8")) req.SetContentType("application/x-www-form-urlencoded") ' 统一处理请求结果 Wait For (hc.Execute(req, 1)) Complete (Success As Boolean, Response As OkHttpResponse, Reason As String, StatusCode As Int) Log("HTTP Response Status Code: " & StatusCode) If Success Then Response.GetAsynchronously("response", File.OpenOutput(File.DirInternalCache, "response.txt", False), True, 1) Wait For response_StreamFinish (StreamSuccess As Boolean, TaskId As Int) If StreamSuccess Then Try Dim parser As JSONParser parser.Initialize(File.ReadString(File.DirInternalCache, "response.txt")) Dim root As Map = parser.NextObject Dim newAccessToken As String = root.Get("access_token") Dim newRefreshToken As String = root.Get("refresh_token") Dim newExpiresIn As Long = root.Get("expires_in") SaveTokens(newAccessToken, newRefreshToken, newExpiresIn, SubFolderID, MainFolderID) Log("Tokens saved successfully.") Return True Catch Log("JSON Parsing failed: " & LastException.Message) Return Null End Try Else Log("Stream finish failed: " & LastException.Message) Return False End If Else Log("HTTP Request failed: " & Reason & ", Status Code: " & StatusCode) ' 输出完整错误响应便于排查 If Response <> Null Then Dim errorText As String = su.InputStreamToString(Response.ErrorStream, "UTF8") Log("Error Response: " & errorText) End If Return Null End If Catch Log("Exception: " & LastException.Message) Return Null End Try End Sub
额外检查项
- 确认
refresh_token未过期且有效,若令牌失效需重新引导用户授权。 - 检查Azure AD应用注册的权限配置,确保
offline_access权限已被授予。
内容的提问来源于stack exchange,提问作者Experior1
相关产品推荐
相关产品推荐

