| 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.REQUEST_INVALID, |
| "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; |
| } |
| |
| } |