Spring zuul proxy not accepting bearer token
Asked Answered
A

4

5

I have a zuul proxy (http://localhost:8765) serving an angular web app (http://localhost:8080/app). Behind the zuul proxy there is also an oauth2 server (http://localhost:8899).

The web resources are proxied under http://localhost:8765/web and the resources are proxied under http://localhost:8765/api. The Zuul proxy serves the static web resources without authentication. So the first authentication is done through a JSON call (GET /api/user) which of course fails with 401.

Now I forward the page to "http://localhost:8899/uaa/oauth/authorize?response_type=token&client_id=web&redirect_uri=http://localhost:8765/web/index.html" to make an implicit grant oauth 2 flow. I can authorize now the web application and get forwarded back to my web app. The token is part of the url and I can parse it.

IMHO the only thing I now have to do is to add this token as Authorization header (e.g. Authorization:Bearer 2829d5e2-4fbe-4f91-b74d-c99b2fe894a7). But the zuul proxy won't accept this request as authorized.

I am using spring boot 1.3.2 and spring cloud Brixton.M4. The Zuul Server Application can be found here and the security config here.

Here are my request headers:

Accept:application/json
Accept-Encoding:gzip, deflate, sdch
Accept-Language:de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
Authorization:Bearer 2829d5e2-4fbe-4f91-b74d-c99b2fe894a7
Connection:keep-alive
Cookie:XSRF-TOKEN=a6ddea36-e3b7-4f22-b80c-b4c8b6fd7760; JSESSIONID=DAE4649D11386D586A0CF739148E505A; XSRF-TOKEN=3a7a57ad-68f6-4cc6-923b-4e8fe340fe1e
Host:localhost:8765
Referer:http://localhost:8765/web/index.html
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/48.0.2564.82 Chrome/48.0.2564.82 Safari/537.36
X-Auth-Token:2829d5e2-4fbe-4f91-b74d-c99b2fe894a7
X-Requested-With:XMLHttpRequest
X-XSRF-TOKEN:a6ddea36-e3b7-4f22-b80c-b4c8b6fd7760

My Zuul configuration is:

server:
  context-path: /

security:
  user:
    password: none
  oauth2:
    sso:
        loginPath: /login
    client:
      accessTokenUri: ${authserver.protocol}://${authserver.hostname}:${authserver.port}/${authserver.contextPath}/oauth/token
      userAuthorizationUri: ${authserver.protocol}://${authserver.hostname}:${authserver.port}/${authserver.contextPath}/oauth/authorize
      clientId: web
    resource:
      userInfoUri: ${authserver.protocol}://${authserver.hostname}:${authserver.port}/${authserver.contextPath}/user
      preferTokenInfo: false

zuul:
  routes:
    web-portal:
      path: /web/**
      url: http://localhost:8080/app
    user:
      path: /api/user/**
      url: ${authserver.protocol}://${authserver.hostname}:${authserver.port}/${authserver.contextPath}/user
    authentication-service:
      path: /uaa/**
      stripPrefix: false

---
spring:
  profiles: local

logging:
  level:
     org:
       springframework:
         security: DEBUG

authserver:
  protocol: http
  hostname: localhost
  port: 8899
  contextPath: uaa

The zuul server log is:

2016-02-11 17:11:02.958 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request '/api/user' matched by universal pattern '/**'
2016-02-11 17:11:02.958 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 1 of 13 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2016-02-11 17:11:02.958 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 2 of 13 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2016-02-11 17:11:02.958 DEBUG 3242 --- [nio-8765-exec-4] w.c.HttpSessionSecurityContextRepository : HttpSession returned null object for SPRING_SECURITY_CONTEXT
2016-02-11 17:11:02.958 DEBUG 3242 --- [nio-8765-exec-4] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@5571734d. A new one will be created.
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 3 of 13 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@42c144ce
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 4 of 13 in additional filter chain; firing Filter: 'CsrfFilter'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.util.matcher.AndRequestMatcher   : Trying to match using org.springframework.security.web.csrf.CsrfFilter$DefaultRequiresCsrfMatcher@4ad95822
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.util.matcher.AndRequestMatcher   : Did not match
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 5 of 13 in additional filter chain; firing Filter: ''
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 6 of 13 in additional filter chain; firing Filter: 'LogoutFilter'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'GET /api/user' doesn't match 'POST /logout
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 7 of 13 in additional filter chain; firing Filter: 'OAuth2ClientAuthenticationProcessingFilter'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/user'; against '/login'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 8 of 13 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 9 of 13 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 10 of 13 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.a.AnonymousAuthenticationFilter  : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@905571d8: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@0: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: DAE4649D11386D586A0CF739148E505A; Granted Authorities: ROLE_ANONYMOUS'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 11 of 13 in additional filter chain; firing Filter: 'SessionManagementFilter'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 12 of 13 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 13 of 13 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/user'; against '/index.html'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/user'; against '/home.html'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/user'; against '/web/**'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/user'; against '/uaa/oauth/**'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /api/user; Attributes: [authenticated]
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@905571d8: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@0: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: DAE4649D11386D586A0CF739148E505A; Granted Authorities: ROLE_ANONYMOUS
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@77816ac4, returned: -1
2016-02-11 17:11:02.960 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.a.ExceptionTranslationFilter     : Access is denied (user is anonymous); redirecting to authentication entry point

org.springframework.security.access.AccessDeniedException: Access is denied

How can I force the authentication on the zuul proxy against the access token?

--- Edit: If I enable the authentication for the static web resource by removing the http security exception for it, I get forwarded to the authorization page. When the request gets forwarded back everything works. The zuul proxy forwards to the oauth server with its /login URL as return address. This seems to be the correct way. I suppose it saves some information in its session and after that forwards back to the initial requesting page (in my case /web/index.html).

When I now restart the authentication service (like simulating expired token) the resources from the web application are served, but the request to /api/user (proxied to the authentication server) is denied.

Same happens when I come from a manually constructed authorize URL like http://localhost:8899/uaa/oauth/authorize?response_type=token&client_id=web&redirect_uri=http://localhost:8765/web/index.html. First I get to the authorization page of the oauth server. This is correct. On click on authorize the request gets forwarded to the web app (/web/index.html). All static content is served without problem, but access to /api/user is again denied. This time with an error logged in the oauth server: Invalid access token: dff5121b-06e4-4bd7-b48e-08ad82d71404

Abreast answered 11/2, 2016 at 16:14 Comment(14)
Why do you configure security.oauth2.sso.* if you want the proxy to be a resource server? You should be able to just @EnableResourceServer and don't touch the SSO stuff.Shaikh
@Dave: Thanks for the hint, just removed this sso config. About the resource server annotation: I was trying to build it accordingly to your tutorial, there was no @EnableResourceServer. The resource server annotation is on my oauth server /user controller. Of course I tried it, but unfortunately it did not work.Abreast
"Did not work" isn't very informative. Maybe you could post a link to your code?Shaikh
Yes, sure. Pushed the latest changes to github. You can see all code unter github.com/thomasletsch/moserp. The zuul proxy is under github.com/thomasletsch/moserp/tree/master/backend/…Abreast
@DaveSyer: Thanks for all your support. I started again trying your suggested solution by switching to @EnableResourceServer. You were right, this is the way to go! It now works as expected! Perhaps you can answer me one last question: Is the way I use the proxy server recomended at all? All examples I have seen so far do use SSO.Abreast
SSO is better for browser-based clients. Otherwise how are your clients going to get a token?Shaikh
I have one browser client and (will have) one android client. For now the web app forwards to the authorization page in case of an 401. There is the angular module oauth-ng still on my list to try out. For the android app, I planned to follow aaronparecki.com/2012/07/29/2/oauth2-simplified to get my tokens in my app (use a web browser to get the token and read it from the page it gets forwarded to).Abreast
So all your clients are browsers effectively, and you ought to use auth code grant (i.e. the SSO features) IMO.Shaikh
@DaveSyer, if he do that, wont he lose option of redirection to login page?Doctrinaire
@ThomasLetsch, are all microservices in your project use Oauth server? Or are any of them public (accessible to all?). I am struggling with similar problem, for all my services behind Zuul Access is denied. If I request ms directly everthing is working.Doctrinaire
@5er: Yes, all the services are hidden behind the proxy and need authentication. So all have the config as described in the accepted answer below.Abreast
@ThomasLetsch, Maybe u have a clue how to solve this problem, please? #38207214Doctrinaire
Hey @DaveSyer can you please explain how then resource server will interact with authorization server? Will resource server call authorization server each time when user make request to resource server?(I'm using JWT tokens)Washerwoman
With JWT interaction with the auth server is optional (you only need to do it if you care about token and account revocation).Shaikh
A
1

Thanks to @Dave I can answer my own question: To have the Zuul proxy acception the OAuth Bearer Token header, you have to configure it as a resource server instead a SSO server. Remove the @EnableOAuth2Sso annotation and use the @EnableResourceServer annotation instead. First I had still my WebSecurityConfigurerAdapter in place. So this was fixed by changing

@Configuration
@EnableOAuth2Sso
public class OAuthConfiguration extends WebSecurityConfigurerAdapter {

to

@Configuration
@EnableResourceServer
public class OAuthConfiguration extends ResourceServerConfigurerAdapter {

If one should use the implicit authentication is another topic (see comment from Dave).

Abreast answered 24/2, 2016 at 10:56 Comment(2)
are you saying the client UI is now annotated with SSO? who plays the SSO client now if Zuul is the resource...Sinful
I have been handed a "working" project that does not have either EnableOAuth2Sso or EnableResourceServer. And i have the same problem.. Any suggestions ?Aurangzeb
Y
4

zuul api not forward header by default so disable it we need to add it

zuul:
  sensitive-headers: Cookie,Set-Cookie
Younger answered 11/3, 2019 at 17:42 Comment(1)
My specific issue was that Zuul was not passing the Authorization header to authenticate the user to make the request to the backend service. Presumably because these headers were being excluded by default. This fixed it for me.Diploid
R
1

You should move to Spring Boot 1.3x.

Then, you can annotate you Zulu Proxy with @EnableOAuath2Sso annotation. In your application.yml for Zuul, specify the following (for Spring Boot 1.3x):

security:
   user:
     password: none
   oauth2:
     client:
       accessTokenUri: ${oauthserver}:${oauthport}/oauth/token
       userAuthorizationUri: ${oauthserver}:${oauthport}/oauth/authorize
       clientId: acme
       clientSecret: acme secret
Rhee answered 12/2, 2016 at 3:26 Comment(4)
Actually I am using spring 1.3. I will edit my question accordingly.Abreast
is it possible to proxy the auth server through zuul? create a zuul route to the auth server and then use it in the oauth2:client: configuration you suggested?Sinful
Yes, this is very much possible using the following :Rhee
code@SpringBootApplication @EnableEurekaClient @EnableOAuth2Client @EnableZuulProxy @EnableOAuth2SsoRhee
A
1

Thanks to @Dave I can answer my own question: To have the Zuul proxy acception the OAuth Bearer Token header, you have to configure it as a resource server instead a SSO server. Remove the @EnableOAuth2Sso annotation and use the @EnableResourceServer annotation instead. First I had still my WebSecurityConfigurerAdapter in place. So this was fixed by changing

@Configuration
@EnableOAuth2Sso
public class OAuthConfiguration extends WebSecurityConfigurerAdapter {

to

@Configuration
@EnableResourceServer
public class OAuthConfiguration extends ResourceServerConfigurerAdapter {

If one should use the implicit authentication is another topic (see comment from Dave).

Abreast answered 24/2, 2016 at 10:56 Comment(2)
are you saying the client UI is now annotated with SSO? who plays the SSO client now if Zuul is the resource...Sinful
I have been handed a "working" project that does not have either EnableOAuth2Sso or EnableResourceServer. And i have the same problem.. Any suggestions ?Aurangzeb
G
1

By default the Zuul Proxy will remove certain headers because it deems them to sensitive and no other server should receive them. It has a list of cookies and headers it needs to remove. This needs to be overridden. The way to do this for a particular route is:

zuul:
  routes:
    profile-service-chaining:
      sensitiveHeaders:
      stripPrefix: false
      serviceId: profile-service-chaining
      path: /services/profiles

The line 'sensitiveHeaders:' will empty the cookies headers. This will cause all headers to be past to the server the request will be sent to. This will allow the Authorization header to sent to the target server. As the Zuul documentation says make sure to always remove sensitive headers. To do this the add:

sensitiveHeaders: Cookie

The above will remove the Cookie header before it is passed to the next server as specified by the Zuul route.

Gaddi answered 1/12, 2020 at 23:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.