2017-10-09 250 views
1

我寫 MS圖形的API Azure的廣告V2端點利用彈簧的OAuth2的SSO提供商。配置 - 錯誤AADSTS90014

我與實施和不斷的測試進展,但我偶然發現了由AAD這是我百思不得其解返回一個錯誤。畢竟,這應該都是純粹的標準OAuth 2流程。

我成功地在MS dev門戶上配置了我的應用程序,提供了一個localhost重定向URL(這是記錄,是唯一支持http方案,榮譽MS)。因此,當我調用http://localhost/myapp/auth/office365 Spring安全性成功攔截調用時,提供了一個正確的重定向到我的瀏覽器客戶端ID爲https://login.microsoftonline.com/common/oauth2/v2.0/authorize與預期的參數。

微軟表示同意畫面給我,然後我得到通過HTTP GET重定向回我的春節,安全的應用程序與預期的授權碼參數。

的問題是,當應用程序試圖協商給予授權代碼的承載令牌頭痛開始。 Spring Security調用POST到https://login.microsoftonline.com/common/oauth2/v2.0/token,但以401錯誤結束。

這裏是堆棧跟蹤

error="invalid_request", error_description="AADSTS90014: The request body must contain the following parameter: 'client_id'. 
Trace ID: 9acd2a10-1cfb-443f-9c57-78d608c00c00 
Correlation ID: bf063914-8926-4e8f-b102-7522d0e3b0af 
Timestamp: 2017-10-09 15:51:44Z", correlation_id="bf063914-8926-4e8f-b102-7522d0e3b0af", error_codes="[90014]", timestamp="2017-10-09 15:51:44Z", trace_id="9acd2a10-1cfb-443f-9c57-78d608c00c00" 
    at org.springframework.security.oauth2.common.exceptions.OAuth2ExceptionJackson2Deserializer.deserialize(OAuth2ExceptionJackson2Deserializer.java:100) 
    at org.springframework.security.oauth2.common.exceptions.OAuth2ExceptionJackson2Deserializer.deserialize(OAuth2ExceptionJackson2Deserializer.java:33) 
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001) 
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3072) 
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:235) 
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readInternal(AbstractJackson2HttpMessageConverter.java:215) 
    at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:193) 
    at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport$AccessTokenErrorHandler.handleError(OAuth2AccessTokenSupport.java:235) 
    at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700) 
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653) 
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:621) 
    at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.retrieveToken(OAuth2AccessTokenSupport.java:137) 
    at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.obtainAccessToken(AuthorizationCodeAccessTokenProvider.java:209) 
    at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainNewAccessTokenInternal(AccessTokenProviderChain.java:148) 
    at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:121) 
    at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221) 
    at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173) 
    at org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter.attemptAuthentication(OAuth2ClientAuthenticationProcessingFilter.java:105) 
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) 
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) 

我特地到Spring的安全實現自己身上找原因,​​。

碰巧錯誤消息error="invalid_request", error_description="AADSTS90014: The request body must contain the following parameter: 'client_id'.不言自明:MS Graph需要客戶端ID(它仍由基本認證頭提供)位於請求主體中。 停一會兒。我想使用普通的舊式Spring Security而不是第三方特定的JAR,以免污染我的類路徑。

展望春天的OAuth 2問題的Java源代碼是該死的清晰。 Spring僅在getParametersForAuthorizeRequest中使用客戶端ID,該ID用於生成重定向URL。當涉及到getParametersForTokenRequest時,表單中未指定客戶端ID。

問題:誰是正確的嗎?在獲得授權碼後,我如何告訴Spring MS 希望令牌請求中的客戶端ID?

回答

1

只是爲了澄清,你實際上沒有使用Microsoft Graph進行身份驗證。您實際上是針對Azure Active Directory進行身份驗證的。 Microsoft Graph API接受您最終將使用的不記名令牌,但它本身不會頒發訪問令牌。

您不清楚您用於授權碼流的哪個端點,AAD有兩個端點:v1v2。主要區別在於v2使用中央註冊並可以驗證工作/學校和個人帳戶。

無論端點如何,在請求訪問令牌時,您都需要在請求主體中提供clientid。實際上,您需要在正文中提供幾個值。還請注意,這些需要提供爲application/x-www-form-urlencoded

爲您提供V1端點(行只打破了可讀性):

grant_type=authorization_code 
&client_id={client-id} 
&code={authoization-code} 
&redirect_uri={redirect-uri} 
&client_secret={client-secret} 
&resource={resource-uri} 

第2版的端點幾乎是相同的,但使用scope而不是resource

grant_type=authorization_code 
&client_id={client-id} 
&code={authoization-code} 
&redirect_uri={redirect-uri} 
&client_secret={client-secret} 
&scope={scopes} 

OP的編輯

現在回到Spring Security。 Spring默認使用針對Azure AD的HTTP基本認證方案。在該方案中,客戶端ID和密鑰被編碼到HTTP頭中,然後表單只包含授權碼和狀態參數,所以我(OP,ndr)對AAD拒絕授權的原因感到困惑。

爲了將客戶端ID和祕密傳遞給表單,我們可以告訴Spring Security使用不同的支持的認證方案。 form認證方案會將客戶端ID和祕密推送到表單中。 下面的代碼工作並檢索訪問令牌。

<oauth2:resource 
    id="msAdAuthenticationSource" 
    client-id="${oauth.appId}" 
    client-secret="${oauth.appSecret}" 
    type="authorization_code" 
    authentication-scheme="form" 
    client-authentication-scheme="form" 
    use-current-uri="true" 
    user-authorization-uri="${oauth.authorizationUri}" 
    access-token-uri="${oauth.accessTokenUri}" 
    scope="${oauth.scopes}" 
    pre-established-redirect-uri="${oauth.redirectUri}" /> 

請注意兩個

authentication-scheme="form" 
client-authentication-scheme="form" 

問題解決了,很多更多的驚喜!

+1

在對Spring進行實驗之後,我得出了相同的結論。 Spring Security配置的一部分需要修改,以便通過表單傳遞客戶端ID。我將編輯您的答案以添加我的部分 –

+1

Marc,在編輯我的問題(顯示V2端點)和您的答案後,我已授予您接受的答案,並提供**配置使Spring Security可以正常工作。 –