2

I am trying to login to API from react app and I keep getting the same 401 error. The warning in my spring boot console is:

2018-07-06 10:35:15.623  WARN 20328 --- [nio-8080-exec-1] c.s.s.JwtAuthorizationTokenFilter        : couldn't find bearer string, will ignore the header

The error in my react console is:

POST http://localhost:8080/api/user 401 ()
Login._this.handleSubmit @ Login.js:48
callCallback @ react-dom.development.js:100
invokeGuardedCallbackDev @ react-dom.development.js:138
invokeGuardedCallback @ react-dom.development.js:187
invokeGuardedCallbackAndCatchFirstError @ react-dom.development.js:201
executeDispatch @ react-dom.development.js:461
executeDispatchesInOrder @ react-dom.development.js:483
executeDispatchesAndRelease @ react-dom.development.js:581
executeDispatchesAndReleaseTopLevel @ react-dom.development.js:592
forEachAccumulated @ react-dom.development.js:562
runEventsInBatch @ react-dom.development.js:723
runExtractedEventsInBatch @ react-dom.development.js:732
handleTopLevel @ react-dom.development.js:4476
batchedUpdates$1 @ react-dom.development.js:16659
batchedUpdates @ react-dom.development.js:2131
dispatchEvent @ react-dom.development.js:4555
interactiveUpdates$1 @ react-dom.development.js:16714
interactiveUpdates @ react-dom.development.js:2150
dispatchInteractiveEvent @ react-dom.development.js:4532
Login.js:61 RESPONSE undefined
Login.js:48 Fetch failed loading: POST "http://localhost:8080/api/user".

I see I am not getting a response but I do not know why. Any help would be appreciated. Thanks!

Here is my login component.

class Login extends Component {

    state={
            username:'',
            password: ''
    };

    handleSubmit = event => {
        event.preventDefault();


        return fetch(API_BASE_URL+"/user", {
        method: 'POST',
        headers: {
            'Accept':'application/json',
            'Content-Type':'application/json'},
         body:JSON.stringify({
             'username': this.state.username,
             'password':this.state.password
         })
        })
        .then((response) => {
                response.json();
                localStorage.setItem(TOKEN_KEY, response.accessToken);
                console.log("RESPONSE", response.data);
        })
        .catch(error =>{
            console.log("ERROR: ", error);
        });
    }
}

Here's my UserController:

public class UserController {


@Value("${jwt.header}")
private String tokenHeader;

@Autowired
private JwtTokenUtil jwtTokenUtil;

@Autowired
@Qualifier("jwtUserDetailsService")
private UserDetailsService userDetailsService;

@RequestMapping(value = "/api/user", method = RequestMethod.GET)
public JwtUser getAuthenticatedUser(HttpServletRequest request) {
    String token = request.getHeader(tokenHeader).substring(7);
    String username = jwtTokenUtil.getUsernameFromToken(token);
    JwtUser user = (JwtUser) userDetailsService.loadUserByUsername(username);
    return user;
}

}

AuthenticationRestController File:

public class AuthenticationRestController {

@Value("${jwt.header}")
private String tokenHeader;

@Autowired
private AuthenticationManager authenticationManager;

@Autowired
private JwtTokenUtil jwtTokenUtil;

@Autowired
PasswordEncoder passwordEncoder;

@Autowired
@Qualifier("jwtUserDetailsService")
private UserDetailsService userDetailsService;

@Autowired
private SocialJUserRepository userRepository;

@RequestMapping(value = "${jwt.route.authentication.path}", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtAuthenticationRequest authenticationRequest) throws AuthenticationException {

    authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());

    // Reload password post-security so we can generate the token
    final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
    final String token = jwtTokenUtil.generateToken(userDetails);

    // Return the token
    return ResponseEntity.ok(new JwtAuthenticationResponse(token));
}

@RequestMapping(value = "${jwt.route.authentication.refresh}", method = RequestMethod.GET)
public ResponseEntity<?> refreshAndGetAuthenticationToken(HttpServletRequest request) {
    String authToken = request.getHeader(tokenHeader);
    final String token = authToken.substring(7);
    String username = jwtTokenUtil.getUsernameFromToken(token);
    JwtUser user = (JwtUser) userDetailsService.loadUserByUsername(username);

    if (jwtTokenUtil.canTokenBeRefreshed(token, user.getLastPasswordResetDate())) {
        String refreshedToken = jwtTokenUtil.refreshToken(token);
        return ResponseEntity.ok(new JwtAuthenticationResponse(refreshedToken));
    } else {
        return ResponseEntity.badRequest().body(null);
    }
}

