Skip to content

Commit

Permalink
JWT Token Expiration (#62)
Browse files Browse the repository at this point in the history
- The JWT authentication token will now expire at a given date. After that date, if a resource is accessed by suppling an expired token, a 401 Unauthorized response code will be returned, and a new token will have to be created with the 'dni' and 'password' credentials. The current expiration date for the token is 1 DAY from its creation.
- A unique JWT authentication token will be returned every time a POST is commited to /api/v1/login with the same credentials. This occurs because it is using the expiration date/time as a part of the token. It is not assured that the token will be unique every time, but it is unlikely to happen.
  • Loading branch information
gibarsin authored Jan 31, 2017
1 parent 0063175 commit 218be46
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
package ar.edu.itba.paw.webapp.auth;

import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;

import java.time.ZonedDateTime;
import java.util.Date;

/**
* Creates a JWT Token and retreives information from it
*/
public class JWTTokenHandler implements TokenHandler {

/**
* Key used to sign the JWT Token
*/
private static final String KEY = "B/1+wzD9jv8UwBZke9EZHfAclHDYre9msPR54UfhXmhx8OBQXrQb0URl9mBwGPBK\n" +
"ZExc4gxayeTxEPT3DIwMCZfCdMBVh4yjhzJ46AzummsrpyHeo2TgRtWRW+KCTtwo\n" +
"19kyrxbMpc8dEY9YO2ghNT0ZOcwtvEu13kzngevUsPV9pWsKdRXGgt5HV3rpuvJ7\n" +
Expand All @@ -30,31 +39,52 @@ public class JWTTokenHandler implements TokenHandler {
"vUf8FRscFkvo2EbtKPPLQiY6A1yuvrpobCgWj6TOKeujALF6jzdhepsk7sL2Qg1g\n" +
"6Nah/Tu9I0VOloJlciQenA==\n";

private UserDetailsService userDetailsServices = new UserDetailsServiceImpl();
private final int DAYS_TO_EXPIRE = 1;

/**
* Creates a new JWT token based on the current date/time and the username.
* This method creates the token independent of whether a token with the same username
* was created before the expiration time.
* The username is used as a subject.
* The expiration date is set with {@link #DAYS_TO_EXPIRE}. As the expiration date uses the current date,
* the token created will be, in general, different to any previous and future tokens created with the same username.
* @param username the username that may be used to create the token
* @return a JWT token with the specified parameters
*/
@Override
public String createToken(final UserDetails user) {
public String createToken(final String username) {
final ZonedDateTime now = ZonedDateTime.now();
final Date expirationDateTime = Date.from(now.plusDays(DAYS_TO_EXPIRE).toInstant()); // io.jsonwebtoken requires java.util.Date

final String token = Jwts.builder()
.setSubject(user.getUsername())
.setSubject(username)
.setExpiration(expirationDateTime)
.signWith(SignatureAlgorithm.HS512, KEY)
.compact();

return token;
}

/**
* Attempts to retrieve the username from the JWT token.
* @param token the JWT token
* @return the username that corresponds to the token
* null if a SignatureException or an ExpiredJwtException was raised.
* The former corresponds to an error when calculating/verifying the signature.
* The latter occurs when the token has expired.
*/
public String getUsername(final String token) {
String username = null;

try {
username = Jwts.parser()
final String username = Jwts.parser()
.setSigningKey(KEY)
.parseClaimsJws(token)
.getBody()
.getSubject();
} catch (SignatureException e) {

return username;
} catch (SignatureException | ExpiredJwtException e) {
return null;
}
return username;

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@
import java.util.Collection;
import java.util.HashSet;

/**
* Service which is used by custom-created filters (i.e. {@link StatelessAuthenticationFilter} , {@link StatelessLoginFilter}
* for handling Token Authentications.
*/
@Component
public class TokenAuthenticationService {
private static final String AUTH_HEADER = "X-AUTH-TOKEN";
private static final String AUTH_HEADER = "X-AUTH-TOKEN"; // Header used to retrieve the Authentication Token

@Autowired
UserDetailsService userDetailsService;
private UserDetailsService userDetailsService;

private final TokenHandler tokenHandler;

Expand All @@ -37,10 +41,10 @@ Authentication getAuthentication(final HttpServletRequest request) {
if(token != null) {
final String username = tokenHandler.getUsername(token);

if(username != null) {
if(username != null) { // There was not an error when trying to retrieve the username from the token

try {
final UserDetails user = userDetailsService.loadUserByUsername(username);

final Collection<GrantedAuthority> authorities = new HashSet<>(user.getAuthorities());

authentication = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), authorities);
Expand All @@ -53,15 +57,26 @@ Authentication getAuthentication(final HttpServletRequest request) {
return authentication;
}

/**
* In case that authentication is successful a response header is populated with a new token
* @param response the HTTP response to put the {@link #AUTH_HEADER} with the created token
* @param userDetails information of the user that may be used to put in the token.
*/
void addAuthentication(final HttpServletResponse response, final UserDetails userDetails) {
final String token = tokenHandler.createToken(userDetails);
final String token = tokenHandler.createToken(userDetails.getUsername());

response.setHeader(AUTH_HEADER, token);
}

/**
* Attempts to read the JSON Body from a POST /login HTTP Request and try to authenticate the user
* @param request the request which contains the JSON body with the credentials
* @return an instance of an authentication if the user exists;
* null in other case
*/
Authentication getAuthenticationForLogin(final HttpServletRequest request) {
try {
final UserLogDTO user = new ObjectMapper().readValue(request.getInputStream(), UserLogDTO.class);
final UserLogDTO user = new ObjectMapper().readValue(request.getInputStream(), UserLogDTO.class); // Attempt to map the JSON received in the body of /login endpoint to a UserLogDTO

final UserDetails userDetails = userDetailsService.loadUserByUsername(user.getDni());

Expand Down
15 changes: 12 additions & 3 deletions webapp/src/main/java/ar/edu/itba/paw/webapp/auth/TokenHandler.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
package ar.edu.itba.paw.webapp.auth;

import org.springframework.security.core.userdetails.UserDetails;

interface TokenHandler {
String createToken(UserDetails user);
/**
* Returns a new token used for further authentication and authorization
* @param username the username that may be used to create the token
* @return the token
*/
String createToken(String username);

/**
* Returns the username
* @param token
* @return the username contained in the token;
* null if there was an error when trying to get the username from the token
*/
String getUsername(String token);
}

0 comments on commit 218be46

Please sign in to comment.