All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.smallrye.jwt.build.impl.JwtClaimsBuilderImpl Maven / Gradle / Ivy

The newest version!
package io.smallrye.jwt.build.impl;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import javax.crypto.SecretKey;

import jakarta.json.JsonNumber;
import jakarta.json.JsonString;
import jakarta.json.JsonValue;

import org.eclipse.microprofile.jwt.Claims;
import org.jose4j.base64url.Base64;
import org.jose4j.jwk.JsonWebKey.OutputControlLevel;
import org.jose4j.jwk.PublicJsonWebKey;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.NumericDate;
import org.jose4j.jwx.HeaderParameterNames;
import org.jose4j.keys.X509Util;
import org.jose4j.lang.JoseException;

import io.smallrye.jwt.algorithm.SignatureAlgorithm;
import io.smallrye.jwt.build.JwtClaimsBuilder;
import io.smallrye.jwt.build.JwtEncryptionBuilder;
import io.smallrye.jwt.build.JwtSignatureBuilder;
import io.smallrye.jwt.build.JwtSignatureException;

/**
 * Default JWT Claims Builder
 *
 */
class JwtClaimsBuilderImpl extends JwtSignatureImpl implements JwtClaimsBuilder, JwtSignatureBuilder {

    private static final String SCOPE_CLAIM = "scope";
    private static final StringVerifier STRING_VERIFIER = new StringVerifier();
    private static final InstantVerifier INSTANT_VERIFIER = new InstantVerifier();
    private static final StringCollectionVerifier STRING_COLLECTION_VERIFIER = new StringCollectionVerifier();
    private static final Map REGISTERED_CLAIM_VERIFIERS;
    static {
        REGISTERED_CLAIM_VERIFIERS = new HashMap<>();
        REGISTERED_CLAIM_VERIFIERS.put(Claims.sub.name(), STRING_VERIFIER);
        REGISTERED_CLAIM_VERIFIERS.put(Claims.iss.name(), STRING_VERIFIER);
        REGISTERED_CLAIM_VERIFIERS.put(Claims.jti.name(), STRING_VERIFIER);
        REGISTERED_CLAIM_VERIFIERS.put(Claims.upn.name(), STRING_VERIFIER);
        REGISTERED_CLAIM_VERIFIERS.put(Claims.preferred_username.name(), STRING_VERIFIER);
        REGISTERED_CLAIM_VERIFIERS.put(Claims.iat.name(), INSTANT_VERIFIER);
        REGISTERED_CLAIM_VERIFIERS.put(Claims.auth_time.name(), INSTANT_VERIFIER);
        REGISTERED_CLAIM_VERIFIERS.put(Claims.exp.name(), INSTANT_VERIFIER);
        REGISTERED_CLAIM_VERIFIERS.put(Claims.aud.name(), STRING_COLLECTION_VERIFIER);
        REGISTERED_CLAIM_VERIFIERS.put(Claims.groups.name(), STRING_COLLECTION_VERIFIER);
    }

    JwtClaimsBuilderImpl() {

    }

    JwtClaimsBuilderImpl(String jsonLocation) {
        super(parseJsonToClaims(jsonLocation));
    }

    JwtClaimsBuilderImpl(Map claimsMap) {
        super(fromMapToJwtClaims(claimsMap));
    }

    JwtClaimsBuilderImpl(JwtClaims claims) {
        super(claims);
    }

