unable to get Oauth2 token from auth server - spring

I have an auth-server configured in spring with
clients
.inMemory()
.withClient("my-web-app")
.secret("my-web-app-secret")
.authorizedGrantTypes(//
"authorization_code",//
"refresh_token",//
"password"//
).scopes("openid")
Now I want to develop a command line application for the webapp. Hence I need to register one more client with a seperate client id and secret.
I have done something like this
.and()
.inMemory()
.withClient("my-cli")
.secret("my-cli-secret")
.authorizedGrantTypes("authorization_code","client_credentials")
.scopes("read","write","trust").authorities("ROLE_USER");
What I want to achieve is use simply provide the username/password and the client app should be able to get the auth token from the auth server.
What I have tried and understood is I should be using Resource Owner password Grant. I have to use the spring Oauth2restTemplate after this.
The problem in this configuration is when I m hitting the tokenuri i m getting
{
error: "unauthorized"
error_description: "Full authentication is required to access this resource"
}
and everytime it is hitting with the anonymous user.

If you want to user username/password for obtaining the access token you definitely need to use grant_type=password.
You also don't need to specify .inMemory() twice - just two clients with .and() between them.
So the configuration then have to be something like
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
// First client
.withClient("my-web-app")
.secret("my-web-app-secret")
.authorizedGrantTypes(//
"authorization_code",//
"refresh_token",//
"password"//
).scopes("openid")
// Second Client(CLI)
.and()
.withClient("my-cli")
.secret("my-cli-secret")
.authorizedGrantTypes("password")
.scopes("read", "write", "trust")
.authorities("ROLE_USER");
}
And other very important thing - you need to set the Authorization: header in the http request for the token. So the header should be
"Authorization: Basic " + Base64.encodeBase64String((clientId + ":" + clientSecret).getBytes())
This header is checked before the username\password and defines that the client(CLI in your case) is an authorized client (this can cause the error from your question).
That would be really good if you can add the code how exactly you use Oauth2RestTemplate

Related

401 on .permitAll() request in Spring Security

