RESTful Authentication via Spring - java

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?

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.

You might consider Digest Access Authentication. Essentially the protocol is as follows:
Request is made from client
Server responds with a unique nonce string
Client supplies a username and password (and some other values) md5 hashed with the nonce; this hash is known as HA1
Server is then able to verify client's identity and serve up the requested materials
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.

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.

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

Related

Spring Security filters for JWT-based authentication, verification and authorization scheme, by example

Java + Spring (and Spring Security) here, interested in implementing a JWT-based auth mechanism for my web service using bearer tokens. My understanding of the proper way of using Spring Security for authentication and authorization is through the use of provided (or custom) filters as follows:
you specify which URLs in your app are authenticated (and thus require authenticated requests to access)
this is typically done in an #EnableWebSecurity-annotated web security class that extends WebSecurityConfigurerAdapter
for any unauthenticated URLs, no filters should block access to the resources being requested
an authentication filter effectively provides a "sign in" endpoint
request clients should hit this signin endpoint (authn filter) initially to obtain an auth token that can be used for making subsequent API calls
this filter should receive a type of "sign in request" object that contains a principal (e.g. username) and credential (e.g. password)
this authn filter should use the principal/credential contained in the sign in request to determine if they represents a valid user in the system
if so, an auth token (JWT, etc.) is generated and sent back to the requesters in the response somehow
else, if the principal/credential don't match a valid user in the system, an error response is returned and authentication fails
for authenticated URLs, a verification filter verifies that the request contains an auth token and that the auth token is valid (was signed correctly, contains user information such as JWT claims, is not expired, etc.)
if the auth token is valid, the request continues on to the authorization filter (see below)
else if the auth token is not valid, verification fails and the filter sends an error response back to the client
finally, an authorization filter verifies that the user associated with the valid auth token has the ability/permission to make such a request
if they do, then the request is allowed to continue on to whatever resources/controller was written to handle it, and that resource/controller provides the response back to the requester
if they don't, an error response is returned to the client
ideally the logic (code) inside this authz filter would have access to the permission annotations added to the resource method, so that I can add endpoints and specify permissions on them without having to modify the code of the authz filter
So to begin with, if anything I have stated above is a Spring Security (or web security in general) anti-pattern or is misled, please begin by providing course correction and steering me in the right direction!
Assuming I'm more or less understanding the "auth flow" above correctly...
Are there any specific Spring Security filters that take care of all of this for me already, or that can be extended and have a few methods overridden to behave this way? Or anything that comes really close? Looking at the list of authentication-specific Spring Security filters I see:
UsernamePasswordAuthenticationFilter -> looks like a decent candidate for the authn filter but expects a username and password parameter on the query string which is strange to me, and most importantly, does not generate a JWT
CasAuthenticationFilter -> looks like its used for CAS-based SSO and is not appropriate for use in non-SSO contexts
BasicAuthenticationFilter -> for HTTP basic authentication-based auth, not appropriate for more sophisticated setups
As for token verification and authorization, I (much to my surprise) don't see anything in the Spring Security landscape that could qualify.
Unless anyone knows of JWT-specific filters that I can use or subclass easily, I think I need to implement my own custom filters, in which case I'm wondering how to conigure Spring Security to use them and not use any of these other authentication filters (such as UsernamePasswordAuthenticationFilter) as part of the filter chain.
As I understand it, you want to:
Authenticate users via a username and password and respond with a JWT
On subsequent requests, authenticate users using that JWT
username/password -> JWT isn't an established authentication mechanism on its own, which is why Spring Security doesn't yet have direct support.
You can get it on your own pretty easily, though.
First, create a /token endpoint that produces a JWT:
#RestController
public class TokenController {
#Value("${jwt.private.key}")
RSAPrivateKey key;
#PostMapping("/token")
public String token(Authentication authentication) {
Instant now = Instant.now();
long expiry = 36000L;
// #formatter:off
String scope = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(" "));
JWTClaimsSet claims = new JWTClaimsSet.Builder()
.issuer("self")
.issueTime(new Date(now.toEpochMilli()))
.expirationTime(new Date(now.plusSeconds(expiry).toEpochMilli()))
.subject(authentication.getName())
.claim("scope", scope)
.build();
// #formatter:on
JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256).build();
SignedJWT jwt = new SignedJWT(header, claims);
return sign(jwt).serialize();
}
SignedJWT sign(SignedJWT jwt) {
try {
jwt.sign(new RSASSASigner(this.key));
return jwt;
}
catch (Exception ex) {
throw new IllegalArgumentException(ex);
}
}
}
Second, configure Spring Security to allow HTTP Basic (for the /token endpoint) and JWT (for the rest):
#Configuration
public class RestConfig extends WebSecurityConfigurerAdapter {
#Value("${jwt.public.key}")
RSAPublicKey key;
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.authorizeRequests((authz) -> authz.anyRequest().authenticated())
.csrf((csrf) -> csrf.ignoringAntMatchers("/token"))
.httpBasic(Customizer.withDefaults())
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
.sessionManagement((session) -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.exceptionHandling((exceptions) -> exceptions
.authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
.accessDeniedHandler(new BearerTokenAccessDeniedHandler())
);
// #formatter:on
}
#Bean
UserDetailsService users() {
// #formatter:off
return new InMemoryUserDetailsManager(
User.withUsername("user")
.password("{noop}password")
.authorities("app")
.build());
// #formatter:on
}
#Bean
JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withPublicKey(this.key).build();
}
}
I think there's appetite to add support for something like this in spring-authorization-server to reduce the /token boilerplate, if you're interested in contributing your efforts!

