RESTful Authentication via Spring

JavaSpringRestSpring MvcSpring Security

Java Problem Overview


Problem:
We have a Spring MVC-based RESTful API which contains sensitive information. The API should be secured, however sending the user's credentials (user/pass combo) with each request is not desirable. Per REST guidelines (and internal business requirements), the server must remain stateless. The API will be consumed by another server in a mashup-style approach.

Requirements:

  • Client makes a request to .../authenticate (unprotected URL) with credentials; server returns a secure token which contains enough information for the server to validate future requests and remain stateless. This would likely consist of the same information as Spring Security's Remember-Me Token.

  • Client makes subsequent requests to various (protected) URLs, appending the previously obtained token as a query parameter (or, less desirably, an HTTP request header).

  • Client cannot be expected to store cookies.

  • Since we use Spring already, the solution should make use of Spring Security.

We've been banging our heads against the wall trying to make this work, so hopefully someone out there has already solved this problem.

Given the above scenario, how might you solve this particular need?

Java Solutions


Solution 1 - Java

We managed to get this working exactly as described in the OP, and hopefully someone else can make use of the solution. Here's what we did:

Set up the security context like so:

<security:http realm="Protected API" use-expressions="true" auto-config="false" create-session="stateless" entry-point-ref="CustomAuthenticationEntryPoint">
	<security:custom-filter ref="authenticationTokenProcessingFilter" position="FORM_LOGIN_FILTER" />
	<security:intercept-url pattern="/authenticate" access="permitAll"/>
	<security:intercept-url pattern="/**" access="isAuthenticated()" />
</security:http>

<bean id="CustomAuthenticationEntryPoint"
	class="com.demo.api.support.spring.CustomAuthenticationEntryPoint" />

<bean id="authenticationTokenProcessingFilter"
	class="com.demo.api.support.spring.AuthenticationTokenProcessingFilter"	>
	<constructor-arg ref="authenticationManager" />
</bean>

As you can see, we've created a custom AuthenticationEntryPoint, which basically just returns a 401 Unauthorized if the request wasn't authenticated in the filter chain by our AuthenticationTokenProcessingFilter.

CustomAuthenticationEntryPoint:

public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
	@Override
	public void commence(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException authException) throws IOException, ServletException {
		response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized: Authentication token was either missing or invalid." );
	}
}

AuthenticationTokenProcessingFilter:

public class AuthenticationTokenProcessingFilter extends GenericFilterBean {

	@Autowired UserService userService;
	@Autowired TokenUtils tokenUtils;
	AuthenticationManager authManager;
	
	public AuthenticationTokenProcessingFilter(AuthenticationManager authManager) {
		this.authManager = authManager;
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		@SuppressWarnings("unchecked")
		Map<String, String[]> parms = request.getParameterMap();

		if(parms.containsKey("token")) {
			String token = parms.get("token")[0]; // grab the first "token" parameter
			
			// validate the token
			if (tokenUtils.validate(token)) {
				// determine the user based on the (already validated) token
				UserDetails userDetails = tokenUtils.getUserFromToken(token);
				// build an Authentication object with the user's info
				UsernamePasswordAuthenticationToken authentication = 
						new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword());
				authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails((HttpServletRequest) request));
				// set the authentication into the SecurityContext
				SecurityContextHolder.getContext().setAuthentication(authManager.authenticate(authentication));			
			}
		}
		// continue thru the filter chain
		chain.doFilter(request, response);
	}
}

Obviously, TokenUtils contains some privy (and very case-specific) code and can't be readily shared. Here's its interface:

public interface TokenUtils {
    String getToken(UserDetails userDetails);
    String getToken(UserDetails userDetails, Long expiration);
    boolean validate(String token);
    UserDetails getUserFromToken(String token);
}

That ought to get you off to a good start.

Solution 2 - Java

You might consider Digest Access Authentication. Essentially the protocol is as follows:

  1. Request is made from client
  2. Server responds with a unique nonce string
  3. Client supplies a username and password (and some other values) md5 hashed with the nonce; this hash is known as HA1
  4. Server is then able to verify client's identity and serve up the requested materials
  5. Communication with the nonce can continue until the server supplies a new nonce (a counter is used to eliminate replay attacks)

All of this communication is made through headers, which, as jmort253 points out, is generally more secure than communicating sensitive material in the url parameters.

Digest Access Authentication is supported by Spring Security. Notice that, although the docs say that you must have access to your client's plain-text password, you can successfully authenticate if you have the HA1 hash for your client.

Solution 3 - Java

Regarding tokens carrying information, JSON Web Tokens (http://jwt.io) is a brilliant technology. The main concept is to embed information elements (claims) into the token, and then signing the whole token so that the validating end can verify that the claims are indeed trustworthy.

I use this Java implementation: https://bitbucket.org/b_c/jose4j/wiki/Home

There is also a Spring module (spring-security-jwt), but I haven't looked into what it supports.

Solution 4 - Java

Why don't you start using OAuth with JSON WebTokens

http://projects.spring.io/spring-security-oauth/

OAuth2 is an standardized authorization protocol/framework. As per Official OAuth2 Specification:

You can find more info here

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChris CashwellView Question on Stackoverflow
Solution 1 - JavaChris CashwellView Answer on Stackoverflow
Solution 2 - JavaTim PoteView Answer on Stackoverflow
Solution 3 - JavaLeif JohnView Answer on Stackoverflow
Solution 4 - Javavaquar khanView Answer on Stackoverflow