blob: d8f33413fa24b83e7cbd9fa856b55379eaf6259c [file] [log] [blame]
package de.ids_mannheim.korap.config;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.joda.time.DateTime;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.JWTClaimsSet.Builder;
import com.nimbusds.jwt.SignedJWT;
import de.ids_mannheim.korap.exceptions.KustvaktException;
import de.ids_mannheim.korap.exceptions.StatusCodes;
import de.ids_mannheim.korap.security.context.TokenContext;
import de.ids_mannheim.korap.user.GenericUserData;
import de.ids_mannheim.korap.user.User;
import de.ids_mannheim.korap.user.Userdata;
import de.ids_mannheim.korap.utils.TimeUtils;
/**
* @author hanl
* @date 19/05/2014
*/
public class JWTSigner {
private static Logger jlog = LogManager.getLogger(JWTSigner.class);
public static boolean DEBUG = false;
private URL issuer;
private JWSSigner signer;
private JWSVerifier verifier;
private final int defaultttl;
public JWTSigner (final byte[] secret, URL issuer, final int defaulttl)
throws JOSEException {
this.issuer = issuer;
this.signer = new MACSigner(secret);
this.verifier = new MACVerifier(secret);
this.defaultttl = defaulttl;
}
public JWTSigner (final byte[] secret, String issuer)
throws MalformedURLException, JOSEException {
this(secret, new URL(issuer), 72 * 60 * 60);
}
public SignedJWT createJWT (User user, Map<String, Object> attr) {
return signContent(user, attr, defaultttl);
}
public SignedJWT signContent (User user, Map<String, Object> attr,
int ttl) {
String scopes;
Builder csBuilder = new JWTClaimsSet.Builder();
csBuilder.issuer(this.issuer.toString());
if ((scopes = (String) attr.get(Attributes.SCOPE)) != null) {
Userdata data = new GenericUserData();
data.readQuietly(attr, false);
Scopes claims = Scopes.mapScopes(scopes, data);
Map<String, Object> map = claims.toMap();
for (String key : map.keySet()) {
csBuilder.claim(key, map.get(key));
}
}
csBuilder.subject(user.getUsername());
if (attr.get(Attributes.CLIENT_ID) != null) {
csBuilder.audience((String) attr.get(Attributes.CLIENT_ID));
}
csBuilder.expirationTime(TimeUtils.getNow().plusSeconds(ttl).toDate());
csBuilder.claim(Attributes.AUTHENTICATION_TIME,
attr.get(Attributes.AUTHENTICATION_TIME));
JWTClaimsSet jwtClaimsSet = csBuilder.build();
if (DEBUG)
jlog.debug(jwtClaimsSet.getClaim(Attributes.AUTHENTICATION_TIME));
SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256),
jwtClaimsSet);
try {
signedJWT.sign(signer);
}
catch (JOSEException e) {
e.printStackTrace();
}
return signedJWT;
}
/**
* @param username
* @param json
* @return
*/
public SignedJWT signContent (String username, String userclient,
String json, int ttl) {
Builder cs = new JWTClaimsSet.Builder();
cs.subject(username);
if (!json.isEmpty())
cs.claim("data", json);
cs.expirationTime(TimeUtils.getNow().plusSeconds(ttl).toDate());
cs.issuer(this.issuer.toString());
if (!userclient.isEmpty())
cs.claim("userip", userclient);
SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256),
cs.build());
try {
signedJWT.sign(signer);
}
catch (JOSEException e) {
return null;
}
return signedJWT;
}
public SignedJWT signContent (String username, String userclient,
String json) {
return signContent(username, userclient, json, defaultttl);
}
public SignedJWT createSignedToken (String username) {
return createSignedToken(username, defaultttl);
}
// add client info
public SignedJWT createSignedToken (String username, int ttl) {
return signContent(username, "", "", ttl);
}
public SignedJWT verifyToken (String token) throws KustvaktException {
SignedJWT client;
try {
client = SignedJWT.parse(token);
if (!client.verify(verifier))
throw new KustvaktException(StatusCodes.INVALID_ACCESS_TOKEN,
"Json Web Signature (JWS) object verification failed.");
if (!new DateTime(client.getJWTClaimsSet().getExpirationTime())
.isAfterNow())
throw new KustvaktException(StatusCodes.EXPIRED,
"Authentication token is expired", token);
}
catch (ParseException | JOSEException e) {
// todo: message or entity, how to treat??!
throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT,
"Token could not be verified", token);
}
return client;
}
// does not care about expiration times
public String retrieveContent (String signedContent)
throws KustvaktException {
SignedJWT jwt;
try {
jwt = SignedJWT.parse(signedContent);
if (!jwt.verify(verifier))
throw new KustvaktException(StatusCodes.INVALID_REQUEST,
"token invalid", signedContent);
return jwt.getJWTClaimsSet().getStringClaim("data");
}
catch (ParseException | JOSEException e) {
return null;
}
}
public TokenContext getTokenContext (String idtoken)
throws ParseException, JOSEException, KustvaktException {
SignedJWT signedJWT = verifyToken(idtoken);
TokenContext c = new TokenContext();
c.setUsername(signedJWT.getJWTClaimsSet().getSubject());
List<String> audienceList = signedJWT.getJWTClaimsSet().getAudience();
if (audienceList != null && !audienceList.isEmpty())
c.addContextParameter(Attributes.CLIENT_ID,
signedJWT.getJWTClaimsSet().getAudience().get(0));
c.setExpirationTime(
signedJWT.getJWTClaimsSet().getExpirationTime().getTime());
Instant instant = Instant.ofEpochMilli((long) signedJWT
.getJWTClaimsSet().getClaim(Attributes.AUTHENTICATION_TIME));
ZonedDateTime zonedAuthTime = ZonedDateTime.ofInstant(instant,
ZoneId.of(Attributes.DEFAULT_TIME_ZONE));
c.setAuthenticationTime(zonedAuthTime);
c.setToken(idtoken);
c.addParams(signedJWT.getJWTClaimsSet().getClaims());
return c;
}
}