I am trying to use spring-security-oauth2.0 with Java based configuration. My configuration is done, but when i deploy application on tomcat and hit the /oauth/token url for access token, Oauth generate the follwoing error:
<oauth>
<error_description>Full authentication is required to access this resource</error_description>
<error>unauthorized</error>
</oauth>
My configuration is on Git hub, please click on link
The code is large, so refer to git. I am using chrome postman client for send request. follwing is my request.
POST /dummy-project-web/oauth/token HTTP/1.1
Host: localhost:8081
Cache-Control: no-cache
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=abc%40gmail.com&client_secret=12345678
The error is just like, the URL is secure by Oauth, but in configuration, i give the all permission for access this URL. What actual this problem is?
The client_id and client_secret, by default, should go in the Authorization header, not the form-urlencoded body.
Concatenate your client_id and client_secret, with a colon between them: abc#gmail.com:12345678.
Base 64 encode the result: YWJjQGdtYWlsLmNvbToxMjM0NTY3OA==
Set the Authorization header: Authorization: Basic YWJjQGdtYWlsLmNvbToxMjM0NTY3OA==
By default Spring OAuth requires basic HTTP authentication. If you want to switch it off with Java based configuration, you have to allow form authentication for clients like this:
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.allowFormAuthenticationForClients();
}
}
The reason is that by default the /oauth/token endpoint is protected through Basic Access Authentication.
All you need to do is add the Authorization header to your request.
You can easily test it with a tool like curl by issuing the following command:
curl.exe --user abc#gmail.com:12345678 http://localhost:8081/dummy-project-web/oauth/token?grant_type=client_credentials
With Spring OAuth 2.0.7-RELEASE the following command works for me
curl -v -u abc#gmail.com:12345678 -d "grant_type=client_credentials" http://localhost:9999/uaa/oauth/token
It works with Chrome POSTMAN too, just make sure you client and secret in "Basic Auth" tab, set method to "POST" and add grant type in "form data" tab.
You should pre authenticate the token apis "/oauth/token"
extend ResourceServerConfigurerAdapter and override configure function to do this.
eg:
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers("/oauth/token").permitAll().
anyRequest().authenticated();
This is incredible but real.
csrf filter is enabled by default and it actually blocks any POST, PUT
or DELETE requests which do not include de csrf token.
Try using a GET instead of POST to confirm that this is it.
If this is so then allow any HTTP method:
#Throws(Exception::class)
override fun configure(http: HttpSecurity) {
/**
* Allow POST, PUT or DELETE request
*
* NOTE: csrf filter is enabled by default and it actually blocks any POST, PUT or DELETE requests
* which do not include de csrf token.
*/
http.csrf().disable()
}
If you are obtaining a 401 the most intuitive thing is to think that in the request you have No Auth or you are missing something in the headers regarding authorization.
But apparently there is an internal function that is filtering the HTTP methods that use POST and returns a 401. After fixing it I thought it was a cache issue with the status code but apparently not.
GL
Disable csrf
Demo
I had the same problem, but I solve this with the following class:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
#Configuration
#EnableWebSecurity
public class OAuthSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
#Override
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
If you have multiple services running on the same proxy server, make sure your proxy is correctly redirecting to servername/oauth/token rather than servername/servicename/oauth/token.
Related
I am working on a RestAPI endpoint for generating random values with Spring Boot and Bouncy Castle.
I also have basic Spring Security activated.
When i make a GET request with the right credentials, everything works fine and the random value gets returned, but if i change it to PostMapping with the exact same underlying code and make a POST request - i get an unauthorized response, even tho the credentials are right.
I am using Insomnia for making HTTP requests.
Has anyone experienced something similar and knows how to fix it?
Creating a Configuration Class for Spring Security solved the problem.
The following code worked:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
http
.csrf().disable();
return http.build();
}
}
I have a resource in an API for which URI is /product/{id} and doing three operations VIEW, GET, DELETE basse on HttpMethod.
How can I manage one user is allowed to only VIEW and admin is allowed to VIEW, GET, DELETE i.e. all options.
I had seen Keycloak Adapter Policy Enforcer but, I don't understand how it works.
I am not getting methods option in create permission.
Can somebody help me in implementing this or suggest some way to do it.
I guess you installed Keycloak for being able to control not just authentication, but also authorization. Then you don't need spring security at all. You need to enable authorization for your client and configure resources, policies and permissions using Keycloak admin console. Here is documentation
To be able to control your resources more granular use policy enforcers and map HTTP methods to scopes like described here: How to add HTTP methods in Keycloak resources for Authorization (Without adapters).
One of the good examples worth to look at is authz-spring-boot. It has complete authorization flow, but without method restriction that can be manually added.
You also can check how does your policy work using "Evalute" tab at Keycloak. This simulates client call to the resource and shows the result
What you need is spring security. You can add it to your project using:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
You could define your security settings like this (I'm assuming that other configuration is already done):
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// ...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
//HTTP Basic authentication
.httpBasic()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/product/**").hasRole("USER")
.antMatchers(HttpMethod.POST, "/product").hasRole("ADMIN")
.antMatchers(HttpMethod.PUT, "/product/**").hasRole("ADMIN")
.antMatchers(HttpMethod.PATCH, "/product/**").hasRole("ADMIN")
.antMatchers(HttpMethod.DELETE, "/product/**").hasRole("ADMIN")
.and()
.csrf().disable()
.formLogin().disable();
}
}
First of all the best suitable option is to use annotation based policy michanisam.
so before each and every rest service you need to write its access policy, for example :
#Secured("ROLE_VIEWER")
public String getUsername() {
SecurityContext securityContext = SecurityContextHolder.getContext();
return securityContext.getAuthentication().getName();
}
So as you can see the getUsername() method will be only allowed by the viewer.
#Secured("ROLE_ADMIN")
public boolean isValidUsername(String username) {
return userRoleRepository.isValidUsername(username);
}
So as you see the above method will only allow to be accessed by admin, the same annotation can be used for rest services as well. to use this facelity you need to integrate spring security with your spring boot application.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
For more detail you can check out this artical, but i do see this is the best way to control the data security and the service security in a spring boot application.
Reference : https://www.baeldung.com/spring-security-method-security
I also had the same problem. The following answer provides the explanation.
Thus, you can define the HTTP methods as scopes and check for permission via the following Keycloak API.
curl -X POST \
http://${host}:${port}/auth/realms/${realm}/protocol/openid-connect/token \
-H "Authorization: Bearer ${access_token}" \
--data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
--data "audience={resource_server_client_id}" \
--data "permission=Resource A#GET"
I've read a bunch of questions around this but none seem to be similar to my edge-case where I already have my JWT.
I'm using the Auth0 (note auth-zero, not Oauth) in my frontend to gain a JWT which is loaded with scopes and authentication for my backend. When I login to my frontend client I get a nice JWT with an access_token. If I copy that token I can make a direct curl request to my backend microservices
curl -X GET -H "Authorization: Bearer TOKEN_HERE" -H "Cache-Control: no-cache" "http://192.168.0.109:39885"
And this works as expected, I get a 200 response. Nice.
Now when I try the same curl request through my Zuul proxy I get a nasty 401.
The configuration I have for my gateway is:
#EnableHystrix
#EnableZuulProxy
#EnableEurekaClient
#SpringBootApplication
public class EdgeServiceApplication {
public static void main(String[] args) {
SpringApplication.run(EdgeServiceApplication.class, args);
}
}
Now reading the documentation and this conversation from the brilliant Dr Syer I know I need to allow the headers to go downstream which I've done:
zuul:
sensitiveHeaders:
routes:
instances:
path: /instances/**
serviceId: instance-service
restore:
path: /restore/**
serviceId: restore-service
Setting sensitiveHeaders empty should allow everything (for testing of course).
Looking further at the docs I see I need to add #EnableOAuth2Sso to my Zuul configuration. This is where I get confused/things break.
From what I know, #EnableOAuth2Sso is for generating and validating tokens. I don't want to do that. I already have my nice token ready for my microservice (which validates it down there).
How do I tell Zuul to not mess with my JWTs and just send them along?
I've solved this, there was to many things wrong with my code to detail but the gist of the issue was:
I was trying to send the entire JWT to the microservice when I should have just been sending the access_token
When I tried sending the access_token, ember-simple-auth0 actually sends the id_token by default
I needed to configure Zuul to pass CORS requests straight to the microservices
Once I started sending the access_token rather than the id_token it was easy to start debugging the issue.
To tell ember-simple-auth0 to use the access_token instead add a new authorizer with the following:
// app/authorizers/application.js
import Ember from 'ember';
import BaseAuthorizer from 'ember-simple-auth/authorizers/base';
const {
isPresent,
debug
} = Ember;
export default BaseAuthorizer.extend({
authorize(sessionData, block) {
let userToken = sessionData['accessToken'];
if (isPresent(userToken)) {
block('Authorization', `Bearer ${userToken}`);
} else {
debug('Could not find the authorization token in the session data for the jwt authorizer.');
}
}
});
Then remember to tell your adapter to use the new authorizer:
export default DS.JSONAPIAdapter.extend(DataAdapterMixin, {
authorizer: 'authorizer:application',
});
To forward CORS to your microservices use:
spring:
mvc:
dispatch-options-request: true
And making sure you're not stripping the headers from the request with:
zuul:
sensitiveHeaders:
routes:
instances:
path: /instances/**
serviceId: instance-service
stripPrefix: false
restore:
path: /restore/**
serviceId: restore-service
stripPrefix: false
Hopefully someone finds this useful.
I'm trying to use Spring Boot to create an OAuth2 authorization that only supports the client credentials flow. As I understand that flow, the client accesses the /oauth/token endpoint directly.
Is there a way to disable the /oauth/authorize endpoint in Spring Boot and allow direct access to /oauth/token without having to be fully authorized first?
#Configuration
#EnableAuthorizationServer
public class OAuth2Configuration extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// TODO: Is there something I can do here to disable /oauth/authorize?
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// client details configuration
}
}
I can't speak to disabling the authorize endpoint but you're right that you can go directly to the token endpoint with the client credentials flow. I'm probably restating something you already know but the credentials for a "client" (client_id/client_secret) are different from the credentials of a "user" (username/password). A "user" goes to the authorize endpoint so that the client can then get tokens from the token endpoint. A "client" (in the client credentials flow) provides the client credentials to the token endpoint directly. Do you need to disable the authorize endpoint?
So, for client_credentials flow, you don't need to go to authorize first (you don't need to disable it). Here's how you'd curl your token if your Spring Boot authorization server was on localhost:8080:
curl -H "Authorization: Basic d2VhcHA6" -X POST http://localhost:8080/oauth/token?grant_type=client_credentials
where d2VhcHA6 is the base64 encoding of your "client_id:client_secret"
I am trying to add OAuth to a rest service that I am developing with Spring framework. I am using annotation based configuration and spring-boot to get it running.
I have the following class in my project:
#Configuration
#EnableWebSecurity
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecuritySettings extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password("123").authorities("ROLE_USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and().httpBasic().and().csrf().disable();
}
}
and my authorization server configuration is as follows:
#Configuration
#EnableAuthorizationServer
public static class MyAuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("web")
.authorizedGrantTypes("password")
.authorities("ROLE_CLIENT","ROLE_TRUSTED_CLIENT","ROLE_USER")
.scopes("read", "write")
.resourceIds(RESOURCE_ID);
}
}
When I make a GET request to /oauth/token/ end point I am asked to enter HTTP basic credentials. When I try to login with the admin user then the following is logged
o.s.s.o.provider.endpoint.TokenEndpoint : Handling error: NoSuchClientException, No client with requested id: admin
Entering username as web works, but I don't know the password for it. A default password is logged but it doesn't work either.
Using default security password: f23087f8-58ce-e3d-bc62-58bf0963e75c
So what is this password? Where can I find it? How can I set it?
The API you are using is from this builder class.
The token endpoint is used by client applications to request access tokens for resources. It isn't used by browser end users. OAuth2 clients are usually allocated a "client secret" which they can use to authenticate at the endpoint, generally with Basic authentication as described in the OAuth 2.0 spec.
So to answer your specific question, you would use the "secret" method on the builder API, and use the value to authenticate as the client:
clients.inMemory().withClient("web")
.authorizedGrantTypes("password")
.secret("webclientsecret")
...
Also, the "password" grant means that the client requests tokens using an end users ID and password, just to make sure that's what you actually intend. It's not related to the password issue here.
This is the OAuth access token. It is based on user login and password and used to access protected resources.
URL "/oauth/token" is used to fetch access tokens instead of available Request Token. This request is digitally signed on the basis of Request Token secret.
The Oauth protocol uses this access tokens in this way:
Application-Consumer gets Request Token.
User is redirected on the Service Provider's site and authorizes Request Token there. (If authorization is made via Http basic, then you should add request header with name "Authorization" and value "Basic EncodeBase64("name:password")", where EncodeBase64 is a function, "name" and "password" are user name and user password.
Application-Consumer exchanges Request Token on Access Token.
Application-Consumer sends authorized requests to the service's API.
You can't find additional info in OAuth 2 Developers Guide and Spring Social Reference
I hope you've got answer to your question(or get closer to it). =)