Spring starter security-basic auth-vai zuul server - spring-security

I have zuul server vai which I am calling OAuth's server to get token vai postman,if I request by sending cilent credentials in body I get token back but when I choose to get it via send client credentials in basic-auth header I am getting unauthorised 401.

Resolved after adding
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity.ignoring()
.antMatchers("/auth/**");
}

Related

How to achieve secure REST api along with springboot session and spring security without authentication

Problem: My java springboot application receives JWT token from external system to authenticate a user with their external identity management provider which returns the user details upon success.
Once userdetails is received, the backend application must create a redirect url for the external system end user. The redirect url will land the user on my angular application to show the landing page.
Here on, all the rest api's should be allowed through an http session.
In case the user tries to access the rest api's directly, he should get an Authentication error.
How can we achieve authorization in this case since authentication was not done by my spring boot application. Can we create custom Spring session using spring security and manually put userDetails in the SecurityContext?
I am currently dealing JWT tokens obtained from Google. Including Google, pretty much all authorization servers provide rest APIs such as GET /userInfo, where you can carry the JWT token in the request header or in the URL as a GET parameter, and then verify if the JWT token is valid, non-expired, etc.
Because verifying a JWT token is usually stateless, these APIs generally come with a generous limit and you can call them as many times as you need.
I assume that you have Spring security integrated and then you can add a filter. In this way, every request has to be verified for its token in the header.
#Service
public class TokenAuthenticationFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
String header = request.getHeader("Authorization");
RestTemplate restTemplate = new RestTemplate(); // If you use Google SDK, xxx SDK, you do not have to use restTemplate
String userInfoUrl = "https://example.com/api/userInfo";
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", header);
HttpEntity entity = new HttpEntity(headers);
ResponseEntity<String> response = restTemplate.exchange(
userInfoUrl, HttpMethod.GET, entity, String.class, param);
User user = response.getBody(); // Get your response and figure out if the Token is valid.
// If the token is valid? Check it here....
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
}
filterChain.doFilter(request, response);
}
}

How to trigger OAuth2 authentication in Zuul API gateway if a downstream service responds with a 401

