spring boot - feign client sending on basic authorization header| Pass jwt token from one microservice to another - spring-boot

I am creating a microservice based project using spring boot.
I have used eureka server for service discovery and registration also using JWT for authentication for authorization and authentication.
Each microservice has jwt validation and global method security is implemented on controllers
I am making inter microservice calls using feign client.
Services -
1)main request service
2)Approver service;
approver service is making a call to main service for invoking a method that is only accessible by ADMIN
but when jwt validation is processed on main request service side..i can only see basic authorization header in Headers.
I am passing JWT token from my approver service
Feign client in approverservice
#FeignClient("MAINREQUESTSERVICE")
public interface MainRequestClient {
#RequestMapping(method=RequestMethod.POST, value="/rest/mainrequest/changestatus/{status}/id/{requestid}")
public String changeRequestStatus(#RequestHeader("Authorization") String token,#PathVariable("requestid")int requestid,#PathVariable("status") String status);
}
Code for reading header from request
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request=(HttpServletRequest) req;
HttpServletResponse response=(HttpServletResponse) res;
String header = request.getHeader("Authorization");
System.out.println("header is "+header);
if (header == null || !header.startsWith("Bearer")) {
chain.doFilter(request, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
While debugging this filter i have printed the token on console
Header when debugged in main request service
So can get help on how can i pass my JWT token from one microservice to another?

Try this (code based on https://medium.com/#IlyasKeser/feignclient-interceptor-for-bearer-token-oauth-f45997673a1)
#Component
public class FeignClientInterceptor implements RequestInterceptor {
private static final String AUTHORIZATION_HEADER="Authorization";
private static final String TOKEN_TYPE = "Bearer";
#Override
public void apply(RequestTemplate template) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication instanceof JwtAuthenticationToken) {
JwtAuthenticationToken token = (JwtAuthenticationToken) authentication;
template.header(AUTHORIZATION_HEADER, String.format("%s %s", TOKEN_TYPE, token.getToken().getTokenValue()));
}
}
}

Related

Cookie Authentication instead of JWT Bearer Token after a successful Oauth2 Login in Spring Boot

I'm using callicoder's spring-boot-react-oauth2-social-login-demo
sample to implement a rest api using Oauth2 client. Sample works without a problem.
However after a successful Oauth2 authentication, I want to issue a cookie instead of JWT Token to secure access to my controllers. In order to do this, I added the lines below determineTargetUrl on OAuth2AuthenticationSuccessHandler. This sets a cookie containing JWT token created by TokenProvider.
CookieUtils.addCookie(response, appProperties.getAuth().getAuthenticationCookieName(), token, (int) appProperties.getAuth().getTokenExpirationMsec());
And then I created a CookieAuthenticationFilter similar to TokenAuthenticationFilter which checks the cookie set by OAuth2AuthenticationSuccessHandler.
public class CookieAuthenticationFilter extends OncePerRequestFilter {
#Autowired
private AppProperties appProperties;
#Autowired
private TokenProvider tokenProvider;
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
Optional<String> jwt = CookieUtils.getCookie(request, appProperties.getAuth().getAuthenticationCookieName()).map(Cookie::getValue);
if (StringUtils.hasText(String.valueOf(jwt)) && tokenProvider.validateToken(String.valueOf(jwt))) {
Long userId = tokenProvider.getUserIdFromToken(String.valueOf(jwt));
UserDetails userDetails = customUserDetailsService.loadUserById(userId);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.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);
}
}
and on SecurityConfig I replaced tokenAuthenticationFilter bean to cookieAuthenticationFilter
http.addFilterBefore(cookieAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
When I run the project, Oauth2 authentication is made successfully and cookie is set. However when I request a secured controller method, CookieAuthenticationFilter.doFilterInternal is not hit and request directly goes to RestAuthenticationEntryPoint.commence and exception is thrown with message Full authentication is required to access this resource .
Do I have to change any more configuration to change authentication to cookie from Bearer (JWT)?
The problem was a result of a missing exception without catch. The sample and the code works as expected.

Spring authentication scheme using the authentication header

I am using a spring boot application and I have a web security config adapter set up to authenticate each request using the jwt.
I want to expand my service to allow a different api end point to be authenticated using the header. One of the services I am integrating with sends a web hook and all it sends is the request with the custom header I set it up to include. How can I set up a specific endpoint to only authenticate using a custom header?
You could use a OncePerRequestFilter to filter the requests to that endpoint and return a 401 if they are do not contain your header with the right value.
You would define your filter:
public class HeaderSecurityFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String value = request.getHeader("Token");
if(value == null || !value.equals("Secret")) {
response.sendError(401);
return;
}
chain.doFilter(request, response);
}
}
And then register it:
#Configuration
public class HeaderSecurityConfiguration {
#Bean
FilterRegistrationBean<HeaderSecurityFilter> filterRegistration() {
FilterRegistrationBean<HeaderSecurityFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new HeaderSecurityFilter());
registration.addUrlPatterns("/some/path/*");
return registration;
}
}
Which would require the header of Token be present with a value of Secret for anything under /some/path/*.
You would also need to ensure through your oauth configuration that you open up access to /some/path/*.

Convert SAML 2.0 to JWT while redirecting from /saml/sso

I have a requirement to create a service provider form ADFS IDP. IDP is sending a SAML 2.0 token and in service side I am receiving it.
I have used spring security same extension plugin in service provider.
My code’s flow is mentioned below
/saml/login ——> will make a call to ADFS(IDP)———>redirect to saml/sso (with SAML token)
Now from this same/sso redirection to Front end (client will happen, which requested the token). I want to send back JWT instead of SAML to send back to browser.
What will be the best way to do it. How can I make /saml/sso to covert SAML to JWT in successRedirectHandler.
Sample handler
#Bean
public SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler() {
SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler =
new SavedRequestAwareAuthenticationSuccessHandler();
successRedirectHandler.setDefaultTargetUrl("/landing");
return successRedirectHandler;
}
Please note that I am using Nimbus JSON JWT jar for SAML to JWT conversion. I would prefer not to create a separate controller to convert SAML to JWT. Any help and pointers will be helpful.
Basically, after the authentication on the IDP side, you'll receive the assertions in response. You'll extract the attributes and perform some validation on the service side. You'll create JWT token with desired attributes. After that, you can redirect to target url with the token. Below is a snippet of code.
public class SAMLLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
public SAMLLoginSuccessHandler() {}
#Override
public void onAuthenticationSuccess(final HttpServletRequest request,
final HttpServletResponse response, final Authentication authentication)
throws IOException, ServletException {
if (authentication.getPrincipal() instanceof UserDetails
|| authentication.getDetails() instanceof UserDetails) {
UserDetails details;
String failureRedirectUrl = Constants.REDIRECTION_WEB;
if (authentication.getPrincipal() instanceof UserDetails) {
details = (UserDetails) authentication.getPrincipal();
} else {
details = (UserDetails) authentication.getDetails();
}
String username = details.getUsername();
SAMLCredential credential = (SAMLCredential) authentication.getCredentials();
List<Attribute> attributes = credential.getAttributes();
// validate user related information coming in assertions from IDP
// TODO:JWT Token generation code
// eventually you want to send that code to client therefore append the token
// in the url to which you want to redirect
String redirectUri; // set the redirect uri
response.sendRedirect(redirectUri);
}
super.onAuthenticationSuccess(request, response, authentication);
}
}

Keycloak spring boot microservices

i have a few java micro services deployed on open shift . all of them are protected by a api-gateway application which uses keycloak for authentication & Authorization.
Down stream services need to log which user perform certain actions.
in my api-gateway application properties i have already set zuul.sensitiveHeaders to empty
zuul.sensitiveHeaders:
i can see bearer token in the downstream applications .
but how do i get the principal/user from token as downstream applications don't have keycloak dependency in gradle. ( if i add the dependency , i need to reconfigure realm and other properties ) .. is this the right way to do ?
i also tried adding a filter in api-gateway to separately set the user_name in header
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
System.out.println(" Filter doFilter "+req.getUserPrincipal());
if(req.getUserPrincipal() != null ){
res.setHeader("MYUSER",req.getUserPrincipal()==null?"NULL":req.getUserPrincipal().getName());
}
chain.doFilter(request, response);
}
But when i try to get the header in downstream microservices is null.
I wouldn't recommend doing this, or assuming that your non-web facing apps are completely secure. Realistically you should be re-validating the bearer token.
What you need is a zuul filter to add a header to the request. This is mostly from memory and you could update the filter to check if it should filter or not, that the request doesn't already contain an expected header etc.
#Component
public class AddUserHeader extends ZuulFilter {
private static final Logger LOG = LoggerFactory.getLogger(AddUserHeader.class);
#Override
public String filterType() {
return "pre";
}
#Override
public int filterOrder() {
return 0;
}
#Override
public boolean shouldFilter{
return true;
}
#Override
public Object run() {
RequestContext.getCurrentContext().addZuulRequestHeader("MYUSER", SecurityContextHolder.getAuthentication().getPrincipal().getName());
return null;
}

SSO with Spring security

I have an application, where user is pre-authorized by SSO and lands to my page, now I need to make a call to another rest api to get some data, which is running on another server, but it will be use the same authentication. So I just wanted to know, how I can provide the authentication process? Do I need to set the cookie what I am getting from the incoming request.
When the request lands on your page it should have a token or key, in the http AUTHORIZATION header, this should be used with a filter
public class AuthFilter extends OncePerRequestFilter {
private String failureUrl;
private SimpleUrlAuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
try {
// check your SSO token here
chain.doFilter(request, response);
} catch (OnlineDriverEnquiryException ode) {
failureHandler.setDefaultFailureUrl(failureUrl);
failureHandler.onAuthenticationFailure(request, response, new BadCredentialsException("Captcha invalid!"));
}
}
public String getFailureUrl() {
return failureUrl;
}
public void setFailureUrl(String failureUrl) {
this.failureUrl = failureUrl;
}
}
Also read this post on how to set up the auto config. Spring security without form login

Resources