Why getting connexion popup while using spring security and Kerberos?

104 Views Asked by At

I have a spring boot microservice for sso authentification using Kerberos.

before the probleme :

The authentication process starts when a user tries to access to the application (angular UI) and follow those steps:

  • The frontend will call the /login api (authentificaion service).

  • Using Kerberos and spring security, I retrieve the id_name of the user who triggers the request: SecurityContextHolder.getContext().getAuthentication().getName().split("@")[0], this id_name is unique inside the company.

  • I use this id_name to query the database and get all the other information about this user (no password) and I send them back to the front end who will inject them in the local storage so he/she doesn't need to connect again next time

This process was working fine until we get a change request to use JWT to secure access to all the microserviecs we have.

At this point I added the needed classes to generate, validate the token and a filter to intercept requests and check if token valid...

The probleme :

The new process is working fine except for one use case:

If first connexion request was made directly from the front end, the request gets blocked and the browser shows a connexion popup. Otherwise, if I call manually, the API from the browser or using postman I get the correct result with Token in the response header.

Note 1 : If I call manually, the /login API from the browser, I can then get connected from application like normal, I can also disconnect and connect again (with different profiles..).

Note 2 : I tried to use the old version of the Web service (before adding JWT change) and I get connected correctly (Token was in the request response)

I added some logs in the RedirectionTokenFilter class to get more information about the request, in the result I can see that the UserPrincipal is null unlike the when the request is coming from Postman.

logs :

 System.out.println("UserPrincipal :" + ((HttpServletRequest) request).getUserPrincipal());
            System.out.println("Auth type :" + ((HttpServletRequest) request).getAuthType());
            System.out.println("Context path :" + ((HttpServletRequest) request).getContextPath());
            Enumeration<String> headers = ((HttpServletRequest) request).getHeaderNames();
            while(headers.hasMoreElements()){
                String param = headers.nextElement();
                System.out.println("HeaderName :" + param);
                try{
                    System.out.println("Param " +param+" :" + ((HttpServletRequest) request).getHeader(param));
                }catch (Exception e){
                }
            }
            System.out.println("Method :" + ((HttpServletRequest) request).getMethod());
            System.out.println("RemoteUser :" + ((HttpServletRequest) request).getRemoteUser());
            System.out.println("ServletPath :" + ((HttpServletRequest) request).getServletPath());

Logs output :

UserPrincipal :null
Auth type :null
Context path :/XXXX-Auth
    HeaderName :host
        Param host :XXXX:9280
    HeaderName :connection
        Param connection :keep-alive
    HeaderName :sec-ch-ua
        Param sec-ch-ua :"Chromium";v="104", " Not A;Brand";v="99", "Google Chrome";v="104"
    HeaderName :pragma
        Param pragma :no-cache
    HeaderName :sec-ch-ua-mobile
        Param sec-ch-ua-mobile :?0
    HeaderName :authorization
        Param authorization :null
    HeaderName :accept
        Param accept :application/json, text/plain,...
    HeaderName :cache-control
        Param cache-control :no-cache
    HeaderName :user-agent
        Param user-agent :Mozilla/5.0 (Windows NT 10.0; Win64; x64)....
    HeaderName :sec-ch-ua-platform
        Param sec-ch-ua-platform :"Windows"
    HeaderName :expires
        Param expires :Sat, 01 Jan 2000 00:00:00 GMT
    HeaderName :origin
        Param origin :https://XXXXXX
    HeaderName :sec-fetch-site
        Param sec-fetch-site :same-site
    HeaderName :sec-fetch-mode
        Param sec-fetch-mode :cors
    HeaderName :sec-fetch-dest
        Param sec-fetch-dest :empty
    HeaderName :referer
        Param referer :https://XXXXXX
    HeaderName :accept-encoding
        Param accept-encoding :gzip, deflate, br
    HeaderName :accept-language
        Param accept-language :en-US,en;q=0.9
Method :GET
RemoteUser :null
ServletPath :/security/login
tocken {null}
token empty trying login
context[auth]=org.springframework.security.authentication.AnonymousAuthenticationToken@274dc7e4: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: xx.xx.xx.xx; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
UserID anonymousUser
userDTO null

My spring security configuration (after adding JWT) :

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
        log.info("sec configuration..");
        http.csrf().disable().cors().and().authorizeRequests().antMatchers("/**").authenticated().and().httpBasic()
                .authenticationEntryPoint(restSpenegoEntryPoint()).and()
                .addFilterBefore(spnegoAuthenticationProcessingFilter(), BasicAuthenticationFilter.class)
                .addFilterAfter(redirectionTokenFilter(), ExceptionTranslationFilter.class).addFilter(filterd)
    }

    @Bean
    public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter() throws Exception {
        SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter = new SpnegoAuthenticationProcessingFilter();
        // spnegoAuthenticationProcessingFilter.setSuccessHandler(customAuthenticationSuccessHandler());
        spnegoAuthenticationProcessingFilter.setAuthenticationManager(authenticationManagerBean());
        return spnegoAuthenticationProcessingFilter;
    }

    @Bean
    RedirectionTokenFilter redirectionTokenFilter() {
        return new RedirectionTokenFilter();
    }

@Bean
    public CorsConfigurationSource corsConfigurationSource() {
        final CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Collections.singletonList("*"));
        configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
        
        configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type", "X-Requested-With"
                ,"Pragma","X-XSS-Protection","X-Frame-Options","X-Content-Type-Options",
                "Vary","Transfer-Encoding","Server","Expires","Date",
                "Access-Control-Allow-Headers","Access-Control-Allow-Credentials"

        ));
        configuration.setExposedHeaders(Arrays.asList("Content-type", "Authorization"));
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

RestSpenegoEntryPoint :

public class RestSpenegoEntryPoint extends SpnegoEntryPoint{

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ex)
            throws IOException, ServletException {
        response.addHeader("WWW-Authenticate", "Negotiate");
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
    }

}

AccessDeniedHandlerImpl :

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
            AccessDeniedException accessDeniedException) throws IOException, ServletException {
        response.setStatus(401);
        response.getWriter().flush();
    }

Angular changes (I'm not a front-end dev, I don't know too much about those changes) :

app.module.ts:

    @NgModule({
        imports: [
           providers: [
             ...
              { provide : HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi   : true},

auth-interceptor.ts:

import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Authservice } from '../service/auth.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    req = req.clone({
      setHeaders: {
        'Authorization': `${Authservice.getToken()}`,
      },
    });
    return next.handle(req);
  }
}

webservice.config.ts :

import {Authservice} from '../auth/service/auth.service';
export class WebServicesConfig {
@@ -11,12 +12,14 @@
        headers.append('Access-Control-Allow-Headers', 'Content-Type');
        headers.append('withCredentials', 'true');
        headers.append('Access-Control-Allow-Origin', '*');
        headers.append('Authorization', Authservice.getToken());
        return headers;
    }
    static getHeaders1(): Headers {
        let headers: Headers = new Headers();
        headers.append('Accept', 'application/json');
        headers.append('Content-Type', 'application/json');
        headers.append('Authorization', Authservice.getToken());
        return headers;
    }
}

Please let me know if something is not clear or if there is some missing information.

Thank you in advance.

0

There are 0 best solutions below