How to invoke a Spring Security Provider?

I have a web app that I am migrating to Grails 3.3.9 (from Grails 2.3.11). The current version of the application utilizes Spring Security LDAP for authentication (user sees login form when they try to access site and they type in username and password). The newer version of the application will utilize the Spring Security SAML plugin for authentication (commonly referred to as Single Sign On).
I have the Single Sign On (SSO) working however the SSO Login Page is only accessible when a user is at our office (has a certain IP address). In the cases where the user is not at our office (has an IP address not in our network). I would like the user to have the option to login with the Spring Security LDAP login form.
I'm sort of lost how to do it though. From the info I've gathered I need to define my security provider in application.groovy(I've used the default Spring Security providers as they seem to do the job individually). What I don't understand though is how do I tell Grails which of the two login methods to use per user. In my instance it would be checking the IP address of the user (which I already have the code for), but how do I then say, for example:
if(ipAddress matches internalIPRange) use samlAuthenticationProvider
else{use ldapAuthProvider}
Here is the provider set up in application.groovy
grails.plugin.springsecurity.providerNames = ['samlAuthenticationProvider', 'ldapAuthProvider', 'daoAuthenticationProvider', 'anonymousAuthenticationProvider']
Also I don't know how to actually call the provider manually (something like provider.invoke() if I had to guess).
It appears to be fairly easy to implement.
You extend the ExceptionTranslationFilter like so:
class IpAwareExceptionTranslationFilter extends ExceptionTranslationFilter {
AuthenticationEntryPoint ldapAuthenticationEntryPoint
#Override
void sendStartAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain,
AuthenticationException reason) throws ServletException, IOException {
SecurityContextHolder.context.authentication = null
requestCache.saveRequest request, response
logger.debug 'Calling Authentication entry point.'
if( isAllowedIpAddress( request ) )
authenticationEntryPoint.commence request, response, reason // default SAML
else
ldapAuthenticationEntryPoint.commence request, response, reason // LDAP
}
}
Then you declare your filter as a bean in resources.groovy:
beans = {
exceptionTranslationFilter( IpAwareExceptionTranslationFilter ){
authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint( '/saml/login' )
ldapAuthenticationEntryPoint = new LoginUrlAuthenticationEntryPoint( '/ldap/login' )
}
}
and it should replace the default exceptionTranslationFilter in the filter chain.

Spring Oauth Get Current User

I'm developing a system with 3 smaller projects, which are as follows:
a client
a ressource server
a authentication server
The authentication server has a register and a login page. The resource server is secured by the authentication server.
From the client I want to access the resource via REST API. The client is calling the resource server via OAuth2RestTemplate from Spring to access the resource. I managed to access the resource, after I authenticated myself.
Now to the problem. At the client I need to know the current user to display the the username and enabling the user to change his profile.
I tried to access the principal of the user via spring security with
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
but it just returned null.
So my question is: Is there a way to get the current logged in user with the OAuth2RestTemplate?
Edit:
So I decided to change the plan to implement a link in my authentication server, which returns the user information. The problem is, when I want to talk the authentication server via OAuth2RestTemplatethe authentication server just returns the login page. Everything works fine, when I call the page from browser or when I want to talk to the resource server via OAuth2RestTemplate.
Set a TokenEnhancer to your AuthorizationServerEndpointsConfigurer in Authorization server. You can add User information to the token as additional info map.
Here is a sample implementation of a custom TokenEnhancer,
public class CustomTokenEnhancer implements TokenEnhancer {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
final Map<String, Object> additionalInfo = new HashMap<String, Object>();
UserDetails user = (UserDetails) authentication.getPrincipal();
additionalInfo.put("<custom_user_info>", user.getUsername());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
In your Authorization server,
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenEnhancer(new CustomTokenEnhancer());
}
Add authentication object to security context holder after successfull authentication, by overriding method of class AbstractAuthenticationProcessingFilter
public void successfulAuthentication(
HttpServletRequest request,
HttpServletResponse response,
FilterChain chain,
Authentication authentication) throws IOException, ServletException
{
// Add the authentication to the Security context
SecurityContextHolder
.getContext()
.setAuthentication(authentication);
}