    private static JwtClaims fromMapToJwtClaims(Map claimsMap) {
        JwtClaims claims = new JwtClaims();
        @SuppressWarnings("unchecked")
        Map newMap = (Map) prepareValue(claimsMap);
        for (Map.Entry entry : newMap.entrySet()) {
            claims.setClaim(entry.getKey(), entry.getValue());
        }
        return claims;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtClaimsBuilder claim(String name, Object value) {
        claims.setClaim(name, verifyValueType(name, prepareValue(value)));
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtClaimsBuilder issuer(String issuer) {
        claims.setIssuer(issuer);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtClaimsBuilder audience(String audience) {
        return audience(Collections.singleton(audience));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtClaimsBuilder audience(Set audiences) {
        claims.setAudience(audiences.stream().collect(Collectors.toList()));
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtClaimsBuilder subject(String subject) {
        claims.setSubject(subject);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtClaimsBuilder upn(String upn) {
        claims.setClaim(Claims.upn.name(), upn);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtClaimsBuilder preferredUserName(String preferredUserName) {
        claims.setClaim(Claims.preferred_username.name(), preferredUserName);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtClaimsBuilder issuedAt(long issuedAt) {
        claims.setIssuedAt(NumericDate.fromSeconds(issuedAt));
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtClaimsBuilder expiresAt(long expiresAt) {
        claims.setExpirationTime(NumericDate.fromSeconds(expiresAt));
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtClaimsBuilder expiresIn(long expiresIn) {
        tokenLifespan = expiresIn;
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtClaimsBuilder groups(Set groups) {
        claims.setClaim(Claims.groups.name(), groups.stream().collect(Collectors.toList()));
        return this;
    }

    @Override
    public JwtClaimsBuilder scope(Set scopes) {
        claims.setClaim(SCOPE_CLAIM, scopes.stream().collect(Collectors.joining(" ")));
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtSignatureBuilder jws() {
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtSignatureBuilder header(String name, Object value) {
        if (HeaderParameterNames.ALGORITHM.equals(name)) {
            return algorithm(toSignatureAlgorithm((String) value));
        } else {
            headers.put(name, value);
            return this;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtSignatureBuilder algorithm(SignatureAlgorithm algorithm) {
        headers.put(HeaderParameterNames.ALGORITHM, algorithm.getAlgorithm());
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtSignatureBuilder keyId(String keyId) {
        headers.put(HeaderParameterNames.KEY_ID, keyId);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtSignatureBuilder thumbprint(X509Certificate cert) {
        headers.put(HeaderParameterNames.X509_CERTIFICATE_THUMBPRINT, X509Util.x5t(cert));
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtSignatureBuilder thumbprintS256(X509Certificate cert) {
        headers.put(HeaderParameterNames.X509_CERTIFICATE_SHA256_THUMBPRINT, X509Util.x5tS256(cert));
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtSignatureBuilder chain(List chain) {
        List base64EncodedCerts = new ArrayList<>(chain.size());
        try {
            for (X509Certificate cert : chain) {
                base64EncodedCerts.add(Base64.encode(cert.getEncoded()));
            }
            headers.put(HeaderParameterNames.X509_CERTIFICATE_CHAIN, base64EncodedCerts);
        } catch (CertificateEncodingException ex) {
            throw ImplMessages.msg.signatureException(ex);
        }
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtSignatureBuilder jwk(PublicKey key) {
        headers.put(HeaderParameterNames.JWK, convertPublicKeyToJwk(key));
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtEncryptionBuilder innerSign(PrivateKey signingKey) throws JwtSignatureException {
        return super.innerSign(signingKey);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtEncryptionBuilder innerSign(SecretKey signingKey) throws JwtSignatureException {
        return super.innerSign(signingKey);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtEncryptionBuilder innerSign() throws JwtSignatureException {
        return super.innerSign();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JwtEncryptionBuilder jwe() {
        JwtBuildUtils.setDefaultJwtClaims(claims, tokenLifespan);
        try {
            return new JwtEncryptionImpl(claims.toJson());
        } finally {
            removeJti();
        }
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private static Object prepareValue(Object value) {
        if (value instanceof Collection) {
            return ((Collection) value).stream().map(o -> prepareValue(o)).collect(Collectors.toList());
        }

        if (value instanceof Map) {
            Map map = (Map) value;
            Map newMap = new LinkedHashMap<>();
            for (Map.Entry entry : map.entrySet()) {
                newMap.put(entry.getKey(), prepareValue(entry.getValue()));
            }
            return newMap;
        }

        if (value instanceof JsonValue) {
            return convertJsonValue((JsonValue) value);
        }

        if (value instanceof Number || value instanceof Boolean) {
            return value;
        }

        if (value instanceof Instant) {
            return ((Instant) value).getEpochSecond();
        }

        if (value instanceof PublicKey) {
            return convertPublicKeyToJwk((PublicKey) value);
        }

        return value.toString();
    }

    private static Object convertJsonValue(JsonValue jsonValue) {
        if (jsonValue instanceof JsonString) {
            String jsonString = jsonValue.toString();
            return jsonString.toString().substring(1, jsonString.length() - 1);
        } else if (jsonValue instanceof JsonNumber) {
            JsonNumber jsonNumber = (JsonNumber) jsonValue;
            if (jsonNumber.isIntegral()) {
                return jsonNumber.longValue();
            } else {
                return jsonNumber.doubleValue();
            }
        } else if (jsonValue == JsonValue.TRUE) {
            return true;
        } else if (jsonValue == JsonValue.FALSE) {
            return false;
        } else {
            return null;
        }
    }

    private static JwtClaims parseJsonToClaims(String jsonLocation) {
        return JwtBuildUtils.parseJwtClaims(jsonLocation);
    }

    private static SignatureAlgorithm toSignatureAlgorithm(String value) {
        try {
            return SignatureAlgorithm.fromAlgorithm(value);
        } catch (Exception ex) {
            throw ImplMessages.msg.unsupportedSignatureAlgorithm(value, ex);
        }
    }

    private static Object verifyValueType(String name, Object value) {
        ClaimTypeVerifier verifier = REGISTERED_CLAIM_VERIFIERS.get(name);
        return verifier == null ? value : verifier.verify(name, value);
    }

    static interface ClaimTypeVerifier {
        // Verify the claim value type
        Object verify(String name, Object value);
    }

    static class StringVerifier implements ClaimTypeVerifier {
        public Object verify(String name, Object value) {
            if (value instanceof String) {
                return value;
            }
            throw new IllegalArgumentException(String.format("'%s' claim value must be String", name));
        }
    }

    static class InstantVerifier implements ClaimTypeVerifier {
        public Object verify(String name, Object value) {
            if (value instanceof Long) {
                return value;
            }
            // If a Number is passed, it must be converted to long
            if (value instanceof Number) {
                return ((Number) value).longValue();
            }
            throw new IllegalArgumentException(String.format("'%s' claim value must be long", name));
        }
    }

    static class StringCollectionVerifier implements ClaimTypeVerifier {
        public Object verify(String name, Object value) {
            if (value instanceof String) {
                return value;
            } else if (value instanceof Collection) {
                Iterator it = ((Collection) value).iterator();
                if (it.hasNext() && it.next() instanceof String) {
                    return value;
                }
            }
            throw new IllegalArgumentException(String.format("'%s' claim value must be String or Collection of Strings", name));
        }
    }

    static Map convertPublicKeyToJwk(PublicKey key) {
        try {
            return PublicJsonWebKey.Factory.newPublicJwk(key).toParams(OutputControlLevel.PUBLIC_ONLY);
        } catch (JoseException ex) {
            throw ImplMessages.msg.signatureException(ex);
        }
    }

    @Override
    public JwtClaimsBuilder remove(String name) {
        claims.unsetClaim(name);
        return this;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy