Background
I have a Spring application with OAuth2 security.
I can easily obtain an OAuth Bearer token with the following request:
POST {{...}}/oauth/token
?grant_type=password
&client_id={{client_id}}
&username={{username}}
&password={{password}}
This returns a 200 OK
request with my access_token
in the response.
Problem
My problem is that one of my clients doesn't like the idea of sending plain text passwords in the query as a query parameter and they want to get the OAuth Bearer token using Basic Autentication.
However I can't make it work the following way:
POST {{...}}/oauth/token
Authorization: Basic base64encoded(username:password)
Content-Type: application/x-www-form-urlencoded
Request Body:
{
"grant_type": "password",
"client_id": {{client_id}}
}
It returns 401 Unauthorized
, and
{
"error": "unauthorized",
"error_description": "Bad credentials"
}
My applicationContext.xml
file looks like this:
<beans>
...
<!-- Definition of the Authentication Service -->
<security:http
pattern="/oauth/token"
create-session="stateless"
authentication-manager-ref="clientAuthenticationManager">
<security:anonymous enabled="false"/>
<security:http-basic entry-point-ref="clientAuthenticationEntryPoint"/>
<security:custom-filter ref="clientCredentialsTokenEndpointFilter" after="BASIC_AUTH_FILTER"/>
<security:access-denied-handler ref="oauthAccessDeniedHandler"/>
</security:http>
<!-- Protected resources -->
<security:http
pattern="/v3/**"
create-session="never"
entry-point-ref="oauthAuthenticationEntryPoint"
access-decision-manager-ref="accessDecisionManager">
<security:anonymous enabled="false"/>
<security:intercept-url pattern="/v3/**" access="IS_AUTHENTICATED_FULLY"/>
<security:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER"/>
<security:access-denied-handler ref="oauthAccessDeniedHandler"/>
</security:http>
<bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="typeName" value="Basic"/>
</bean>
<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"/>
<bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"/>
<bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="clientAuthenticationManager"/>
</bean>
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
<constructor-arg>
<list>
<bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter"/>
<bean class="org.springframework.security.access.vote.RoleVoter"/>
<bean class="org.springframework.security.access.vote.AuthenticatedVoter"/>
</list>
</constructor-arg>
</bean>
<security:authentication-manager id="clientAuthenticationManager">
<security:authentication-provider user-service-ref="clientDetailsUserService"/>
</security:authentication-manager>
<bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails"/>
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="myAuthProvider"/>
</security:authentication-manager>
<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore"/>
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="accessTokenValiditySeconds" value="86400"/>
<property name="tokenStore" ref="tokenStore"/>
<property name="supportRefreshToken" value="true"/>
<property name="clientDetailsService" ref="clientDetails"/>
</bean>
<bean id="oAuth2RequestFactory" class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory">
<constructor-arg ref="clientDetails"/>
</bean>
<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices">
<oauth:authorization-code/>
<oauth:implicit/>
<oauth:refresh-token/>
<oauth:password/>
</oauth:authorization-server>
<oauth:resource-server id="resourceServerFilter" token-services-ref="tokenServices"/>
<oauth:client-details-service id="clientDetails">
<oauth:client client-id="web-console"
authorized-grant-types="password,authorization_code,refresh_token,implicit,redirect"
authorities="ROLE_CLIENT, ROLE_TRUSTED_CLIENT"
scope="read,write,trust"
access-token-validity="86400"
refresh-token-validity="86400"/>
</oauth:client-details-service>
...
</beans>
Question
Ideally I should be able to call the /oauth/token
endpoint with Authorization Basic xxxxxxxxxxxxxxx
header, and I would be able to obtain the OAuth bearer token.