SSO登录用户获取Access Token时Authorize接口返回HTML错误求助
问题说明
你需要获取Access Token访问应用API,已经通过Postman可以正常拿到,现在尝试用Java Rest Assured复现「先拿授权码、再用授权码换Access Token」的流程,但调用Authorize接口时始终返回text/html类型的错误响应,错误信息如下:
<!DOCTYPE html> <html dir="ltr" class="" lang="en"> <head> <title>Sign in to your account</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=2.0, user-scalable=yes"> <meta http-equiv="Pragma" content="no-cache"> <meta http-equiv="Expires" content="-1"> <link rel="preconnect" href="https://aadcdn.msauth.net" crossorigin> <meta http-equiv="x-dns-prefetch-control" content="on"> <link rel="dns-prefetch" href="//aadcdn.msauth.net"> <link rel="dns-prefetch" href="//aadcdn.msftauth.net"> <meta name="PageID" content="ConvergedError" /> <meta name="SiteID" content="" /> <meta name="ReqLC" content="1033" /> <meta name="LocLC" content="en-US" /> <meta name="referrer" content="origin" /> <noscript> <meta http-equiv="Refresh" content="0; URL=" /> </noscript> <meta name="robots" content="none" /> <script type="text/javascript">//<![CDATA[ $Config={"strHeaderText":"Sign in","unsafe_strTopMessage":"Sorry, but we’re having trouble signing you in.","strMainMessage":"We received a bad request.","strAdditionalMessage":"","strServiceExceptionMessage":"AADSTS900144: The request body must contain the following parameter: \u0026#39;scope\u0026#39;.","strTraceId":"43b3c9f3-5d82-43e1-ae7e-fcb88fe24d00","iErrorCode":900144,"iHttpErrorCode":400,"iViewId":1,"urlCancel":"","strTimestamp":"2021-08-23T18:09:43Z","urlLearnMoreRiskyApp":"https://go.microsoft.com/fwlink/?linkid=2133457","iMaxStackForKnockoutAsyncComponents":10000,"fShowButtons":true,"fEnabledFlaggedSigninFlow":true,"urlCdn":"https://aadcdn.msauth.net/shared/1.0/","urlFooterTOU":"https://www.microsoft.com/en-US/servicesagreement/","urlFooterPrivacy":"https://privacy.microsoft.com/en-US/privacystatement","iPawnIcon":0,"sPOST_Username":"","sFTName":"flowToken","dynamicTenantBranding":null,"staticTenantBranding":null,"fEnableLayoutTemplates":true,"oAppCobranding":{},"iBackgroundImage":2,"fApplicationInsightsEnabled":false,"iApplicationInsightsEnabledPercentage":0,"urlSetDebugMode":"https://login.microsoftonline.com/common/debugmode","fEnableCssAnimation":true,"fAllowGrayOutLightBox":true,"fIsRemoteNGCSupported":true,"fUseSameSite":true,"uiflavor":1001,"fOfflineAccountVisible":false,"scid":1013,"hpgact":1800,"hpgid":1117,"pgid":"ConvergedError","apiCanary":"AQABAAAAAAD--DLA3VO7QrddgJg7Wevr4vgU5dSyj56ii8l46ni2Rlv-gbTV2fIzqJIZnmVvo36GN53V3xSnHh5tMvCuWoooKdZMo9EdfVDONwnh2GgbIDGAN_B1AjGh8Hp1P5BuOwdAgdkHrcxUmzKCivfO5qKbuG8mEyfK7w0wOcdk33wnPqkZU-N_Kh6GBxCHFt_5VMVU4LGhfBi9z9FKR9khgg94FHH3ozYq6Io8vIR1sDHjgyAA","canary":"/5Mr0VLUDgJZmi+BudaCdz5N6AwqT1rk13F4BqW281Y=8:1","correlationId":"103f8f0c-3657-447d-ba14-b899e94fa7aa","sessionId":"43b3c9f3-5d82-43e1-ae7e-fcb88fe24d00","locale":{"mkt":"en-US","lcid":1033},"slMaxRetry":2,"slReportFailure":true,"strings":{"desktopsso":{"authenticatingmessage":"Trying to sign you in"}},"enums":{"ClientMetricsModes":{"None":0,"SubmitOnPost":1,"SubmitOnRedirect":2,"InstrumentPlt":4}},"urls":{"instr":{"pageload":"https://login.microsoftonline.com/common/instrumentation/reportpageload","dssostatus":"https://login.microsoftonline.com/common/instrumentation/dssostatus"}},"browser":{"ltr":1,"_Other":1,"Full":1,"RE_Other":1,"b":{"name":"Other","major":-1,"minor":-1},"os":{"name":"Unknown","version":""},"V":-1},"watson":{"url":"/common/handlers/watson","bundle":"https://aadcdn.msauth.net/ests/2.1/content/cdnbundles/watson.min_ybdb1ixzkv-fkor2mu6q6w2.js","sbundle":"https://aadcdn.msauth.net/ests/2.1/content/cdnbundles/watsonsupportwithjquery.3.5.min_dc940oomzau4rsu8qesnvg2.js","fbundle":"https://aadcdn.msauth.net/ests/2.1/content/cdnbundles/frameworksupport.min_oadrnc13magb009k4d20lg2.js","resetErrorPeriod":5,"maxCorsErrors":-1,"maxInjectErrors":5,"maxErrors":10,"maxTotalErrors":3,"expSrcs":["https://login.microsoftonline.com","https://aadcdn.msauth.net/","https://aadcdn.msftauth.net/",".login.microsoftonline.com"],"envErrorRedirect":true,"envErrorUrl":"/common/handlers/enverror"},"loader":{"cdnRoots":["https://aadcdn.msauth.net/","https://aadcdn.msftauth.net/"],"logByThrowing":true},"serverDetails":{"slc":"ProdSlices","dc":"SEASLR1","ri":"SI2XXXX","ver":{"v":[2,1,11935,14]},"rt":"2021-08-23T18:09:43","et":0},"clientEvents":{"enabled":true,"telemetryEnabled":true,"useOneDSEventApi":true,"flush":60000,"autoPost":true,"autoPostDelay":1000,"minEvents":1,"maxEvents":1,"pltDelay":500,"appInsightsConfig":{"instrumentationKey":"69adc3c768bd4dc08c19416121249fcc-66f1668a-797b-4249-95e3-6c6651768c28-7293","webAnalyticsConfiguration":{"autoCapture":{"jsError":true}}},"defaultEventName":"IDUX_ESTSClientTelemetryEvent_WebWatson","serviceID":3},"fApplyAsciiRegexOnInput":true,"fBreakBrandingSigninString":true,"fFixIncorrectApiCanaryUsage":true,"fTrimChromeBssoUrl":true,"inlineMode":5}; //]]></script> </head> </html>
你编写的获取授权码的代码如下:
oAuthToken = given() .header("authorization", "Basic " + <encoded username and password>) .contentType(ContentType.URLENC) .formParam("response_type", "code") .queryParam("client_id", "<xyz client id>") .queryParam("redirect_uri", "<the call back url>") .queryParam("scope", "user.read") .post("/oauth2/v2.0/authorize") .then() .statusCode(200) .extract() .response();
你当前使用的可正常运行的Postman配置如下:
问题分析与解决方案
首先从错误提示AADSTS900144: The request body must contain the following parameter: 'scope'可以直接定位第一个错误:
- 你设置了请求的content-type为
application/x-www-form-urlencoded,但将client_id、redirect_uri、scope三个参数放在了查询参数(queryParam)中,服务端会从POST请求的表单体中读取参数,自然找不到必填的scope字段,直接报错。
其次你对OAuth2授权码流的响应逻辑理解有误:
- 授权码流的Authorize接口请求成功后,不会返回包含授权码的JSON响应,而是返回302重定向响应,授权码会附加在你配置的
redirect_uri的查询参数中,你当前校验响应状态码为200,本身就是错误的逻辑。 - Postman能正常拿到token,是因为Postman内置的OAuth2授权功能自动处理了登录跳转、重定向拦截提取授权码、调用token接口换token的全流程,不是直接调用Authorize接口就能拿到授权码。
如果是自动化测试场景不需要用户交互,更推荐直接使用密码流或者客户端凭据流,跳过拿授权码的步骤,直接调用/oauth2/v2.0/token接口拿token,适配性更高。
修正后的Authorize请求示例代码如下:
Response authResponse = given() .contentType(ContentType.URLENC) // 所有参数都放在formParam中,不要用queryParam .formParam("response_type", "code") .formParam("client_id", "<xyz client id>") .formParam("redirect_uri", "<the call back url>") .formParam("scope", "user.read") // 如果需要免登录,补充用户名密码参数 .formParam("username", "<your username>") .formParam("password", "<your password>") // 不要拦截重定向,方便拿到重定向地址 .redirects().follow(false) .post("/oauth2/v2.0/authorize") .then() // 成功响应是302不是200 .statusCode(302) .extract() .response(); // 从重定向地址中提取授权码 String redirectUrl = authResponse.getHeader("Location"); String authCode = redirectUrl.split("code=")[1].split("&")[0];
内容的提问来源于stack exchange,提问作者Bhuvaneshwari K
相关产品推荐
相关产品推荐

