package org.libresonic.player.service; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.DecodedJWT; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.security.SecureRandom; import java.util.Date; public class JWTSecurityService { private static final Logger logger = LoggerFactory.getLogger(JWTSecurityService.class); public static final String JWT_PARAM_NAME = "jwt"; public static final String CLAIM_PATH = "path"; // TODO make this configurable public static final int DEFAULT_DAYS_VALID_FOR = 7; private static SecureRandom secureRandom = new SecureRandom(); private final SettingsService settingsService; public JWTSecurityService(SettingsService settingsService) { this.settingsService = settingsService; } public static String generateKey() { BigInteger randomInt = new BigInteger(130, secureRandom); return randomInt.toString(32); } public static Algorithm getAlgorithm(String jwtKey) { try { return Algorithm.HMAC256(jwtKey); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } private static String createToken(String jwtKey, String path, Date expireDate) { UriComponents components = UriComponentsBuilder.fromUriString(path).build(); String query = components.getQuery(); String claim = components.getPath() + (!StringUtils.isBlank(query) ? "?" + components.getQuery() : ""); logger.debug("Creating token with claim " + claim); return JWT.create() .withClaim(CLAIM_PATH, claim) .withExpiresAt(expireDate) .sign(getAlgorithm(jwtKey)); } public String addJWTToken(String uri) { return addJWTToken(UriComponentsBuilder.fromUriString(uri)).build().toString(); } public UriComponentsBuilder addJWTToken(UriComponentsBuilder builder) { return addJWTToken(builder, DateUtils.addDays(new Date(), DEFAULT_DAYS_VALID_FOR)); } public UriComponentsBuilder addJWTToken(UriComponentsBuilder builder, Date expires) { String token = JWTSecurityService.createToken( settingsService.getJWTKey(), builder.toUriString(), expires); builder.queryParam(JWTSecurityService.JWT_PARAM_NAME, token); return builder; } public static DecodedJWT verify(String jwtKey, String token) { Algorithm algorithm = JWTSecurityService.getAlgorithm(jwtKey); JWTVerifier verifier = JWT.require(algorithm).build(); return verifier.verify(token); } public DecodedJWT verify(String credentials) { return verify(settingsService.getJWTKey(), credentials); } }