Java rest API authentication, best practises with JWT

I'm using the JAX-RS framework, to make a restless backend for an application, and I have a resource, to authorize, user, as they login, and here is my code in my login resource so far:

@Path("authentication")
public class AuthenticationRessource {


private static Gson gson = new GsonBuilder().setPrettyPrinting().create();
private static AuthenticationFacade authenticationFacade = new AuthenticationFacade();

@GET
@Path("login")
@Produces(APPLICATION_JSON)
public Response authenticateUser(@FormParam("email") String email, @FormParam("password") String password) {

    try {
        //skal måske ændres til bruger
        User user = authenticationFacade.authenticateUser(email, password);

        String token = authenticationFacade.generateAuthenticationToken(user);

       return Response.ok().header(AUTHORIZATION, "Bearer " + token).build();

    } catch (Exception e) {
        Response.status(Response.Status.UNAUTHORIZED).build();
        System.err.print("det fuckede op, på grund af Thomas har ødelagt koden");
        e.printStackTrace();
    }
    return Response.status(Response.Status.UNAUTHORIZED).build();
}
}

right now I have a resource for authenticating, but maybe this belongs inside of my user resource, and I should assign a path, for authenticating for each user (user, superuser, admin etc.)

Should I add a provider annotation, for this resource, would it make a difference?

Is it the best practice to add AUTHORIZATION to the header, when I add the token?

Upvotes: 3

Views: 3043

Answers (1)

Derek
Derek

Reputation: 136

I will preface this by explaining that I model many of my decisions off of applications created by the JHipster generator: https://www.jhipster.tech/security/

Instead of returning AUTHORIZED or UNAUTHORIZED from the /authenticate endpoint, I return the JWT. The /authenticate endpoint is hit during the login step and the JWT is saved. See the following example:

@PostMapping("/authenticate")
@Timed
public ResponseEntity<JWTToken> authorize(@Valid @RequestBody LoginVM loginVM) {

    UsernamePasswordAuthenticationToken authenticationToken =
        new UsernamePasswordAuthenticationToken(loginVM.getUsername(), loginVM.getPassword());

    Authentication authentication = this.authenticationManager.authenticate(authenticationToken);
    SecurityContextHolder.getContext().setAuthentication(authentication);
    boolean rememberMe = (loginVM.isRememberMe() == null) ? false : loginVM.isRememberMe();
    String jwt = tokenProvider.createToken(authentication, rememberMe);
    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.add(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt);
    return new ResponseEntity<>(new JWTToken(jwt), httpHeaders, HttpStatus.OK);
}

I can then run the stored token through a filter any time an API endpoint is used to determine whether the user is still AUTHENTICATED or UNAUTHENTICATED:

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
    throws IOException, ServletException {
    HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
    String jwt = resolveToken(httpServletRequest);
    if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) {
        Authentication authentication = this.tokenProvider.getAuthentication(jwt);
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }
    filterChain.doFilter(servletRequest, servletResponse);
}

And to resolve the JWT token:

private String resolveToken(HttpServletRequest request){
    String bearerToken = request.getHeader(AUTHORIZATION_HEADER);
    if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
        return bearerToken.substring(7, bearerToken.length());
    }
    return null;
}

The AUTHORIZATION_HEADER is simply "Authorization":

public static final String AUTHORIZATION_HEADER = "Authorization";

There are some missing pieces I did not include in this response that you can probably infer, but if you are struggling I would encourage you to generate a JHipster monolith application and see first hand a good implementation of JWT which you can model your own implementation after. This is one of my go-tos for help with with Java, Spring, Maven applications.

Upvotes: 2

Related Questions