Spring Oauth 2 SSO, Zuul and OpenAM integration

4k views Asked by At

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!

1

There are 1 answers

0
Warren Strange On

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.