I'm new to Spring boot and Spring Security. I have microservice project using Spring boot. And in my gateway app, I use OAuth2 for authentication. The authentication provider is from my organization and it is OIDC implementation.
I'm using oauth2 resource server to authenticate the bearer token, by configuring jwk-set-uri and jwk-set-uri properties.
spring-boot-starter-web => 2.6.7
spring-boot-starter-oauth2-resource-server => 2.6.7
spring-security => 5.6.3
application.properties
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://<org-auth-url>.com
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://<org-auth-url>/<jwk-uri>
With just above configuration, the authentication works fine. So I have not added any Security Config class also. But for authorization and other processing like to get user data in Controller, I need the user information and AD group details.
I have the user information endpoint URL. And when I test it in postman client, the response contains user information along with AD groups.
How to get the User details for Authorization?
Ok.
You've already added the required uri. Good.
Now you need to add some configuration:
#Configuration
#EnableWebSecurity
public class OAuth2ResourceServerSecurityConfiguration {
#Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}")
String jwkSetUri;
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(HttpMethod.GET,
///// more your requestMatchers /////
.anyRequest().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
return http.build();
}
#Bean
JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withJwkSetUri(this.jwkSetUri).build();
}
}
Now you should be able to receive jwt claims in your controllers with #AuthenticationPrincipal annotation.
#RestController
public class YourController {
#GetMapping("/")
public String doAnything(#AuthenticationPrincipal Jwt jwt) {
return jwt.getSubject();
}
}
Please add more info and I'll try to explain it better :-)
==== UPD ====
Really useful official manual on this.
Official code samples
I am developing RESTfull API for BFF(Bridge for front-end) application using Spring boot.
In my application it is not required to do any Authorization/Authentication and the only thing that i should do is to pass the JWT token from Authorization header to the HTTP client that calls another API. My goal was not to grant access to the endpoints of my application for the requests that don't contain Authorization header with token.
I reached my goal using the next config in WebSecurityConfigurerAdapter
#Configuration
#Order(1)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
.requestMatcher(new RequestHeaderRequestMatcher("Authorization"))
.authorizeRequests()
.antMatchers("/**").permitAll();
}
}
This configuration seems like working as needed but I get the empty response body if i don't provide the token in Authorization header:
Response image
Is it possible to configure somehow Spring Security to throw an exception and handle it with #ExceptionHandler in #ControllerAdvice or somewhere else to return the custom JSON object with error?
I am really new with Spring Security so please tell me if my configuration in WebSecurityConfigurerAdapter is not good enough for my purposes.
I'm trying to implement a spring-boot based REST service that should use Azure AD as an OAuth2 server for client authentication.
I registered two applicatons:
Mobile native app that is using as a client for my service
Rest-service as a backend.
All requests to the backend app should be authenticated through Azure AD
with using OAuth2 flow.
As an implementation of mobile app I'm using curl:
For obtaining a Bearer token I use https://login.microsoftonline.com/TENANT_ID/oauth2/token
curl -s -X POST https://login.microsoftonline.com/<TENANT_ID>/oauth2/token -d grant_type=password -d username=$USER_NAME -d password=$PASSWORD -d resource=$RESOURCE_ID -d client_id=$CLIENT_ID
where $USER_NAME and $PASSWORD are credetials of an Azure AD user, $RESOURCE_ID is a SID of my REST service and $CLIENT_ID is a SID of my mobile client for the REST serice.
Azure successfully returns JSON with token data.
My Oauth2 Config for Backend app:
#Configuration
#EnableResourceServer
public class OAuth2Config extends ResourceServerConfigurerAdapter {
#Bean
ResourceServerTokenServices resourceTokenServices() {
RemoteTokenServices tokenServices = new RemoteTokenServices();
tokenServices.setClientId(resourceId);
tokenServices.setClientSecret(/*I do not have it*/resourcePassword);
tokenServices.setCheckTokenEndpointUrl(/*I do not have it*/checkToken);
return tokenServices;
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenServices(resourceTokenServices());
resources.resourceId("rest_api");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").authenticated();
}
}
My REST controller:
#RestController
#RequestMapping("/data")
public class CustomerRestController {
#RequestMapping(method = RequestMethod.GET)
public SomeData getMyData(Principal principal){
System.out.println("RESOURCE WAS REQUESTED BY " + principal.getName());
return new SomeData(principal.getName());
}
}
But I didn't find in the endpoint list any URL that can be used by my REST service for checking a bearer token and obtaining user data from Azure AD.
Also, as I understand, it should be present some kind of credentials for my REST service for using Azure AD
How can I find required values or I'm going by a wrong way?
Azure AD uses JWT tokens for authorization, so I have to implement work with this type of tokens instead of checking a token on the server.
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.
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). =)