How does spring sso authentication with oauth2 work? - spring-security

I've set up 2 oauth2 client Spring Boot 2 web applications running on different ports and an authorization server. If I authenticate on one web application, I can then go and access a secured resource on the other. Brilliant, SSO in action!
But looking at the network traffic, I can't see any Bearer tokens on headers, only some SESSIONID related cookies. The use of HTTP sessions got me worrying that horizontal scaling could be an issue. Then I realized that the second application is working and authenticating somehow.
So what is the browser passing on the call to the 2nd web application that enables it to use the existing authentication? Are there any scaling concerns due to the use of http session by spring security.
thanks
ui oauth2 client application.yml (using spring boot 2 security's oauth2 client implementation)
spring:
profiles: oauth2-security
security:
oauth2:
client:
registration:
myoauth:
client-id: myoauth-trusted-client
client-secret: ...
authorization-grant-type: authorization_code
redirect-uri-template: http://localhost:${server.port}/ui/login/oauth2/code/myoauth
provider:
myoauth:
authorization-uri: http://localhost:8081/auth/oauth/authorize
token-uri: http://localhost:8081/auth/oauth/token
user-info-uri: http://localhost:8081/auth/user_info
user-info-authentication-method: header
user-name-attribute: name
The authz server. using the shim jar spring-security-oauth2-autoconfigure
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private AuthenticationManager authenticationManager;
public AuthorizationServerConfig(AuthenticationConfiguration authenticationConfiguration) throws Exception {
this.authenticationManager = authenticationConfiguration.getAuthenticationManager();
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.authenticationManager(this.authenticationManager) //for use with password grant type
.authorizationCodeServices(new InMemoryAuthorizationCodeServices()); //for use with authorization_code grant type
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("myoauth-trusted-client")
.authorizedGrantTypes("authorization_code")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.secret("{bcrypt}" + new BCryptPasswordEncoder().encode("..."))
.scopes("all")
.autoApprove(true)
.redirectUris("http://localhost:8082/ui/login/oauth2/code/myoauth", "http://localhost:8083/ui/login/oauth2/code/myoauth").and()
.withClient("myoauth-client-with-secret")
.authorizedGrantTypes("password", "client_credentials")
.authorities("ROLE_CLIENT")
.scopes("read")
.secret("{bcrypt}" + new BCryptPasswordEncoder().encode("..."))
;
}
}

Related

Spring Cloud Security: How to support multiple Identity Provider for Oauth2 Resource Server / OAuth2 Authorization Server?

I want to build a spring cloud infrastructure with several oauth2 resource servers supporting multiple identity provider (like google, facebook, github, ....) and also one self implemented authorization mechanism.
Oauth2 Resource Server example
Security config
#Configuration
#EnableWebSecurity
#EnableResourceServer
public class Oauth2ResourceServerConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated();
}
}
application.yml
security:
oauth2:
resource:
user-info-uri: https://api.github.com/user
As you can see this example is using github as identity provider and its working fine.
Oauth2 Authorization Server example:
Config
#Configuration
#EnableAuthorizationServer
public class Oauth2AuthorizationServerConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Override
public void init(final AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("{noop}user").roles("USER")
.and()
.withUser("admin").password("{noop}admin").roles("USER", "ADMIN");
}
#Override
public void configure(final AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
}
}
application.yml
security:
oauth2:
client:
client-id: user
client-secret: user
authorized-grant-types: password,client_credentials,authorization_code,refresh_token
scope: read,write
I can change my oauth2 resource server to use the authorization server:
security:
oauth2:
resource:
#user-info-uri: https://api.github.com/user
user-info-uri: http://${AUTH_HOST:localhost}:${AUTH_PORT:9000}/user
And it works just fine.
But what do I have to do if I now want to use both of them, github and my own authorization server?
Do I simply need a different configuration in my Oauth2 resource server to provide multiple user-info-uri or do I have to do more?
Can I extend my own authorization service to support github & co?
I would prefer not to use Auth0 and Co because I really dont like to outsource the most important part of my application: security. Even though I would like to try it. But I could not find any working example so far for auth0 + spring cloud gateway + token authentication for all underlying services.

Should the OAuth2 Token Endpoint require authentication?

I'm using Spring Security OAuth2 to create my own authorization server. In my case I want to enable a Angular client (SPA) to use the Authorization Code Grant.
The client can use the oauth/authorize endpoint, the user can log in and the browser is redirect to the SPA. Now the client wants to get the token via oauth/token. But this endpoint is secured and needs client id and client secret. The security is enabled by default in Spring.
In the docs I could find the following:
The token endpoint is protected for you by default by Spring OAuth in the #Configuration support using HTTP Basic authentication of the client secret. This is not the case in XML (so it should be protected explicitly).
As far as I know there shouldn't be a client secret used in public clients. But that means, that the oauth/token endpoint should not be secure.
Question: Is it a good practice to disable auth for oauth/token? If not, how should I solve this?
This is my WebSecurityConfig:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
#Override
protected UserDetailsService userDetailsService() {
// WARN: Do not use the default password encoder in production environments!
return new InMemoryUserDetailsManager(
User.withDefaultPasswordEncoder()
.username("user-a")
.password("password")
.roles("USER_ROLE")
.build()
);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http = http
.requiresChannel()
.anyRequest()
.requiresSecure()
.and()
.cors()
.and()
.authorizeRequests()
.antMatchers("/.well-known/**")
.permitAll()
.and();
super.configure(http);
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(singletonList("*"));
configuration.setAllowedMethods(asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}

Spring Boot as a resource server (JWK validation) & Angular/Cordova as front end - OAuth2 for social Login (Facebook & Google) support

I am stuck with the implementation of spring boot as a resource server for multiple authorization servers for validating the access/id tokens provided by authorization servers (such as google, facebook via front end libraries). Here is the architecture I am looking for the below desired flow as a working model.
Desired Architecture Image - Click here
what I implemented so far: I used a library angularx-social-login on angular 8 to get the required tokens google. I was able to pass the token back to the backend resource server to validate the token with google and authorize. Below is the code snippets.
Property File:
google:
client-id: xyz
iss: accounts.google.com
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://accounts.google.com
jwk-set-uri: https://accounts.google.com/.well-known/openid-configuration
Security Config Snippet
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuer;
#Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}")
private String jwkSetUri;
#Value("${google.client-id}")
private String clientId;
#Value("${google.iss}")
private String iss;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.anyRequest().authenticated().and()
.oauth2ResourceServer()
.jwt().decoder(jwtDecoder());
}
#Bean
JwtDecoder jwtDecoder() {
NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder)
JwtDecoders.fromOidcIssuerLocation(issuer);
OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(clientId);
OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(iss);
OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>
(withIssuer, audienceValidator);
jwtDecoder.setJwtValidator(withAudience);
return jwtDecoder;
}
}
The above snippet works for one authorization server (google in this case) but
below are my issues
How do Intercept the code to validate if the user exists in our DB first?
How do I add support for another authorization server like facebook?

