Backend (Okta Saml Spring Boot) with Cross domain front end reactjs

Viewed 118

We are facing some challenges while integrating frontend and backend server with cross domain.

Kindly have a look and assist us to resolve this issue.

Frontend (Reactjs UI.COM)

Backend (Spring Boot Okat Saml integrated Api.com) integrated with Okta SAMl Authentication

                            Spring security to saml authentication

                            Create getUser  https://api.com/user Api that return user details     

                            Created https://api.com/private controller to handle redirect  

Approach##A

  1. Frontend call fetch api Frontend/user and check if user details is present or not (Note: first user details will not present So it'll execute below steps)

            2)Frontend will invoke  redirect backend/private and  come to backend server
    
            3) then backend server will redirect to okta and get Saml Response 
    
            4)Now Backend server Spring Security will parse saml response and  start session(i.e “BackendSession”) and prepare SamlUserDetails 
    
            5)Now time to redirect back to originater/frontend So Backend server will call redirect frontend and go back to frontend/UI and drop “BacknedSessionId” in browser cookie. (Note: Here cookie will drop aginst backend domain only)      
    
    
            Problem Statement: Here frontend unable to read  "BackendendSessionId" which is drop aginst backend
    
    
            6) Further task: need to call backend/user api to get user details and this api expecting  credential i.e SamlUserDetails (i.e inside "BackendendSessionId")  
    

Approach##B:
1 to 4 same as above .

            5)Now time to redirect back to originater/frontend So Backend server will call redirect frontend and  also append "BackendendSessionId" into uri/query param and go back to frontend/UI and also drop “BacknedSessionId” in browser cookie. (Note: Here cookie will drop aginst backend domain only)            
            
            6)Now frontend get "BackendendSessionId" through uri/query param
            
            7) Now frontend call backend/user api and pass "BackendendSessionId" into request header
            
            
            Problem Statement: Here Backend/user api expecting  credential i.e SamlUserDetails
            
            

Note: Client is not interested in cookies at all. So we have eliminated dropping cookies approach

Code reference

https://api.com/private controller to handle redirect

@GetMapping("/private")
public ModelAndView index(@SAMLUser SAMLUserDetails user, HttpServletRequest request, HttpServletResponse response){
    Map<String, String> userAttributes = user.getAttributes();
    log.info("userAttributes {}", userAttributes);
    log.info("saml user {}", user);
    log.info("saml userName {}", user.getUsername());
    String location = request.getHeader("referer");
    if (isBlank(location)){
        location=srtUiUrl;
    }
    StringBuilder sb = new StringBuilder();
    sb.append(location);
    sb.append("?stateId=");
    sb.append(request.getSession().getId());
    log.info("RedirectUrl ",sb.toString());
    return new ModelAndView("redirect:" + sb.toString());

getUser https://api.com/user api return user

@GetMapping("/receive/v1/user")
public ResponseEntity<User> getUser(@SAMLUser SAMLUserDetails user) {
    log.info("SAMLUserDetails : ", user);

    if (null==user) {
        log.info("No user found Authentication required");
        return new ResponseEntity<>(null, HttpStatus.OK);
    } else {
            Map<String, String> userAttributes = user.getAttributes();
            log.info("SAMLUserDetails Attributes {}", userAttributes);
            User userModel=new User();
            userModel.setId(user.getUsername());
            userModel.setFirstName(userAttributes.get(SrtConstant.FIRST_NAME));
            userModel.setLastName(userAttributes.get(SrtConstant.LAST_NAME));
            userModel.setEmail(userAttributes.get(SrtConstant.USER_EMAIL));
            userModel.setShortName(userAttributes.get(SrtConstant.STORE_NAME));
            userModel.setGroupName(userAttributes.get(SrtConstant.GROUPS));
            userModel.setStoreId(userAttributes.get(SrtConstant.STORE_ID));
            userModel.setStoreName(userAttributes.get(SrtConstant.STORE_NAME));
            log.info("userModel :", userModel);
        return ResponseEntity.ok().body(userModel);
    }
}
            
            

Spring security to saml authentication

@Override protected void configure(final HttpSecurity http) throws Exception { log.info("configure : "+"spHost :"+ spHost +" ,KeystoreFilePath : "+keyStoreFile +" ,KeyStoreAlias : "+keyStoreAlias+" ,metadatpath : "+metadataPath); http .csrf().disable() .authorizeRequests() .antMatchers("/saml*").permitAll() .antMatchers(HttpMethod.GET, "/receive/").permitAll() .antMatchers(HttpMethod.POST, "/receive/").permitAll() .antMatchers(HttpMethod.PATCH, "/receive/").permitAll() .antMatchers(HttpMethod.DELETE, "/receive/").permitAll() .antMatchers(HttpMethod.PUT, "/user").permitAll() .anyRequest().authenticated() .and() .apply(saml()) .userDetailsService(samlUserDetailsService) .serviceProvider() .protocol("https") .hostname(spHost) .basePath("/") .keyStore() .storeFilePath(keyStoreFile) .keyPassword(keyStorePassword) .keyname(keyStoreAlias) .and() .and() .identityProvider() .metadataFilePath(metadataPath); }

Cors filter

  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    final HttpServletResponse response = (HttpServletResponse) res;
    response.setHeader("Access-Control-Allow-Origin", srtUiUrl);
    response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
    response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
    response.setHeader("Access-Control-Max-Age", "3600");
    response.setHeader("Access-Control-Allow-Credentials", "true");

    if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) req).getMethod())) {
        response.setStatus(HttpServletResponse.SC_OK);
    } else {
        chain.doFilter(req, res);
    }
}
            
            

Same-site=none setting

server: port: 8443 servlet: session: cookie: same-site: none secure: true

0 Answers
Related