I have specified .permitAll() on the endpoint "/api/v2/user/login/**" but it still gives 401 when I don't give any authentication details in postman.
In fact, it's showing abnormal behaviour, below are my observations.
Gives 200 for any correct user details (regardless of role).
If I make a request with correct user details, it gives 200. If just after that request I do another request with incorrect password, it still gives 200. But incorrect username isn't tolerated.
Once it gives 401, it will keep giving 401 for all requests until I enter correct credentials.
CSRF is disabled so that shouldn't be an issue. I have tried playing with the order of permitAll request but that hasn't worked yet. Checkout the last antMatchers.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().
authorizeRequests().
antMatchers(HttpMethod.POST, "/api/v2/user/", "/api/v2/user", "/api/v2/user/change-role/**").hasAuthority("ROOT").
antMatchers(HttpMethod.GET, "/api/v2/user/", "/api/v2/user").hasAuthority("ROOT").
antMatchers(HttpMethod.POST, "/api/v1/customers/", "/api/v1/customers").hasAnyAuthority("ADMIN", "ROOT").
antMatchers(HttpMethod.GET, "/api/v1/customers/", "/api/v1/customers").hasAnyAuthority("EMPLOYEE", "ADMIN", "ROOT").
antMatchers(HttpMethod.POST, "/api/v2/user/login/**").permitAll().
anyRequest().
authenticated().
and().
httpBasic();
}
And here's the relevant controller method.
#RequestMapping(value = "/user/login", method = RequestMethod.POST)
public ResponseEntity<Boolean> loginUser(#RequestParam String username, #RequestParam String password){
return myUsersService.loginUser(username, password);
}
Any ideas are appreciated. Thanks!
Put antMatchers with permitAll first in the chain and remove /** to match the actual path you want to permit without auth.

BearerTokenAuthenticationFilter is allowing the request when Authorization token is not present in the request

I am extending the WebSecurityConfigurerAdapter and adding the OAuth2 authorization for the app to app communication. If I provide a wrong token or expired token, I am getting the failure response. But if I dont provide an Authorization header, then it is not failing.
http.authorizeRequests().
mvcMatchers(path).hasAnyAuthority(...)
.and()
.oauth2ResourceServer().
jwt();
I can see that the below code inside the BearerTokenAuthenticationFilter which may be the reason the no token scenario is not failing
if (token == null) {
filterChain.doFilter(request, response);
return;
}
How this scenario is usually handled.

An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: 406 Not Acceptable

I am trying use spirng-oauth2-client to connect my project with a third-party authentication server (following this instruction), ans right now when I run the application, after the authorization step, I am redirect back for my application, and a page with this error is displayed:
[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: 406 Not Acceptable: [Media is not supported]
In the comments for an answer in another Stack Overflow post, someone suggested that this is happening because "Spring makes the POST for the authenntication code with FORM parameters, whereas mercadolibre expects no body, only query parameters".
I have this configuration right now:
application.properties
spring.security.oauth2.client.registration.mercadolivre.provider=mercadolivre
spring.security.oauth2.client.registration.mercadolivre.client-id=...
spring.security.oauth2.client.registration.mercadolivre.client-secret=...
spring.security.oauth2.client.registration.mercadolivre.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.mercadolivre.redirect-uri={baseUrl}/login/oauth2/code/{registrationId}
spring.security.oauth2.client.provider.mercadolivre.authorization-uri=https://auth.mercadolivre.com.br/authorization
spring.security.oauth2.client.provider.mercadolivre.token-uri=https://api.mercadolibre.com/oauth/token
security.java
#Configuration
public class Security extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.defaultSuccessUrl("/");
}
}
Anyone knows how to change the Spring behavior to match th required for the service? I mean, making the POST for the authenntication code with no body, only query parameters?
For me the error was [invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: 401 Unauthorized
The issue was an expired/outdated Client Id and Secret. (I used the Client Id and Secret before and it worked)
This error relates to the response you get from the authentication server, either during client authentication or during fetching of the user-info. We can force the method for both requests to be BASIC instead of POST with these properties
spring.security.oauth2.client.registration.mercadolivre.client-authentication-method=BASIC
spring.security.oauth2.client.provider.mercadolivre.user-info-authentication-method=BASIC
In you controller tha you is redirected for, try to put consumes Json like this:
#GetMapping(value = "", consumes = MediaType.APPLICATION_JSON_VALUE)
public String indexPage() {
.
.
}
Or MediaType.ALL_VALUE

Spring OAuth2 - how to use the /oauth/authenticate endpoint?

So...I'm struggling to implement an authorization server with using Spring Boot OAuth2. For now I get a 403 response on:
GET oauth/authorize?username=demo&password=demo&client_id=demo&response_type=token
For the love of god, is the request okay? I would like to call this endpoint from a browser application and it should return an access_token and a refresh_token. Why do I need to provide a client_id for this? I'm on the edge of a mental breakdown because of this. How are you supposed to send a request to this endpoint?
The response is:
{
"timestamp": "2019-09-15T05:03:17.206+0000",
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/oauth/authorize"
}
Edit:
My simplified question would be this: Is there an endpoint that comes with #EnableAuthorizationServer, and it works as I am imagining it? You provide a username and a password, and it returns an access_token and a refresh_token.
The answer is yes the endpoint is POST /oauth/token
With parameters :
username -> YOUR_USERNAME
password -> YOUR_PASSWORD
grant_type -> password
The clientId and the secret must be send in the Authorization header.
ClientId is just for user to accessing the server. so first create a server and then try to create client:
in server add this code:
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("ClientId")
.secret("secret")
.authorizedGrantTypes("authorization_code")
.scopes("user_info")
.autoApprove(true);
}
Client : add you client id properly in spring property what you have kept in server

Spring Security Custom Authentication Filter and Authorization

I've implemented a custom authentication filter, and it works great. I use an external identity provider and redirect to my originally requested URL after setting my session and adding my authentication object to my security context.
Security Config
#EnableWebSecurity(debug = true)
#Configuration
class SecurityConfig extends WebSecurityConfigurerAdapter {
// this is needed to pass the authentication manager into our custom security filter
#Bean
#Override
AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean()
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
//.antMatchers("/admin/test").hasRole("METADATA_CURATORZ")
.antMatchers("/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(new CustomSecurityFilter(authenticationManagerBean()), UsernamePasswordAuthenticationFilter.class)
}
}
Filter logic
For now, my custom filter (once identity is confirmed) simply hard codes a role:
SimpleGrantedAuthority myrole = new SimpleGrantedAuthority("METADATA_CURATORZ")
return new PreAuthenticatedAuthenticationToken(securityUser, null, [myrole])
That authentication object (returned above) is then added to my SecurityContext before redirecting to the desired endpoint:
SecurityContextHolder.getContext().setAuthentication(authentication)
Controller Endpoint
#RequestMapping(path = '/admin/test', method = GET, produces = 'text/plain')
String test(HttpServletRequest request) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication()
String roles = auth.getAuthorities()
return "roles: ${roles}"
}
This endpoint then yields a response in the browser of:
"roles: [METADATA_CURATORZ]"
Great. So my authentication and applying a role to my user is working great.
Now, if I uncomment this line from the security config:
//.antMatchers("/admin/test").hasRole("METADATA_CURATORZ")
I can no longer access that resource and get a 403 -- even though we've already proven the role was set.
This seems totally nonsensical and broken to me, but I'm no Spring Security expert.
I'm probably missing something very simple. Any ideas?
Some questions I have:
Does my custom filter need to be placed before a specific built-in filter to ensure the authorization step occurs after that filter is executed?
When in the request cycle is the antMatcher/hasRole check taking place?
Do I need to change the order of what I am calling in my security configure chain, and how should I understand the config as I've currently written it? It's obviously not doing what I think it should be.
Does my custom filter need to be placed before a specific built-in filter to ensure the authorization step occurs after that filter is executed?
Your filter MUST come before FilterSecurityInterceptor, because that is where authorization and authentication take place. This filter is one of the last to be invoked.
Now as to where the best place for your filter might be, that really depends. For example, you really want your filter to come before AnonymousAuthenticationFilter because if not, unauthenticated users will always be "authenticated" with an AnonymousAuthenticationToken by the time your filter is invoked.
You can check out the default order of filters in FilterComparator. The AbstractPreAuthenticatedProcessingFilter pretty much corresponds to what it is you're doing - and its placement in the order of filters gives you an idea of where you could put yours. In any case, there should be no issue with your filter's order.
When in the request cycle is the antMatcher/hasRole check taking place?
All of this happens in FilterSecurityInterceptor, and more precisely, in its parent AbstractSecurityInterceptor:
protected InterceptorStatusToken beforeInvocation(Object object) {
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
.getAttributes(object);
if (attributes == null || attributes.isEmpty()) {
...
}
...
Authentication authenticated = authenticateIfRequired();
// Attempt authorization
try {
this.accessDecisionManager.decide(authenticated, object, attributes);
}
catch (AccessDeniedException accessDeniedException) {
...
throw accessDeniedException;
}
Extra information:
In essence, the FilterSecurityInterceptor has a ExpressionBasedFilterInvocationSecurityMetadataSource that contains a Map<RequestMatcher, Collection<ConfigAttribute>>. At runtime, your request is checked against the Map to see if any RequestMatcher key is a match. If it is, a Collection<ConfigAttribute> is passed to the AccessDecisionManager, which ultimately either grants or denies access. The default AccessDecisionManager is AffirmativeBased and contains objects (usually a WebExpressionVoter) that process the collection of ConfigAttribute and via reflection invokes the SpelExpression that corresponds to your "hasRole('METADATA_CURATORZ')" against a SecurityExpressionRoot object that was initialized with your Authentication.
Do I need to change the order of what I am calling in my security configure chain, and how should I understand the config as I've currently written it? It's obviously not doing what I think it should be.
No, there shouldn't be any issue with your filters. Just as a side note, in addition to what you have in your configure(HttpSecurity http) methods, the WebSecurityConfigurerAdapter you extend from has some defaults:
http
.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<>()).and()
.logout();
You can take a look at HttpSecurity if you want to see exactly what these do and what filters they add.
THE PROBLEM
When you do the following:
.authorizeRequests()
.antMatchers("/admin/test").hasRole("METADATA_CURATORZ")
... the role that is searched for is "ROLE_METADATA_CURATORZ". Why?
ExpressionUrlAuthorizationConfigurer's static hasRole(String role) method ends up processing "METADATA_CURATORZ":
if (role.startsWith("ROLE_")) {
throw new IllegalArgumentException(
"role should not start with 'ROLE_' since it is automatically inserted. Got '"
+ role + "'");
}
return "hasRole('ROLE_" + role + "')";
}
So your authorization expression becomes "hasRole('ROLE_METADATA_CURATORZ'" and this ends up calling the method hasRole('ROLE_METADATA_CURATORZ') on SecurityExpressionRoot, which in turn searches for the role ROLE_METADATA_CURATORZ in the Authentication's authorities.
THE SOLUTION
Change
SimpleGrantedAuthority myrole = new SimpleGrantedAuthority("METADATA_CURATORZ");
to:
SimpleGrantedAuthority myrole = new SimpleGrantedAuthority("ROLE_METADATA_CURATORZ");

Resources