Spring OAUTH2 for an angular web applciation

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.

Spring Oauth 2 SSO, Zuul and OpenAM integration

Introduction, Requirements:
right now i am writing a Single Page Application with AngularJS which talks to a Spring REST API. For security purposes I would like to setup a reverse proxy with zuul which proxies every request to the API and verifies that the user is authenticated. Also, if the user is not authenticated he should be redirected to an OpenAM instance (functioning as OAuth 2 Authorization Server). If the user is authenticated the request should be forwarded to the API with a Json Web Token (JWT) in the Header, containing at least the LDAP groups of the User.
In short I would like to have something like a API Gateway similar to the solution in this tutorial: https://spring.io/blog/2015/02/03/sso-with-oauth2-angular-js-and-spring-security-part-v
Status quo
I setup the Spring Cloud Security and Zuul with the following config:
server:
port: 9000
spring:
oauth2:
sso:
home:
secure: false
path: /,/**/*.html
client:
accessTokenUri: http://openam.example.org:8080/OpenAMTest/oauth2/access_token
userAuthorizationUri: http://openam.example.org:8080/OpenAMTest/oauth2/authorize
clientId: bearer-client
clientSecret: clientsecret
scope: openid profile
resource:
userInfoUri: http://openam.example.org:8080/OpenAMTest/oauth2/userinfo
zuul:
routes:
exampleApp:
path: /example-application/**
url: http://openam.example.org:8081/example-application
The Application class looks like the following:
#SpringBootApplication
#EnableZuulProxy
#EnableOAuth2Sso
public class TestZuulProxy extends SpringBootServletInitializer {
public static void main(String[] args){
SpringApplication.run(TestZuulProxy.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(applicationClass);
}
private static Class<TestZuulProxy> applicationClass = TestZuulProxy.class;
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends OAuth2SsoConfigurerAdapter {
#Override
public void match(RequestMatchers matchers) {
matchers.anyRequest();
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/index.html", "/home.html", "/")
.permitAll().anyRequest().authenticated().and().csrf()
.csrfTokenRepository(csrfTokenRepository()).and()
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
public class CsrfHeaderFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class
.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie==null || token!=null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
}
}
}
Now when i go to the "example-application" i get forwarded to the OpenAM authorization login screen. When I type in the credentials I can access the "example-application". Console log on the Gateway Service:
2015-06-22 17:14:10.911 INFO 6964 --- [nio-9000-exec-3] o.s.c.s.o.r.UserInfoTokenServices : Getting user info from: http://openam.example.org:8080/OpenAMTest/oauth2/userinfo
2015-06-22 17:14:10.953 INFO 6964 --- [nio-9000-exec-3] o.s.b.a.audit.listener.AuditListener : AuditEvent [timestamp=Mon Jun 22 17:14:10 CEST 2015, principal=Aaccf Amar, type=AUTHENTICATION_SUCCESS, data={details=remoteAddress=0:0:0:0:0:0:0:1, sessionId=<SESSION>, tokenType=BearertokenValue=<TOKEN>}]
Http-Header read by Zuul Filter:
authorization --- Bearer c2b75b5a-c026-4e07-b8b9-81e9162c9277
x-forwarded-host --- localhost:9000
x-forwarded-prefix --- /example-application
So something works! I have an access-token that gets forwarded to the REST-API.
Problem
1) This solution does not really meet my requirements, because I don't want the REST API to call the token-endpoint of OpenAM. I want that a JWT with the nessessary claims gets passed to the API in the Header. Should I create a JWT in the Gateway (e.g. Zuul Filter) manually or is there another solution?
2) In the solution above, when the access-token expires Zuul keeps forwarding me to the API. Why is this? Doesn't Spring Oauth2 checks if the access-token expires? how can I implement that?
3) I also tried to configure the tokenInfoUri in application.yml, but then I am getting a "405 Method Not Allowed" exception, because I think OpenAM expects a GET request on the tokeninfo-Endpoint. Can I customize this somehow? Which Classes do I need to override/customize to change the request.
If you have an advices, ideas or possible solutions, let me know!
Thank you!
If you want to use a JWT in your application, configure OpenAM as an OpenID Connect provider (OpenAM 12.0 or later). Once the user has authenticated OpenAM will issue a JWT with a number of claims about the user. Your SPA can pass that along in requests to your service tier.
If you want a gateway to enforce AuthN/ AuthZ on the users session, you can use something like ForgeRock's OpenIG. This can act as a policy enforcement point, and has the ability to introspect JWT tokens.

Resources