@ExceptionHandler({AuthenticationException.class})
public ResponseEntity<String> handleAuthenticationException(AuthenticationException e) {
    return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage());
}

/**
 * Authenticates the user. If something is wrong, an {@link AuthenticationException} will be thrown
 */
private void authenticate(String username, String password) {
    Objects.requireNonNull(username);
    Objects.requireNonNull(password);


    try {
        authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
    } catch (DisabledException e) {
        throw new AuthenticationException("User is disabled!", e);
    } catch (BadCredentialsException e) {
        throw new AuthenticationException("Bad credentials!", e);
    }
}

@RequestMapping(value = "${jwt.route.signup.path}", method = RequestMethod.POST)
public ResponseEntity<?> registerUser(@Valid @RequestBody SignupRequest signupRequest) {
    if(userRepository.existsByUsername(signupRequest.getUsername())) {
        return new ResponseEntity(new ApiResponse(false, "Username is already taken!"),
                HttpStatus.BAD_REQUEST);
    }

    if(userRepository.existsByEmail(signupRequest.getEmail())) {
        return new ResponseEntity(new ApiResponse(false, "Email Address already in use!"),
                HttpStatus.BAD_REQUEST);
    }

    // Creating user's account
    socialjuser user = new socialjuser(signupRequest.getFirstname(), signupRequest.getLastname(), signupRequest.getUsername(),
            signupRequest.getEmail(), signupRequest.getPassword());

    user.setPassword(passwordEncoder.encode(user.getPassword()));


    socialjuser result = userRepository.save(user);

    URI location = ServletUriComponentsBuilder
            .fromCurrentContextPath().path("/api/users/{username}")
            .buildAndExpand(result.getUsername()).toUri();

    return ResponseEntity.created(location).body(new ApiResponse(true, "User registered successfully"));
}
2
  • 1
    Your controller action is defined as GET while, you are doing a POST from react. Also, the controller expects token to be part of the header, while none is being passed from react. from your code, getAuthenticatedUser can be called only after user has logged in and not for user-login Commented Jul 6, 2018 at 16:05
  • Can you post the your login controller from the spring-boot application? Commented Jul 6, 2018 at 16:13

1 Answer 1

1

Your getAuthenticatedUser API can be called only after login. So, handleSubmit method in Login Component needs to call a different end-point, like this:

fetch(API_BASE_URL+"/login", {
method: 'POST', // POST or GET depending on your definition inside controller
headers: {
    'Accept':'application/json',
    'Content-Type':'application/json'},
 body:JSON.stringify({
     'username': this.state.username,
     'password':this.state.password
 })
})
.then((response) => {
        response.json();
        localStorage.setItem(TOKEN_KEY, response.accessToken); //Persist
         console.log("RESPONSE", response.data);
})
.catch(error =>{
    console.log("ERROR: ", error);
});

The above call persists the authentication TOKEN in local storage. As, a next step the User Details of the logged in user can be fetched from your backend in following manner:

getUserDetails = () => {
    fetch(API_BASE_URL+"/user", {
    method: 'GET',
    headers: {
        'Authorization': `Bearer ${localStorage.getItem(TOKEN_KEY)}`,
        'Accept':'application/json',
        'Content-Type':'application/json'}
    })
    .then((response) => {
            console.log("RESPONSE", response.data); // <- Your User Details here.
    })
    .catch(error =>{
        console.log("ERROR: ", error);
    });
}

When you add the authorization header with "Bearer" prefix, the warning should go away, as it is exactly what it says.

Sign up to request clarification or add additional context in comments.

2 Comments

Ok I see....for the login controller do I pass in the request? If so how do I get the username and password from the request?
Yes @user9802794, depending upon your application you definetly need to pass username and password either as part of your request body or headers. Answering to the 2nd question... you are already reading the username and password from the request by this line of code authenticationRequest.getUsername(), the methods are already part of your JwtAuthenticationRequest Bean

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.