How to add the bearer token that is currently added to the response's auth header, to the response body?

Viewed 652

I implemented authorization to my SpringBoot API by using the bearer token, which when login is successful is added to the response's 'Authorization' header, this token then needs to be read by the login fetch method of my React project and added to the subsequent request 'authorization' headers.

Unfortunately though I have been told this is not possible using Fetch so I am now trying to refactor my login functionality so that it returns the token in the response body as opposed to the headers.

I have no idea how to do this, currently no JSON body object is returned, only the headers and I will have to change a lot of my AuthorizationFilter and AuthenticationFilter classes. If someone could point me in the right direction or advice me on what to change that'd be greatly appreciated.

Thanks.

AuthorizationFilter:

public class AuthorizationFilter extends BasicAuthenticationFilter {

public AuthorizationFilter(AuthenticationManager authenticationManager) { super(authenticationManager);}

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)

        throws IOException, ServletException {

        String header = request.getHeader("Authorization");

        if(header == null || !header.startsWith("Bearer")) {

        filterChain.doFilter(request,response);

        return;
        }

        UsernamePasswordAuthenticationToken authenticationToken = getAuthentication(request);

        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        filterChain.doFilter(request,response);

        }


private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        String token = request.getHeader("Authorization");
        if(token != null) {

        String user = Jwts.parser().setSigningKey("SecretKeyToGenJWTs".getBytes())
        .parseClaimsJws(token.replace("Bearer",""))
        .getBody()
        .getSubject();

        if(user != null) {

        return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());

        }
        return null;
        }
        return null;
        }
}

AuthenticationFilter:

   public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {

        private AuthenticationManager authenticationManager;

        public AuthenticationFilter(AuthenticationManager authenticationManager) {
            this.authenticationManager = authenticationManager;
            setFilterProcessesUrl("/login");
        }
        @Override

        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
            try {

                com.example.gambit2.domain.User creds = new ObjectMapper().readValue(request.getInputStream(), com.example.gambit2.domain.User.class);
                return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(creds.getUsername(), creds.getPassword(),new ArrayList<>()));
            }

        catch(IOException e) {

                throw new RuntimeException("Could not read request" + e);
            }

        }
        protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain, Authentication authentication)
        {
            String token = Jwts.builder()
                    .setSubject(((User) authentication.getPrincipal()).getUsername())
                    .setExpiration(new Date(System.currentTimeMillis() + 864_000_000))
                    .signWith(SignatureAlgorithm.HS512, "SecretKeyToGenJWTs".getBytes())
                    .compact();
            response.addHeader("Authorization","Bearer " + token);


        }
    }

UserController's signup method:

@PostMapping("/signup")
        public void signUp(@RequestBody User user) {
                user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
                userRepository.save(user);
                System.out.println(user.getUsername() + " SAVED SUCCESSFULLY");
        }

REACT Home.js:

const SIGNUP_URL = 'http://localhost:8080/users/signup';
const LOGIN_URL = 'http://localhost:8080/login';
class Home extends Component {

    constructor(props) {
        super(props);
        this.state = {
            isAuthenticated:false,
            resData: ''
        };
    }

    componentDidMount() {
        const payload = {
            "username": "hikaru",
            "password": "JohnSmith72-"
        };
        fetch(SIGNUP_URL, {
            method: 'POST',
            headers: {
                "Accept": "application/json",
                "Content-Type": "application/json"
            },
            body: JSON.stringify(payload)
        })
            .then(response => response.json())
            .then((data) => {
                console.log(data);
            });
        fetch(LOGIN_URL, {
            method: 'POST',
            headers: {
                "Accept": "application/json",
                "Content-Type": "application/json"
            },
            body: JSON.stringify(payload)
        })
            .then(response =>

                console.log(response.headers.get("Authorization")
            );

    }

In the above fetch call the /signup is successful and it the server outputs 'Tim is saved successfully', and the login is also successful but the token is sent in response and the 'console.log(response.headers.get("Authorization")) returns 'null'.

I am aware that with CORS fetch will not work for auth header as it can only be used for like 4 headers right?

I tried to add a method that loops through each entry and prints them, and then call it in the .then() as such:

.then(response => this.iterateThroughEntries(response)
        );

But this only printed the default headers, not the auth header.

Thanks

1 Answers

I managed to get the fetch to successfully receive the Authorization header from the server by updating the CorsSource in my WebSecurityConfig class by adding 'localhost:8080' 'localhost:3000' as an origin and by adding the Authorization header as an exposed header, so that it can be used by CORS requests.

configuration.addExposedHeader("Authorization");
Related