This question already has answers here:
Is it possible to get an access_token from Spring OAuth2 server without client secret?
(4 answers)
Closed 5 years ago.
I have a Spring Security OAuth 2.0 based application, configured with a JDBC and LDAP. As per the OAuth 2.0 specification, client secret must.
When I generate token by using following URL it generates token and works fine:
/oauth/token?grant_type=password&client_secret=test&client_id=test&username=test&password=test
and when I try to generate token without client_secret it gives:
401: Unauthorized
error_description: "Bad User Credentials"
but I want to generate token without client_secret like:
/oauth/token?grant_type=password&username=test&password=test
securityConfig.java:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity( prePostEnabled = true )
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {
private static final int EMBEDDED_LDAP_SERVER_PORT = 33388;
#Autowired
private UserAuthenticationProvider userAuthenticationProvider;
#Autowired
private LdapAuthenticationProvider ldapAuthenticationProvider;
#Autowired
private AuthTokenStore oAuthTokenStore;
#Autowired
private AuthDelegatingAuthenticationEntryPoint delegatingAuthenticationEntryPoint;
#Override
#Qualifier("authenticationManagerBean")
#Bean
protected AuthenticationManager authenticationManager() throws Exception {
return new ProviderManager(Arrays.asList((AuthenticationProvider) ldapAuthenticationProvider,userAuthenticationProvider));
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(delegatingAuthenticationEntryPoint);
}
#Bean
public ResourceServerTokenServices tokenService() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(oAuthTokenStore);
tokenServices.setReuseRefreshToken(true);
return tokenServices;
}
Unfortunately there is no easy way around your problem. Spring security interprets the standard very strict:
This is a quote from the OAuth2 spec, RFC 6749, section 4.3.2 (Resource Owner Password Credentials Grant - Access Token Request):
If the client type is confidential or the client was issued client
credentials (or assigned other authentication requirements), the
client MUST authenticate with the authorization server as described
in Section 3.2.1.
For spring security the password grant always falls into this category. Section 3.2.1 requires client ID and client password.
Also the spring security documentation goes this way:
28.1.1 Authorization Server
Unless you want to change the authentication logic of spring security's OAuth2 (not recommended) you are stuck.
From my point of view there is no problem. Client ID and password costs you nothing and bring a little bit more security to your application.
Related
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 am using Basic authorization in my Springboot project. Once I successfully login, I can inspect the backend webpage and see that there is an Authorization with the value Basic YmNyeX323G5yb2xsdGVjaC5jb206cyE5c2RzZA in the Network tab.
I can then use the value Basic YmNyeX323G5yb2xsdGVjaC5jb206cyE5c2RzZA to make requests via Postman to the server by setting that value as the Authorization value.
So, my question is, how can I return this value after logging in?
This is my SecurityConfiguration.java file:
#Configuration
#EnableConfigurationProperties
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
MongoUserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/", "/register").permitAll().anyRequest().authenticated()
.and().logout(logout -> logout
.permitAll()
.clearAuthentication(true)
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID"))
.httpBasic()
.and().sessionManagement().disable();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userDetailsService);
}
What would the end point look like that can return the basic authorization token mentioned above?
You don't, simply because that is not how Basic Authentication works.
The "token" (or rather header) is build on the client and sent to the server. The token is nothing more than the username and password separated by a : and then Base64 encoded. This is part of the HTTP standard and is just provided/done by the browser. So after entering the username / password your browser will create an Authorization consisting of that encoded token.
So you entered
username: bcry}nrolltech.com
password: s!9sdsd
which becomes YmNyeX323G5yb2xsdGVjaC5jb206cyE5c2RzZA ( bcry}nrolltech.com:s!9sdsd encoded in Base64). Which is send to the server as Authorization: YmNyeX323G5yb2xsdGVjaC5jb206cyE5c2RzZA. Which can use the reverse process to decode it. (Just as I did to retrieve the username/password).
The token is then decoded on the server, split on the ; and the seperated username and password are passed to the authentication mechanism for validation.
So it isn't the server that is generating this "token" nor is it a token but merely the encoded username and password.
There is more in-depth information on WikiPedia on how Basic Authentication works. RFC2617 and RFC7617 describes the protocol in great detail.
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
Object details = authentication.getDetails();
Try this, it might be useful, token in details
I am trying to familiarize myself with Spring Security, in particular migrating from Spring Security OAuth to Soring Security (as in the following example/guide https://github.com/spring-projects/spring-security/wiki/OAuth-2.0-Migration-Guide).
However, I am seeming to only get 403 Forbidden errors. I am accessing from Postman and am using my company's existing OAuth server. I am able to get a token from the auth server, so I know I have those credentials correct and I have verified what roles the OAuth user has.
I am using the following dependencies:
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
implementation 'org.springframework.boot:spring-boot-starter-web'
This is the simple endpoint I am attempting to access:
#RestController
public class AppController
{
#GetMapping("/hello")
public String hello()
{
return "hello";
}
}
This is my application.yml file:
spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: <<company-website-here>>/uaa/oauth/token_keys
And this is my security configuration class:
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.authorizeRequests()
.antMatchers(HttpMethod.GET, "/hello").hasRole("MY_ROLE")
.anyRequest().authenticated()
.and()
.oauth2ResourceServer().jwt();
}
}
I can't seem to figure out why I seem to only get 403 errors. I have also tried adding #EnableWebSecurity to the security config class, but that didn't make a difference. Adding the auth server URL explicitly to the server and/or manually creating a JwtDecoder didn't do the trick either; it appears the url is being automatically picked up from the yml file, based on its property name.
I am trying to move away from using the org.springframework.security.oauth.boot dependency and ResourceServerConfigurerAdapter.
I had to add my own converter like so:
private static class JwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken>
{
private final Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter;
public JwtAuthenticationConverter()
{
this.jwtGrantedAuthoritiesConverter = jwt -> jwt
.getClaimAsStringList("authorities")
.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
#Override
public final AbstractAuthenticationToken convert(#NonNull Jwt jwt)
{
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
return new JwtAuthenticationToken(jwt, authorities, jwt.getClaimAsString("client_id"));
}
}
Then had to add this to the main security config:
.jwtAuthenticationConverter(new JwtAuthenticationConverter());
There may be a couple of things happening.
As you're migrating to Spring Security 5, you may need to extract your authorities manually. Check this post and it's correct answer.
You are using hasRole function and this will append "ROLE_" before your authority/role. So if the role on your JWT token is not ROLE_JWT_ROLE you should use
hasTransaction.
I work with a web app that exposes a REST API to mobile apps. I upgraded my Spring Boot version from 1.5.3.RELEASE to 2.0.2.RELEASE and after fixing a few breaking changes I am facing one that I cannot solve.
I followed this Spring Boot 2.0 Migration Guide and Spring Boot Security 2.0 and also looked into Security changes in Spring Boot 2.0 M4.
The issue is that the app uses JWT authentication and there is an endpoint (/auth/login) accepts user credentials and generates a long-lived JWT in return.
There is a filter that examines the JWT token sent by the client and determines whether the client can access the requested resource.
Custom security config is like this:
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfiguration {
#Configuration
#Order(1)
public class AuthenticationConfiguration extends WebSecurityConfigurerAdapter {
// Some dependencies omitted
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
// we don't need CSRF because JWT token is invulnerable
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// don't create session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers(HttpMethod.GET, "/version/**").permitAll()
// Some more antMatchers() lines omitted
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated();
// Custom JWT based security filter
httpSecurity
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
// disable page caching
httpSecurity.headers().cacheControl();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
return new JwtAuthenticationTokenFilter(jwtTokenUtil);
}
}
#Configuration
#Order(2)
public class ClientVersionSupportConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry
.addInterceptor(versionCheckingFilter())
.addPathPatterns("/**")
.excludePathPatterns("/error"); // Some more endpoints omitted
}
#Bean
public VersionCheckingInterceptor versionCheckingFilter() {
return new VersionCheckingInterceptor();
}
}
}
Note the .antMatchers("/auth/**").permitAll() line. /auth endpoints should be accessible without JWT since the JWT has not yet been generated when the user has not yet logged in.
Before upgrading Spring Boot, it worked fine, now it is not working. Login attemps are rejected by the filter that checks the JWT. Looks like .permitAll() is not making the requests pass through. /version/** does not work either. Hitting it from the browser gives an error page.
I also tried to delete lines from the config until this remained:
httpSecurity
.authorizeRequests()
.antMatchers("/auth/**").permitAll()
It did not help. Could you please help with restoring the original behavior?
Do you have a base path for you api, e.g. /api ?
The server.contextPath default Spring property name has changed to server.servlet.context-path.
So if you use a default base path for you api, you won't find the endpoints where you expect them. Unless you update the property ;)
Can I chain multiple instances of AuthenticationEntryPoint in Spring Security 3.2.4?
I attempting to create the following scenario:
A certain URL is secured with Spring Security
The AuthenticationEntryPoint used is LoginUrlAuthenticationEntryPoint
An admin interface can spawn services under this URL
The admin can choose to secure these services with CLIENT-CERT
When a user attempts to access the secure URL:
If the path has been secured with CLIENT-CERT then authentication fails unless they have provided a valid certificate the corresponds to a user in the UserService. Standard Spring Security x509 authentication.
Once the user has been authentication as per the first point, or if the URL is not secured with CLIENT-CERT, they are directed to a FORM based authentication page.
Once they successfully authenticate with a username and password, they are directed to a landing page.
I am running on Tomcat 7.0.54 with clientAuth="want". This works perfectly in a "simple" Spring Security set up - i.e. with one WebSecurityConfigurerAdapter set to x509() and another set to formLogin() as per this example
So, I want a process flow something like the following:
I have had some success with dynamically changing the used authentication method by using a DelegatingAuthenticationEntryPoint but:
When using an AntPathRequestMatcher to map, say, /form/** to a LoginUrlAuthenticationEntryPoint the authentication servlet (/j_spring_security_check) gives a HTTP404 error.
When using an AntPathRequestMatcher to map, say, /cert/** to a Http403ForbiddenEntryPoint the user's details are not extracted from the presented client certificate so this gives a HTTP403 error.
I also cannot see how to force a user to authenticate twice.
I am using the java-config and not XML.
My code:
I have a DelegatingAuthenticationEntryPoint:
#Bean
public AuthenticationEntryPoint delegatingEntryPoint() {
final LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> map = Maps.newLinkedHashMap();
map.put(new AntPathRequestMatcher("/basic/**"), new BasicAuthenticationEntryPoint());
map.put(new AntPathRequestMatcher("/cert/**"), new Http403ForbiddenEntryPoint());
final DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(map);
entryPoint.setDefaultEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"));
return entryPoint;
}
And my configure
#Override
protected void configure(final HttpSecurity http) throws Exception {
defaultConfig(http)
.headers()
.contentTypeOptions()
.xssProtection()
.cacheControl()
.httpStrictTransportSecurity()
.addHeaderWriter(new XFrameOptionsHeaderWriter(SAMEORIGIN))
.and()
.authorizeRequests()
.accessDecisionManager(decisionManager())
.anyRequest()
.authenticated()
.and()
.httpBasic()
.authenticationEntryPoint(delegatingEntryPoint())
.and()
.sessionManagement()
.maximumSessions(1)
.sessionRegistry(sessionRegistry())
.maxSessionsPreventsLogin(true);
}
Where decisionManager() returns a UnanimousBased instance. sessionRegistry() returns a SessionRegistryImpl instance. Both methods are #Bean.
I add a custom UserDetailsService using:
#Autowired
public void configureAuthManager(
final AuthenticationManagerBuilder authBuilder,
final InMemoryUserDetailsService authService) throws Exception {
authBuilder.userDetailsService(authService);
}
And I have a custom FilterInvocationSecurityMetadataSource mapped using a BeanPostProcessor as in this example.
Chaining multiple entry points won't really work.
Your best option here might be to just customize the form-login process to check for the certificate if it's needed (before authenticating the user). That would probably simplify the configuration overall. It would really just be the same as a normal form-login setup.
The work done by the X509 filter is quite minimal. So for example, you could override the attemptAuthentication method, call super.attemptAuthentication() and then check that the certificate information matches the returned user authentication information.