I'm experimenting with three Spring cloud (boot) applications.
An Authentication Server on port 9999
A basic backend-sample that has secured and unsecured endpoints on port 9008
A basic Zuul API gateway with several routes (secured and unsecured) to the backend-sample on port 9000
The backend-sample boot application is annotated as a resource server (#EnableResourceServer) and secures some endpoints with a ResourceServerConfigurerAdapter
When I first call one of the routes that are secured on the Zuul API gateway, I get redirected to the authentication server's login page. After logging in there, I get redirected to the secured route I initially requested. Secured backend-sample endpoints behave as expected which means that the backend-sample does get the granted roles for the supplied token. If I hit a backend-sample endpoint I don't have the proper role for, I get an OAuth 403 response. Everything fine in this case.
We need to put legacy services behind the API gateway as well. These render html and should be able to trigger a login when the user hits a secured area there. We can't secure these areas on API gateway route level as the legacy backends have complicated (grown) permission models for many different sub URLs.
Does anyone know a good way to make a Spring-cloud API gateway redirect to an authentication server's login in such a downstream 401-response case? I tried a simple redirect in a ZuulFilter of type "post" but failed as the response is already committed there.
Backend-sample application.yml;
server:
port: 9008
security:
oauth2:
resource:
userInfoUri: http://localhost:9999/uaa/user
API gateway application.yml:
server:
port: 9008
zuul:
proxy:
addProxyHeaders: true
sensitive-headers:
routes:
unsecured-backend-sample:
path: /unsecured-backend-sample/**
url: http://localhost:9008
authorized-backend-sample:
path: /authorized-backend-sample/**
url: http://localhost:9008/
user-role-secured-backend-sample:
path: /user-role-secured-backend-sample/**
url: http://localhost:9008/
xxx-role-secured-backend-sample:
path: /xxx-role-secured-backend-sample/**
url: http://localhost:9008/
security:
oauth2:
client:
accessTokenUri: http://localhost:9999/uaa/oauth/token
userAuthorizationUri: http://localhost:9999/uaa/oauth/authorize
clientId: acme
clientSecret: acmesecret
resource:
userInfoUri: http://localhost:9999/uaa/user
I finally found a solution that works great for me. I wrote a ZuulFilter that handles only 401 responses and redirects to login. It also saves the rejected request in an HTTP session request cache so the SavedRequestAwareAuthenticationSuccessHandler can redirect you back to the initially requested downstream service URL.
#Component
public class LoginOnDownstreamUnauthorizedResponseFilter extends ZuulFilter {
private Logger logger = LoggerFactory.getLogger(getClass());
private AuthenticationEntryPoint authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint("/login");
private RequestCache requestCache = new HttpSessionRequestCache();
#Override
public boolean shouldFilter() {
// Only handle downstream 401s
return RequestContext.getCurrentContext().getResponse().getStatus() == HttpStatus.SC_UNAUTHORIZED;
}
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
HttpServletResponse response = ctx.getResponse();
// We need to put the rejected request in the request cache for SavedRequestAwareAuthenticationSuccessHandler
// to find it's way back to the initial request URI after successful authentication.
requestCache.saveRequest(request, response);
String text = String.format("Downstream service %s responded with a status code 401.", request.getRequestURI());
logger.debug(text + " Calling Authentication entry point.");
try {
authenticationEntryPoint.commence(request, response, new InsufficientAuthenticationException(text));
} catch (IOException | ServletException e) {
logger.error("Failed to redirect to Authentication entry point", e);
}
return null;
}
#Override
public String filterType() {
return "post";
}
#Override
public int filterOrder() {
// make sure to run before SendResponseFilter
return 500;
}
}

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.

Spring OAuth: Resource Server with Authorization Server backend

I want to develop two independent services, one for the business stuff and one for the user authentication using Spring OAuth 2
Let's call them Business-Service and OAuth-Service.
Now I want the Business-Service delegate to the OAuth-Service if a request is not authenticated. The client application (an Android app) should not know about the OAuth-Service a priori, it should only be delegated to it by the Business-Service with an 302 HTTP redirect for non-authenticated request. To be precise, I want my API landing page to provide a link to http://businessservice.com/login and when my client app decides to follow this link, it gets redirected to the OAuth-Service.
If I annotate the Business-Service with #EnableOAuth2Resource , all of its resources are protected returning a 401 when I curl them without an access token. So far so good. If I provide an access token like this:
curl -v http://localhost:8667/resource/ -H "Authorization: Bearer $TOKEN"
I can access the resource. Still good.
However if I annotate the Business-Service with #EnableOAuth2Sso for enabling the redirection to the OAuth service, it looses the capability of accessing the resources with an access token (same curl as above), it only returns a 302 to the login page http://localhost:8667/login
If I use both annotations, the #EnableOAuth2Resource always seems to "win", as the authentication works but calling http://localhost:8667/login returns a 404.
So what is the right way to create a resource server that delegates to the auth server for non-authenticated calls?
After trying around for hours I now found a solution.
The Business Server (Resource Server) now looks as follows:
#SpringBootApplication
#EnableOAuth2Sso
#EnableOAuth2Resource
public class BusinessService {
public static void main(final String[] args) {
final ConfigurableApplicationContext context = SpringApplication.run(BusinessService.class, args);
}
}
with two configurations, one for the SSO:
#Configuration
public class OAuth2SsoConfiguration extends OAuth2SsoConfigurerAdapter {
#Override
public void match(final RequestMatchers matchers) {
matchers.antMatchers("/");
}
#Override
public void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll();
}
}
and one for the Resource:
#Configuration
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(final HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/resource/**").and().authorizeRequests().anyRequest().authenticated().antMatchers("/").permitAll();
}
}
This results in the following:
curl -v http://localhost:8667/
returns
HTTP/1.1 200 OK
{"links":[{"rel":"login","href":"http://localhost:8667/login"}]}
curl -v http://localhost:8667/resource/
returns
HTTP/1.1 401 Unauthorized
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
curl -v http://localhost:8667/login
returns
HTTP/1.1 302 Found
Location: http://localhost:8666/user/oauth/authorize?client_id=clientId&redirect_uri=http%3A%2F%2Flocalhost%3A8667%2Flogin&response_type=code&state=YmmNO9
So my business servie is secured with as a resource server returning a 401 for all business resources. The root of the service is applicable for all clients so they can discover the login relation and if they follow this relation, they're redirected to the Authorization server

why Spring Security Exception Translation Filter creates 403 Response Code for default configuration

I am pretty new to Spring Security land. I am using programmatic configuration of Spring Security with servletApi() which is pretty neat.
Here is the configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.securityContext().and()
.servletApi().and()
.authorizeUrls()
.antMatchers("/login").permitAll()
.antMatchers("/").permitAll()
.antMatchers("/**").authenticated();
}
I am using http servlet api login I am not using any filter for this.
In case a unauthorised request, ExceptionTranslationFilter uses Http403EntryForbiddenEntryPoint to return 403 forbidden status.
In my scenario:
If user does not authenticated, a 401 status code should return.
If user authenticated but not authorised, a 403 status code should return.
But default configuration creates 403 status for both case.
Here are my questions:
Why is the default entry point is Http403EntryForbiddenEntryPoint? It can be 401?
If I change Http403EntryForbiddenEntryPoint to Http401EntryForbiddenEntryPoint, does It create a problem?
Thanks

Resources