Spring security OpenID flow using JWT for authentication

I am trying to do authentication for my web application using widely used OpenId Connect flow.
I understand that the user who wants to authenticate on example1.com will first be redirected to the authentication server (or OP : "OpenId Provider"). The user authenticates on that server which then redirects him back to the original example1.com site with a signed JWT token.
Technical challenges:
I am suing spring security as part of my web application. I took a custom filter to redirect the user to authentication server and then it redirects back to my example1.com with valid JWT (Json Web Token).
My Custom Filter:
public class MyCustomFilter extends AbstractAuthenticationProcessingFilter {
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
//reading the AWT access token from reqest beahder
String token = request.getHeader("Authorization");
if(token == null){
// redirecting user to authentication server
String accessCodeUrl = "authentication.com?query string"
response.sendRedirect(accessCodeUrl);
} else{
response.addHeader("Authorization", token);
}
//Now how can i inject the above received token to spring security
}
}
My spring xml config:
<security:http auto-config="false" entry-point-ref="entryPoint" disable-url-rewriting="true">
<!-- set disabled to true to remove Cross Site Request Forger support -->
<security:csrf disabled="true" />
<security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER','ROLE_ADMIN')" />
<security:custom-filter ref="${MyCustomFilter }" position="PRE_AUTH_FILTER" />
<security:access-denied-handler error-page="${security.accessDeniedPage}"/>
<security:headers>
<security:frame-options policy="SAMEORIGIN"/>
</security:headers>
</security:http>
My custom filter is extending AbstractAuthenticationProcessingFilter. is it good fit for my requirement or should i consider extending any other filter's like BasicAuthenticationFilter or OncePerRequestFilter or GenericFilterBean.
I am confused here as to take 1 or 2 filters. because first time i am redirecting to authorization server, but from second time i should use the JWT token provided by authentication server.
Since i am going with stateless approach. I need to exchange JWT token every time between example.com and browser. I am using ExtJs as front end, what is the best practice to exchange JWT token between browser and example.com.
As per my understanding i should send JWT token every time from browser along with every request. Does that mean should i manually inject every time JWT token as part of Ajax request or Do we have some approach to append to all the outgoing Ajax requests.

Spring Security: OAuth2 custom filter to validate password expiration error handling

I've been trying to implement OAuth2 password expiration filter and I'm unsure about what the proper way would be to do so. The idea is as follows:
User tries to login.
User gets response with a header containing token if the password is expired.
User get's redirected to password change page using that token (i.e. /password-change/{token}).
He submits his old and new passwords, it gets changed.
Some rest controller retrieves user id by that token and does the rest password changing logic.
User should be redirected back to the initial login page where he logins with his new password (if he would be logged in instantly after the password change, he could navigate through secured pages even if the password would not be changed in background due to some exception, etc.).
So... I set a custom flag in user details for password expiration because I can't use credentialsNonExpired as it gets validated in DaoAuthenticationProvider and thrown as an exception which gets processed as InvalidGrantException which doesn't give me much control. I've figured out that in order to access user details right after it's authentication my filter should be in the inner Spring Security filter chain placed after OAuth2AuthenticationProcessingFilter:
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
...
http.addFilterAfter(new PasswordExpirationFilter(), BasicAuthenticationFilter.class
}
}
Why does my filter get placed after OAuth2AuthenticationProcessingFilter while there's no BasicAuthenticationFilter in the chain? I've digged through Spring Security and OAuth2 documentation and sources and couldn't find the right answer.
If that user's password is expired my filter generates some random string and it saves it to retrieve user details later during the password change request (at least it should be):
public class PasswordExpirationFilter extends OncePerRequestFilter implements Filter, InitializingBean {
private static final String TOKEN_HEADER = ...;
private ExpiredPasswordRepository repo; // gets set in a constructor and is basically holding a concurrent map of tokens
...
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
UserDetails details = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (details.isPasswordExpired) {
String uuid = UUID.randomUUID().toString();
repo.push(uuid, details.getId());
SecurityContextHolder.clearContext();
SecurityContextHolder.getContext().setAuthentication(null);
request.getSession(false).invalidate(); // don't create a new session
response.addHeader(TOKEN_HEADER, uuid);
response.sendError(HttpStatus.SC_PRECONDITION_FAILED, "Credentials have expired");
} else {
filterChain.doFilter(request, response);
}
}
}
Do I have to revoke the OAuth token as well? It gets reused in later requests and I keep getting the last userDetails object and therefore I keep getting the same response from my filter.
Is it even the right place to do all this validation? How should one validate the password for the concrete user and not the OAuth client?
Ok, I think I resolved this issue by revoking the access token via injected TokenStore in my filter (I used BearerTokenExtractor to get the token value) which seems pretty logical in this situtation. I still had no time to figure out, why my filter gets placed after OAuth2AuthenticationProcessingFilter, though.

Categories

Resources