Spring Security OAuth 2.0 without client_secret
I am developing a login system for my Android application using Spring Boot and Spring Security OAuth 2.0.
My starting point is the following demo repository: https://github.com/royclarkson/spring-rest-service-oauth . In the demo, you can find the following setting:
OAuth2 client:
clients
.inMemory()
.withClient("clientapp")
.authorizedGrantTypes("password", "refresh_token")
.authorities("USER")
.scopes("read", "write")
.resourceIds(RESOURCE_ID)
.secret("123456");
The way to get the access token in its tests:
private String getAccessToken(String username, String password) throws Exception {
String authorization = "Basic " + new String(Base64Utils.encode("clientapp:123456".getBytes()));
String content = mvc
.perform(
post("/oauth/token")
.header("Authorization", authorization)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("username", username)
.param("password", password)
.param("grant_type", "password")
.param("scope", "read write")
.param("client_id", "clientapp")
.param("client_secret", "123456"))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString();
return content.substring(17, 53);
}
Every test in the project works fine, but I want to do things differently and I am having problems with it. As you can see, the demo client detects client_secret
(which is also used in tests), but is client_secret
really useless in the Android environment, I cannot guarantee its "privacy".
See https://apigility.org/documentation/auth/authentication-oauth2 :
If we are using a public client (this is the case by default when no secret is associated with the client), you can omit the client_secret value;
and see http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-2.1 :
Public: Clients unable to maintain the confidentiality of their credentials (for example, clients running on a device used by the resource owner, such as an installed native application or a browser-based website application) and the inability of a secure client to authenticate by any other means.
So, I did key deletion in client config:
clients
.inMemory()
.withClient("books_password_client")
.authorizedGrantTypes("password", "refresh_token")
.authorities("USER")
.scopes("read", "write")
.resourceIds(RESOURCE_ID);
Also adapted the method getAccessToken(...)
:
private String getAccessToken(String username, String password) throws Exception {
String authorization = "Basic " + new String(Base64Utils.encode("books_password_client:123456".getBytes()));
String content = mvc
.perform(
post("/oauth/token")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("username", username)
.param("password", password)
.param("grant_type", "password")
.param("client_id", "books_password_client"))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString();
return content.substring(17, 53);}
}
But when I use this new setting, my tests fail, I cannot get the access token, I keep getting HTTP 401 Unauthorized error.
source to share
This is what I am working on:
Response response =
given().
auth()
.preemptive().basic("clientapp", "")
.formParam("username", username)
.formParam("password", password)
.formParam("grant_type", "password")
.formParam("scope", "read%20write")
when()
.post("/oauth/token").
then()
.extract().response();
source to share