I'm trying to implements OAUTH2 for a web applciation and need a simple OAUTH2 setup to secure rest APIs. One of the rest APIs provides login functionality where in it validates user credentails(username/password). My usecase is as follows:
1. Client for APIs is AngularJS for now but there might be other clients in the future
2.Users to login into the application with username and password i.e. UI layer (angular) to call login rest service and on successful authentication generate access token with an expiry time.
3. Clients to use this generated access token to consume other APIs in the application
Please suggest a simple OAUTH2 configuration for the above usecase?
Spring OAuth2 come with build-in endpoint to obtain an access_token via API with Resource Owner Password Grant
Here is an simple configuration for you
#Configuration
public class OAuth2ServerConfiguration {
private static final String RESOURCE_ID = "tonywebapp-restservice";
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends
AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Autowired
private UserApprovalHandler userApprovalHandler;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenStore(tokenStore())
.userApprovalHandler(userApprovalHandler)
.authenticationManager(authenticationManager)
.pathMapping("/oauth/error", "/oauth/error.html")
.pathMapping("/oauth/confirm_access", "/oauth/confirm_access.html")
;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("your_client_id_here")
.resourceIds(RESOURCE_ID)
.authorizedGrantTypes("password", "refresh_token")
.authorities("USER")
.scopes("read.some.thing", "write.some.thing", "any.thing.you.want")
.resourceIds(RESOURCE_ID)
.secret("your_secret_here");
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(tokenStore ());
return tokenServices;
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
}
But, it's recommendation that not using this grant for web base apps. Because you cannot protec your secret. I'm using implicit grant type for my AngularJS web apps.
Related
With Spring Security <5.2
In a legacy project, for a password grant scenario, I need to configure an authorization server.
Currently it is done extending AuthorizationServerConfigurerAdapter, and the authorization endpoints are configured overriding the configure(AuthorizationEndpointsServerConfigurer) method.
My problem is that this configurer takes one AuthenticationManager for the password grant, when I would need something like an AuthenticationManagerResolver (but I can't upgrade to 5.2) to be able to apply a different authentication depending on the incoming URL (an authentication manager for admin URLs, e.g. "/admin/**", and another one for non-admin).
How can I do that? I can change the approach, but again I can't upgrade.
You can try implementing your own DelegatingAuthenticationManager, inject the list of your AuthenticationManagers in it, and put your logic in authenticate method. E.g:
#Component("delegatingAM")
public class DelegatingAuthenticationManager implements AuthenticationManager {
private final List<AuthenticationManager> ams;
#Autowire
public DelegatingAuthenticationManager(#Qualifier("x") AuthenticationManager amX, #Qualifier("y") AuthenticationManager amY) {
this.ams = List.of(amX, amY); // Arrays.asList(amX, amY);
// you can inject variables for your conditions here
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (...) this.ams.get(0).authenticate(authentication);
if (...) this.ams.get(0).authenticate(authentication);
// Or you can loop over the list like AuthenticationManager is implemented with AuthenticatioProvider
}
}
Then inject it to AuthorizationServerConfigurerAdapter
#Configuration
#EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("delegatingAM")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.authenticationManager(this.authenticationManager)
}
...
}
Hope it will help, for the worse case, you could start to think about using many AuthorizationServerSecurityFilterChains, one AuthenticationManager for each. And based on the URL, direct the request to the right SecurityFilterChain.
Can someone please point me to some resources that can guide me on how I can implement a custom spring security authentication that first test the credentials of the found user on an ldap server first if the field for the ldap username exists for that user, and failing to authenticate (either because the ldap username doesn't exist or the password given doesn't authenticate the username on the server) there would attempt to authenticate against the local password kept hashed in the local database for the user.
Thank you.
It seems there aren't too many good answers to that specific question available.
While I have not stood up a full working LDAP example, there should be a good start to that part in the ldap sample that #Marcus linked in the comments. Having said that, you can easily register two authentication providers in the order you want, with the default DaoAuthenticationProvider being second:
#Configuration
public class SecurityConfiguration {
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// #formatter:off
return http
.authorizeRequests(authorizeRequests -> authorizeRequests
.anyRequest().authenticated()
)
.formLogin(withDefaults())
.authenticationProvider(ldapAuthenticationProvider())
.authenticationProvider(daoAuthenticationProvider())
.build();
// #formatter:on
}
#Bean
public AuthenticationProvider ldapAuthenticationProvider() {
LdapContextSource contextSource = null; // TODO See sample for example
return new LdapAuthenticationProvider(new BindAuthenticator(contextSource));
}
#Bean
public AuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService());
return authenticationProvider;
}
#Bean
public UserDetailsService userDetailsService() {
UserDetails userDetails = User.builder()
.username("user")
.password("{noop}password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(userDetails);
}
}
You will obviously want to provide your own user details which uses a database instead of in-memory. The username-password sample would get you started with that, and you can replace the MapCustomUserRepository in that example with e.g. a spring data #Repository.
I have a WebApp consisting of 2 parts.
One is with a frontend (Vaadin) where i want the user to be Logged-In via OAuth2. I then Check whether the user has a certain Role or not. --> If user opens the URL he shall be redirected to the OAuthLogin automatically. --> This is working with the #EnableOAuthSso.
Second Part is the REST-API of the Application, which is found by anything under /api/*. fE. /api/devices
should give me a list if the Get-Request has a valid Bearer-Token. If the GET Request has no Bearer-Token or a wrong Role (Authority) if want to get a 403.
Now this is my configuration:
#Configuration
#EnableOAuth2Sso
public class ProdWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final String ADMIN_ROLE= "role.global.admin";
private static final String READ_API_ROLE= "role.base.read.api";
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/login**", "/error**").permitAll()
.antMatchers("/*").hasAuthority(ADMIN_ROLE)
.antMatchers("/api/**").hasAnyAuthority(ADMIN_ROLE, READ_API_ROLE)
.and().logout().permitAll().logoutSuccessUrl(rootAuthUri + "/connect/endsession")
;
}
Now when opening for example /manageDevices in the Browser i get forced to be logged in via Auth-Code-Flow and everything works like as expected.
When i try to open /api/devices i also get forced to be logged in via Oauth. Even when i do send Http-Header with Authentication: Bearer xxxxx. Somehow it always forces me to the Login-Screen from my OAuth login.
application.properties these lines are defined:
base.rootauthuri=https://oauth2.mypage.ch
security.oauth2.client.clientId=client.base.parameters
security.oauth2.client.clientSecret=secret
security.oauth2.client.accessTokenUri=${base.rootauthuri}/connect/token
security.oauth2.client.userAuthorizationUri=${base.rootauthuri}/connect/authorize
security.oauth2.client.scope=openid,scope.base.parameters,role,offline_access
security.oauth2.client.clientAuthenticationScheme=form
security.oauth2.resource.userInfoUri=${base.rootauthuri}/connect/userinfo
How can i force everything under /api/* to not redirect to the AuthenticationForm but respond with 403 if no Bearer Token is sent. How can i make it to Check whether the Bearer-Token has Role "READ_API_ROLE" also.
I had the same question with SSO, I configured a ResourceServe for that:
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Autowired
private ResourceServerConfiguration configuration;
#PostConstruct
public void setSecurityConfigurerOrder() {
configuration.setOrder(3);
}
#Bean("resourceServerRequestMatcher")
public RequestMatcher resources() {
return new AntPathRequestMatcher("/api/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/v1/**") // this is free resource
.and().authorizeRequests()
.antMatchers("/api/**").permitAll() // This is free resource for mvc calls
// Usado para paths que necessitam de token bearer
.and().requestMatchers().antMatchers("/integration/**")
.and().authorizeRequests()
.antMatchers("/integration/**").authenticated(); // this is protected resource, it's necessary token
}
}
I not configure WebSecurityConfigurerAdapter in my project;
Check this:
Spring Boot 1.3.3 #EnableResourceServer and #EnableOAuth2Sso at the same time
https://www.baeldung.com/spring-security-oauth2-enable-resource-server-vs-enable-oauth2-sso
I have a Auth Server implementation with Spring Boot + OAuth2 + Login through Google. And a resource server for my backend data services. I have used JDBC token store. Everything works great. But I am having difficulty in understanding logout implementation. Currently, whenever user clicks on logout I just delete the token from the browser local storage but the session stays active in the Auth server hence I don't need to login again. What I want is whenever use clicks on logout I want to invalidate the session and force him to login again.
Is there any nice way to do this? I currently don't have any logout configuration in my Spring Boot Auth server configuration.
Thanks
Try registering a LogoutSuccessHandler to do that. Something along the lines of:
#Configuration
#EnableWebSecurity
#EnableResourceServer
public class SecurityConfig extends ResourceServerConfigurerAdapter {
#Bean
public DefaultTokenServices tokenServices() {
return new DefaultTokenServices();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("myResourceId");
resources.tokenServices(tokenServices());
}
#Override
public void configure(HttpSecurity http) throws Exception {
// configure http security here...
http.logout().logoutSuccessHandler(new SimpleUrlLogoutSuccessHandler() {
#Override
public void onLogoutSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) {
OAuth2AccessToken token = tokenServices().getAccessToken((OAuth2Authentication) authentication);
tokenServices().revokeToken(token.getValue());
}
});
}
}
I've been working on securing a Restful Service using Spring Security Oauth. I've been banging my head trying to secure the /oauth/token endpoint using SSL and only allowing for POST calls.
I'm using #EnableAuthorizationServer which states
Convenience annotation for enabling an Authorization Server (i.e. an
AuthorizationEndpoint and a TokenEndpoint) in the current application
context, which must be a DispatcherServlet context. Many features of
the server can be customized using #Beans of type
AuthorizationServerConfigurer (e.g. by extending
AuthorizationServerConfigurerAdapter). The user is responsible for
securing the Authorization Endpoint (/oauth/authorize) using normal
Spring Security features (#EnableWebSecurity etc.), but the Token
Endpoint (/oauth/token) will be automatically secured using HTTP Basic
authentication on the client's credentials. Clients must be registered
by providing a ClientDetailsService through one or more
AuthorizationServerConfigurers.
Which is great, but I can't seem to override the token endpoint piece or enforce POST-only calls, like with the intercept-url xml syntax
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore()
}
#Autowired
AuthenticationManager authenticationManager
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient('testApp')
.scopes("read", "write")
.authorities('ROLE_CLIENT')
.authorizedGrantTypes("password","refresh_token")
.secret('secret')
.accessTokenValiditySeconds(7200)
}
}
I secured my Resource server with
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Autowired
private RestAuthenticationEntryPoint authenticationEntryPoint;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.requiresChannel().anyRequest().requiresSecure()
.and()
.csrf()
.requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
.disable()
.headers()
.frameOptions().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/**").authenticated()
}
}
Is there a similar builder syntax for the Authorization Servers TokenEndpoint security that uses requiresChannel?
I ended up creating my own config using
org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerSecurityConfiguration
Since i'm using Spring boot I just autowired the SecurityProperties and added this line for the SSL on the Oauth endpoints
if (this.security.isRequireSsl()) {
http.requiresChannel().anyRequest().requiresSecure();
}
And for the POST requirement
http
.authorizeRequests()
.antMatchers(HttpMethod.POST,tokenEndpointPath).fullyAuthenticated()
.antMatchers(HttpMethod.GET,tokenEndpointPath).denyAll()
Afterwards removed the #EnableAuthorizationServer so it would use my config.