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

io.smallrye.jwt.config.JWTAuthContextInfoProvider Maven / Gradle / Ivy

The newest version!
/*
 *   Copyright 2018 Red Hat, Inc, and individual contributors.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */
package io.smallrye.jwt.config;

import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.Dependent;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Inject;

import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.jwt.config.Names;

import io.smallrye.jwt.KeyFormat;
import io.smallrye.jwt.KeyProvider;
import io.smallrye.jwt.SmallryeJwtUtils;
import io.smallrye.jwt.algorithm.KeyEncryptionAlgorithm;
import io.smallrye.jwt.algorithm.SignatureAlgorithm;
import io.smallrye.jwt.auth.principal.JWTAuthContextInfo;
import io.smallrye.jwt.util.KeyUtils;
import io.smallrye.jwt.util.ResourceUtils;

/**
 * A CDI provider for the JWTAuthContextInfo that obtains the necessary information from
 * MP config properties.
 */
@Dependent
public class JWTAuthContextInfoProvider {
    private static final String AUTHORIZATION_HEADER = "Authorization";
    private static final String BEARER_SCHEME = "Bearer";
    private static final String NONE = "NONE";
    private static final String DEFAULT_GROUPS_SEPARATOR = " ";

    /**
     * Create JWTAuthContextInfoProvider with the public key and issuer
     *
     * @param publicKey the public key value
     * @param issuer the issuer
     * @return a new instance of JWTAuthContextInfoProvider
     */
    public static JWTAuthContextInfoProvider createWithKey(String publicKey, String issuer) {
        return create(publicKey, NONE, false, false, issuer, Optional.empty());
    }

    /**
     * Create JWTAuthContextInfoProvider with the decryption key and issuer
     *
     * @param decryptionKey the decryption key value
     * @param issuer the issuer
     * @return a new instance of JWTAuthContextInfoProvider
     */
    public static JWTAuthContextInfoProvider createWithDecryptionKey(String decryptionKey, String issuer) {
        return create(NONE, NONE, false, false, issuer, Optional.of(decryptionKey));
    }

    /**
     * Create JWTAuthContextInfoProvider with the verification public key location and issuer
     *
     * @param keyLocation the verification public key location
     * @param issuer the issuer
     * @return a new instance of JWTAuthContextInfoProvider
     */
    public static JWTAuthContextInfoProvider createWithKeyLocation(String keyLocation, String issuer) {
        return create(NONE, keyLocation, false, false, issuer, Optional.empty());
    }

    /**
     * Create JWTAuthContextInfoProvider with the verification public key location and issuer.
     * Tokens will be expected to contain either 'x5t' or 'x5t#S256' thumbprints.
     *
     * @param keyLocation certificate location which points to a PEM certificate or JWK containing the certificate chain
     * @param issuer the issuer
     * @return a new instance of JWTAuthContextInfoProvider
     */
    public static JWTAuthContextInfoProvider createWithCertificate(String keyLocation, String issuer) {
        return create(NONE, keyLocation, false, true, issuer, Optional.empty());
    }

    /**
     * Create JWTAuthContextInfoProvider with the verification secret key location and issuer
     *
     * @param keyLocation the verification secret key location
     * @param issuer the issuer
     * @return a new instance of JWTAuthContextInfoProvider
     */
    public static JWTAuthContextInfoProvider createWithSecretKeyLocation(String keyLocation, String issuer) {
        return create(NONE, keyLocation, true, false, issuer, Optional.empty());
    }

    /**
     * Create JWTAuthContextInfoProvider with the keystore and issuer
     *
     */
    public static JWTAuthContextInfoProvider createWithVerifyKeyStoreLocation(String keyLocation,
            Optional theKeyStorePassword,
            Optional theKeyStoreVerifyKeyAlias,
            Optional theKeyStoreDecryptKeyAlias, String issuer) {
        return create(NONE, keyLocation, Optional.empty(), Optional.empty(), theKeyStorePassword,
                theKeyStoreVerifyKeyAlias,
                theKeyStoreDecryptKeyAlias, false, false, issuer, Optional.empty());
    }

    /**
     * Create JWTAuthContextInfoProvider with the keystore and issuer
     *
     */
    public static JWTAuthContextInfoProvider createWithKeyStoreLocation(String keyLocation,
            Optional theKeyStorePassword,
            Optional theKeyStoreVerifyKeyAlias,
            Optional theKeyStoreDecryptKeyAlias, String issuer) {
        return create(NONE, keyLocation, Optional.empty(), Optional.empty(), theKeyStorePassword, theKeyStoreVerifyKeyAlias,
                theKeyStoreDecryptKeyAlias, false, false, issuer, Optional.empty());
    }

    public static JWTAuthContextInfoProvider create(String key,
            String keyLocation,
            boolean secretKey,
            boolean verifyCertificateThumbprint,
            String issuer,
            Optional decryptionKey) {
        return create(key, keyLocation, Optional.empty(), Optional.empty(), Optional.empty(),
                Optional.empty(), Optional.empty(), secretKey, verifyCertificateThumbprint, issuer, decryptionKey);
    }

    private static JWTAuthContextInfoProvider create(String key,
            String keyLocation,
            Optional theKeyStoreType,
            Optional theKeyStoreProvider,
            Optional theKeyStorePassword,
            Optional theKeyStoreVerifyKeyAlias,
            Optional theKeyStoreDecryptKeyAlias,
            boolean secretKey,
            boolean verifyCertificateThumbprint,
            String issuer,
            Optional decryptionKey) {
        JWTAuthContextInfoProvider provider = new JWTAuthContextInfoProvider();
        provider.mpJwtPublicKey = !secretKey ? key : NONE;
        provider.jwtSecretKey = secretKey ? key : NONE;
        provider.mpJwtPublicKeyAlgorithm = Set.of(SignatureAlgorithm.RS256);
        provider.mpJwtLocation = !secretKey && !theKeyStoreDecryptKeyAlias.isPresent() ? keyLocation : NONE;
        provider.verifyKeyLocation = secretKey ? keyLocation : NONE;
        provider.verifyCertificateThumbprint = verifyCertificateThumbprint;
        provider.mpJwtIssuer = issuer;
        provider.mpJwtDecryptKeyLocation = theKeyStoreDecryptKeyAlias.isPresent() ? keyLocation : NONE;
        provider.jwtDecryptKey = decryptionKey;
        provider.decryptionKeyLocation = NONE;
        provider.mpJwtTokenHeader = Optional.of(AUTHORIZATION_HEADER);
        provider.mpJwtTokenCookie = Optional.of(BEARER_SCHEME);
        provider.tokenHeader = provider.mpJwtTokenHeader;
        provider.tokenCookie = provider.mpJwtTokenCookie;
        provider.tokenKeyId = Optional.empty();
        provider.tokenDecryptionKeyId = Optional.empty();
        provider.tokenSchemes = BEARER_SCHEME;
        provider.requireNamedPrincipal = Optional.of(Boolean.TRUE);
        provider.defaultSubClaim = Optional.empty();
        provider.subPath = Optional.empty();
        provider.defaultGroupsClaim = Optional.empty();
        provider.groupsPath = Optional.empty();
        provider.expGracePeriodSecs = 0;
        provider.maxTimeToLiveSecs = Optional.empty();
        provider.mpJwtVerifyClockSkew = 60;
        provider.mpJwtVerifyTokenAge = Optional.empty();
        provider.jwksRefreshInterval = 60;
        provider.forcedJwksRefreshInterval = 30;
        provider.signatureAlgorithm = Optional.of(SignatureAlgorithm.RS256);
        provider.keyEncryptionAlgorithm = Optional.empty();
        provider.mpJwtDecryptKeyAlgorithm = new HashSet<>(Arrays.asList(KeyEncryptionAlgorithm.RSA_OAEP,
                KeyEncryptionAlgorithm.RSA_OAEP_256));
        provider.keyFormat = KeyFormat.ANY;
        provider.keyProvider = KeyProvider.DEFAULT;
        provider.mpJwtVerifyAudiences = Optional.empty();
        provider.expectedAudience = Optional.empty();
        provider.groupsSeparator = DEFAULT_GROUPS_SEPARATOR;
        provider.requiredClaims = Optional.empty();
        provider.tlsCertificate = Optional.empty();
        provider.tlsCertificatePath = Optional.empty();
        provider.tlsTrustedHosts = Optional.empty();
        provider.httpProxyHost = Optional.empty();
        provider.httpProxyPort = 80;
        provider.keyStoreType = theKeyStoreType;
        provider.keyStoreProvider = theKeyStoreProvider;
        provider.keyStorePassword = theKeyStorePassword;
        provider.keyStoreVerifyKeyAlias = theKeyStoreVerifyKeyAlias;
        provider.keyStoreDecryptKeyAlias = theKeyStoreDecryptKeyAlias;
        provider.keyStoreDecryptKeyPassword = Optional.empty();

        return provider;
    }
    // The MP-JWT spec defined configuration properties

    /**
     * @since 1.1
     */
    @Inject
    @ConfigProperty(name = "mp.jwt.verify.publickey", defaultValue = NONE)
    private String mpJwtPublicKey;

    @Inject
    @ConfigProperty(name = "smallrye.jwt.verify.secretkey", defaultValue = NONE)
    private String jwtSecretKey;
    /**
     * @since 1.2
     */
    @Inject
    @ConfigProperty(name = "mp.jwt.verify.publickey.algorithm", defaultValue = "RS256")
    private Set mpJwtPublicKeyAlgorithm = Set.of(SignatureAlgorithm.RS256);
    /**
     * @since 1.1
     */
    @Inject
    @ConfigProperty(name = "mp.jwt.verify.issuer", defaultValue = NONE)
    private String mpJwtIssuer;
    /**
     * @since 1.1
     */
    @Inject
    @ConfigProperty(name = "mp.jwt.verify.publickey.location", defaultValue = NONE)
    private String mpJwtLocation;
    /**
     * @since 1.2
     */
    @Inject
    @ConfigProperty(name = "mp.jwt.decrypt.key.location", defaultValue = NONE)
    private String mpJwtDecryptKeyLocation;

    /**
     * @since 2.1
     */
    @Inject
    @ConfigProperty(name = "mp.jwt.decrypt.key.algorithm", defaultValue = "RSA_OAEP,RSA_OAEP_256")
    private Set mpJwtDecryptKeyAlgorithm = new HashSet<>(Arrays.asList(KeyEncryptionAlgorithm.RSA_OAEP,
            KeyEncryptionAlgorithm.RSA_OAEP_256));;

    @Inject
    @ConfigProperty(name = "smallrye.jwt.decrypt.key")
    private Optional jwtDecryptKey;

    /**
     * Verification key location.
     * This property can point to both public and secret keys and if it is set then 'mp.jwt.verify.publickey.location' will be
     * ignored.
     * Note that the secret keys will only recognized in the JWK format.
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.verify.key.location", defaultValue = NONE)
    private String verifyKeyLocation;

    /**
     * Decryption key location
     *
     * @deprecated Use {@link JWTAuthContextInfoProvider#mpJwtDecryptKeyLocation}
     */
    @Deprecated
    @Inject
    @ConfigProperty(name = "smallrye.jwt.decrypt.key.location", defaultValue = NONE)
    private String decryptionKeyLocation;

    /**
     * Supported JSON Web Algorithm encryption algorithm.
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.decrypt.algorithm")
    @Deprecated
    private Optional keyEncryptionAlgorithm;

    /**
     * @since 1.2
     */
    @Inject
    @ConfigProperty(name = Names.TOKEN_HEADER)
    private Optional mpJwtTokenHeader;

    /**
     * @since 1.2
     */
    @Inject
    @ConfigProperty(name = Names.TOKEN_COOKIE)
    private Optional mpJwtTokenCookie;

    /**
     * @since 1.2
     */
    @Inject
    @ConfigProperty(name = "mp.jwt.verify.audiences")
    Optional> mpJwtVerifyAudiences;

    /**
     * @since 2.1
     */
    @Inject
    @ConfigProperty(name = "mp.jwt.verify.clock.skew", defaultValue = "60")
    private int mpJwtVerifyClockSkew;

    /**
     * @since 2.1
     */
    @Inject
    @ConfigProperty(name = "mp.jwt.verify.token.age")
    Optional mpJwtVerifyTokenAge;

    // SmallRye JWT specific properties
    /**
     * HTTP header which is expected to contain a JWT token, default value is 'Authorization'
     *
     * @deprecated Use {@link JWTAuthContextInfoProvider#mpJwtTokenHeader}
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.token.header")
    @Deprecated
    private Optional tokenHeader;

    /**
     * Cookie name containing a JWT token. This property is ignored unless the "smallrye.jwt.token.header" is set to 'Cookie'
     *
     * @deprecated Use {@link JWTAuthContextInfoProvider#mpJwtTokenCookie}
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.token.cookie")
    @Deprecated
    private Optional tokenCookie;

    /**
     * If `true` then `Authorization` header will be checked even if the `smallrye.jwt.token.header` is set to `Cookie` but no
     * cookie with a `smallrye.jwt.token.cookie` name exists.
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.always-check-authorization", defaultValue = "false")
    private boolean alwaysCheckAuthorization;

    /**
     * Verification key identifier ('kid'). If it is set then if a signed JWT token contains 'kid' then both values must
     * match.
     * It will also be used to select a verification JWK key from a JWK set.
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.token.kid")
    private Optional tokenKeyId;

    /**
     * Decryption key identifier ('kid'). If it is set then if an encrypted JWT token contains 'kid' then both values must
     * match.
     * It will also be used to select a decryption JWK key from a JWK set.
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.token.decryption.kid")
    private Optional tokenDecryptionKeyId;

    /**
     * The scheme used with an HTTP Authorization header.
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.token.schemes", defaultValue = BEARER_SCHEME)
    private String tokenSchemes;

    /**
     * Check that the JWT has at least one of 'sub', 'upn' or 'preferred_user_name' set. If not the JWT validation will
     * fail.
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.require.named-principal", defaultValue = "true")
    private Optional requireNamedPrincipal = Optional.of(Boolean.TRUE);

    /**
     * Default subject claim value. This property can be used to support the JWT tokens without a 'sub' claim.
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.claims.sub")
    private Optional defaultSubClaim;

    /**
     * Path to the claim containing the sub. It starts from the top level JSON object and
     * can contain multiple segments where each segment represents a JSON object name only, example: "realm/sub".
     * Use double quotes with the namespace qualified claim names.
     * This property can be used if a token has no 'sub' claim but has the sub set in a different claim.
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.path.sub")
    private Optional subPath;

    /**
     * Default groups claim value. This property can be used to support the JWT tokens without a 'groups' claim.
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.claims.groups")
    private Optional defaultGroupsClaim;

    /**
     * Path to the claim containing an array of groups. It starts from the top level JSON object and
     * can contain multiple segments where each segment represents a JSON object name only, example: "realm/groups".
     * Use double quotes with the namespace qualified claim names.
     * This property can be used if a token has no 'groups' claim but has the groups set in a different claim.
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.path.groups")
    private Optional groupsPath;

    /**
     * Separator for splitting a string which may contain multiple group values.
     * It will only be used if the "smallrye.jwt.path.groups" property points to a custom claim whose value is a string.
     * The default value is a single space because the standard 'scope' claim may contain a space separated sequence.
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.groups-separator", defaultValue = DEFAULT_GROUPS_SEPARATOR)
    private String groupsSeparator;

    /**
     * @deprecated Use {@link JWTAuthContextInfoProvider#mpJwtVerifyClockSkew} instead
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.expiration.grace", defaultValue = "0")
    @Deprecated
    private int expGracePeriodSecs;

    /**
     * The maximum number of seconds that a JWT may be issued for use. Effectively, the difference
     * between the expiration date of the JWT and the issued at date must not exceed this value.
     * Note that setting this property to a non-positive value relaxes the requirement for the token to have a valid 'iat'
     * (issued at) claim.
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.time-to-live")
    Optional maxTimeToLiveSecs;

    /**
     * JWK cache refresh interval in minutes. It will be ignored unless the 'mp.jwt.verify.publickey.location' property points
     * to the HTTP or HTTPS URL based JWK set.
     * Note this property will only be used if no HTTP Cache-Control response header with a positive 'max-age' parameter value
     * is available.
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.jwks.refresh-interval", defaultValue = "60")
    private int jwksRefreshInterval;

    /**
     * Forced JWK cache refresh interval in minutes which is used to restrict the frequency of the forced refresh attempts which
     * may happen when the token verification fails due to the cache having no JWK key with a 'kid' property matching the
     * current token's 'kid' header.
     * It will be ignored unless the 'mp.jwt.verify.publickey.location' points to the HTTP or HTTPS URL based JWK set.
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.jwks.forced-refresh-interval", defaultValue = "30")
    private int forcedJwksRefreshInterval;

    /**
     * Supported JSON Web Algorithm asymmetric or symmetric signature algorithm.
     *
     * This property should only be used for setting a required symmetric algorithm such as 'HS256'.
     * It is deprecated for setting asymmetric algorithms such as 'ES256' - use {@link #mpJwtPublicKeyAlgorithm} instead.
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.verify.algorithm")
    private Optional signatureAlgorithm;

    /**
     * Verify the certificate thumbprint.
     * If this property is enabled then a signed token must contain either 'x5t' or 'x5t#256' X509Certificate thumbprint
     * headers.
     * Verification keys can only be in JWK or PEM Certificate key formats in this case.
     * JWK keys must have a 'x5c' (Base64-encoded X509Certificate) property set.
     * Note that 'smallrye.jwt.token.kid' property will have no effect as 'x5t' and 'x5t#S256'
     * are the key identifiers when this property is set.
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.verify.certificateThumbprint", defaultValue = "false")
    private boolean verifyCertificateThumbprint;

    /**
     * Supported key format. By default a key can be in any of the supported formats:
     * PEM key, PEM certificate, JWK key set or single JWK (possibly Base64URL-encoded).
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.verify.key-format", defaultValue = "ANY")
    private KeyFormat keyFormat;

    /**
     * Supported verification key provider.
     */
    @Inject
    @ConfigProperty(name = "smallrye.jwt.verify.key-provider", defaultValue = "DEFAULT")
    private KeyProvider keyProvider;

    /**
     * Key cache size.
     * If the verification key resolver acquires keys dynamically on every request then it may
     * use this property, as well as {@link #keyCacheTimeToLive}, to control the key cache.
     * 

* For example, setting `smallrye.jwt.verify.key-provider=AWS_ALB` will activate * AWS ALB verification key resolver which will use the current token `kid` header value * to fetch the key from the AWS ALB key endpoint. This resolve will need to cache the acquired * keys and control the cache size. *

* If the maximum key cache size has been reached then the cache entries which have been in the cache * for longer than the configured {@link #keyCacheTimeToLive} duration, should be removed and a new * key added instead. If the cache size can not be reduced then the key resolver should * return this key without caching it. */ @Inject @ConfigProperty(name = "smallrye.jwt.key-cache-size", defaultValue = "100") private int keyCacheSize; /** * Key cache entry time-to-live duration in minutes. * If the verification key resolver acquires keys dynamically on every request then it may * use this property, as well as {@link #keyCacheSize}, to control the key cache. *

* For example, setting `smallrye.jwt.verify.key-provider=AWS_ALB` will activate * AWS ALB verification key resolver which will use the current token `kid` header value * to fetch the key from the AWS ALB key endpoint. This resolve will need to cache the acquired * keys. *

* If the maximum key cache size {@link #keyCacheSize} has been reached then the cache entries which have been in the cache * for longer than the configured time-to-live duration, should be removed and a new * key added instead. If the cache size can not be reduced then the key resolver should * return this key without caching it. */ @Inject @ConfigProperty(name = "smallrye.jwt.key-cache-time-to-live", defaultValue = "10") private int keyCacheTimeToLive; /** * Relax the validation of the verification keys. * Public RSA keys with the 1024 bit length will be allowed if this property is set to 'true'. */ @Inject @ConfigProperty(name = "smallrye.jwt.verify.relax-key-validation", defaultValue = "true") private boolean relaxVerificationKeyValidation = true; /** * The audience value(s) that identify valid recipient(s) of a JWT. Audience validation * will succeed, if any one of the provided values is equal to any one of the values of * the "aud" claim in the JWT. The config value should be specified as a comma-separated * list per MP Config requirements for a collection property. * * @since 2.0.3 * @deprecated Use {@link JWTAuthContextInfoProvider#mpJwtVerifyAudiences} */ @Inject @ConfigProperty(name = "smallrye.jwt.verify.aud") @Deprecated Optional> expectedAudience; /** * List of claim names that must be present in the JWT for it to be valid. The configuration should be specified * as a comma-separated list. */ @Inject @ConfigProperty(name = "smallrye.jwt.required.claims") Optional> requiredClaims; /** * TLS Trusted Certificate. * If this property is set then the 'smallrye.jwt.client.tls.certificate.path' will be ignored. */ @Inject @ConfigProperty(name = "smallrye.jwt.client.tls.certificate") private Optional tlsCertificate; /** * TLS Trusted Certificate Path. * This property will be ignored if the 'smallrye.jwt.client.tls.certificate' is set. */ @Inject @ConfigProperty(name = "smallrye.jwt.client.tls.certificate.path") private Optional tlsCertificatePath; /** * TLS Trust All. * If this property is set to 'true' then HTTPS HostnameVerifier will trust all the hostnames. */ @Inject @ConfigProperty(name = "smallrye.jwt.client.tls.trust-all", defaultValue = "false") private boolean tlsTrustAll; /** * TLS Trusted Hosts. Set this property if `smallrye.jwt.client.tls.trust-all` property is disabled. */ @Inject @ConfigProperty(name = "smallrye.jwt.client.tls.hosts") private Optional> tlsTrustedHosts; /** * HTTP Proxy Host. */ @Inject @ConfigProperty(name = "smallrye.jwt.http.proxy.host") private Optional httpProxyHost; /** * HTTP Proxy Port. */ @Inject @ConfigProperty(name = "smallrye.jwt.http.proxy.port", defaultValue = "80") private int httpProxyPort = 80; /** * Key store type. If not given, the type is automatically detected * based on the file name. */ @ConfigProperty(name = "smallrye.jwt.keystore.type") private Optional keyStoreType = Optional.empty(); /** * Key store provider. If not given, the provider is automatically detected * based on the key store file type. */ @ConfigProperty(name = "smallrye.jwt.keystore.provider") private Optional keyStoreProvider = Optional.empty(); /** * Key store password. */ @ConfigProperty(name = "smallrye.jwt.keystore.password") private Optional keyStorePassword = Optional.empty(); /** * Key store verification key alias. Public verification key will be extracted from a matching certificate. */ @ConfigProperty(name = "smallrye.jwt.keystore.verify.key.alias") private Optional keyStoreVerifyKeyAlias = Optional.empty(); /** * Key store decryption key alias. */ @ConfigProperty(name = "smallrye.jwt.keystore.decrypt.key.alias") private Optional keyStoreDecryptKeyAlias = Optional.empty(); /** * Key store decryption key password, in case it's different from {@link #keyStorePassword}. */ @ConfigProperty(name = "smallrye.jwt.keystore.decrypt.key.password") private Optional keyStoreDecryptKeyPassword = Optional.empty(); /** * Obtain remote keys on startup. */ @ConfigProperty(name = "smallrye.jwt.resolve-remote-keys-at-startup", defaultValue = "false") private boolean fetchRemoteKeysOnStartup = false; @Produces Optional getOptionalContextInfo() { String resolvedVerifyKeyLocation = !NONE.equals(verifyKeyLocation) ? verifyKeyLocation : mpJwtLocation; // Log the config values JWTAuthContextInfo contextInfo = new JWTAuthContextInfo(); if (mpJwtIssuer != null && !mpJwtIssuer.equals(NONE)) { contextInfo.setIssuedBy(mpJwtIssuer.trim()); } final boolean verificationPublicKeySet = !NONE.equals(mpJwtPublicKey); final boolean verificationSecretKeySet = !NONE.equals(jwtSecretKey); final boolean verificationKeyLocationSet = !NONE.equals(resolvedVerifyKeyLocation); if (verificationPublicKeySet) { contextInfo.setPublicKeyContent(mpJwtPublicKey); if (verificationKeyLocationSet || verificationSecretKeySet) { ConfigLogging.log.publicKeyConfiguredButOtherKeyPropertiesAreAlsoUsed(); } } else if (verificationSecretKeySet) { contextInfo.setSecretKeyContent(jwtSecretKey); if (verificationKeyLocationSet) { ConfigLogging.log.secretKeyConfiguredButKeyLocationIsAlsoUsed(); } } else if (verificationKeyLocationSet) { String resolvedVerifyKeyLocationTrimmed = resolvedVerifyKeyLocation.trim(); if (resolvedVerifyKeyLocationTrimmed.startsWith("http")) { if (fetchRemoteKeysOnStartup) { try { InputStream is = ResourceUtils.getResourceStream(resolvedVerifyKeyLocationTrimmed); if (is != null) { try (InputStream keyStream = is) { String KeyContent = new String(ResourceUtils.readBytes(keyStream)); contextInfo.setPublicKeyContent(KeyContent); } if (contextInfo.getPublicKeyContent() == null) { throw ConfigMessages.msg.invalidPublicKeyLocation(); } } } catch (Exception ex) { throw ConfigMessages.msg.readingPublicKeyLocationFailed(ex); } } else { contextInfo.setPublicKeyLocation(resolvedVerifyKeyLocationTrimmed); } } else { if (isPublicKeyInKeystore()) { try { contextInfo.setPublicVerificationKey(getVerificationKeyFromKeystore(resolvedVerifyKeyLocationTrimmed)); } catch (Exception ex) { throw ConfigMessages.msg.readingPublicKeyLocationFailed(ex); } } else { try { contextInfo.setPublicKeyContent(ResourceUtils.readResource(resolvedVerifyKeyLocationTrimmed)); if (contextInfo.getPublicKeyContent() == null) { throw ConfigMessages.msg.invalidPublicKeyLocation(); } } catch (IOException ex) { throw ConfigMessages.msg.readingPublicKeyLocationFailed(ex); } } } } else if (isPublicKeyInKeystore()) { try { contextInfo.setPublicVerificationKey(getVerificationKeyFromKeystore(null)); } catch (Exception ex) { throw ConfigMessages.msg.readingPublicKeyLocationFailed(ex); } } final String theDecryptionKeyLocation; if (!NONE.equals(mpJwtDecryptKeyLocation)) { theDecryptionKeyLocation = mpJwtDecryptKeyLocation; } else if (!NONE.equals(decryptionKeyLocation)) { ConfigLogging.log.replacedConfig("smallrye.jwt.decrypt.key.location", "mp.jwt.decrypt.key.location"); theDecryptionKeyLocation = decryptionKeyLocation; } else { theDecryptionKeyLocation = NONE; } if (jwtDecryptKey.isPresent()) { contextInfo.setDecryptionKeyContent(jwtDecryptKey.get()); } else if (!NONE.equals(theDecryptionKeyLocation)) { String decryptionKeyLocationTrimmed = theDecryptionKeyLocation.trim(); if (decryptionKeyLocationTrimmed.startsWith("http")) { if (fetchRemoteKeysOnStartup) { try { InputStream is = ResourceUtils.getResourceStream(decryptionKeyLocationTrimmed); if (is != null) { try (InputStream keyStream = is) { String KeyContent = new String(ResourceUtils.readBytes(keyStream)); contextInfo.setDecryptionKeyContent(KeyContent); } if (contextInfo.getDecryptionKeyContent() == null) { throw ConfigMessages.msg.invalidDecryptKeyLocation(); } } } catch (Exception ex) { throw ConfigMessages.msg.readingDecryptKeyLocationFailed(ex); } } else { contextInfo.setDecryptionKeyLocation(decryptionKeyLocationTrimmed); } } else { if (isPrivateKeyInKeystore()) { try { contextInfo.setPrivateDecryptionKey(getDecryptionKeyFromKeystore(decryptionKeyLocationTrimmed)); } catch (Exception ex) { throw ConfigMessages.msg.readingDecryptKeyLocationFailed(ex); } } else { try { contextInfo.setDecryptionKeyContent(ResourceUtils.readResource(decryptionKeyLocationTrimmed)); if (contextInfo.getDecryptionKeyContent() == null) { throw ConfigMessages.msg.invalidDecryptKeyLocation(); } } catch (IOException ex) { throw ConfigMessages.msg.readingDecryptKeyLocationFailed(ex); } } } } else if (isPrivateKeyInKeystore()) { try { contextInfo.setPrivateDecryptionKey(getDecryptionKeyFromKeystore(null)); } catch (Exception ex) { throw ConfigMessages.msg.readingDecryptKeyLocationFailed(ex); } } if (mpJwtTokenHeader.isPresent()) { contextInfo.setTokenHeader(mpJwtTokenHeader.get()); } else if (tokenHeader.isPresent()) { ConfigLogging.log.replacedConfig("smallrye.jwt.token.header", "mp.jwt.token.header"); contextInfo.setTokenHeader(tokenHeader.get()); } else { contextInfo.setTokenHeader(AUTHORIZATION_HEADER); } if (mpJwtTokenCookie.isPresent()) { SmallryeJwtUtils.setContextTokenCookie(contextInfo, mpJwtTokenCookie); } else if (tokenCookie.isPresent()) { ConfigLogging.log.replacedConfig("smallrye.jwt.token.cookie", "mp.jwt.token.cookie"); SmallryeJwtUtils.setContextTokenCookie(contextInfo, tokenCookie); } else { SmallryeJwtUtils.setContextTokenCookie(contextInfo, Optional.of(BEARER_SCHEME)); } if (expGracePeriodSecs > 0) { ConfigLogging.log.replacedConfig("smallrye.jwt.expiration.grace", "mp.jwt.verify.clock.skew"); contextInfo.setClockSkew(expGracePeriodSecs); } else if (mpJwtVerifyClockSkew > 0) { contextInfo.setClockSkew(mpJwtVerifyClockSkew); } contextInfo.setAlwaysCheckAuthorization(alwaysCheckAuthorization); contextInfo.setTokenKeyId(tokenKeyId.orElse(null)); contextInfo.setTokenDecryptionKeyId(tokenDecryptionKeyId.orElse(null)); contextInfo.setRequireNamedPrincipal(requireNamedPrincipal.orElse(null)); SmallryeJwtUtils.setTokenSchemes(contextInfo, tokenSchemes); contextInfo.setDefaultSubjectClaim(defaultSubClaim.orElse(null)); SmallryeJwtUtils.setContextSubPath(contextInfo, subPath); contextInfo.setDefaultGroupsClaim(defaultGroupsClaim.orElse(null)); contextInfo.setTlsCertificate(tlsCertificate.orElse(null)); contextInfo.setTlsCertificatePath(tlsCertificatePath.orElse(null)); contextInfo.setTlsTrustedHosts(tlsTrustedHosts.orElse(null)); contextInfo.setTlsTrustAll(tlsTrustAll); contextInfo.setHttpProxyHost(httpProxyHost.orElse(null)); contextInfo.setHttpProxyPort(httpProxyPort); SmallryeJwtUtils.setContextGroupsPath(contextInfo, groupsPath); contextInfo.setMaxTimeToLiveSecs(maxTimeToLiveSecs.orElse(null)); contextInfo.setTokenAge(mpJwtVerifyTokenAge.orElse(null)); contextInfo.setJwksRefreshInterval(jwksRefreshInterval); contextInfo.setForcedJwksRefreshInterval(forcedJwksRefreshInterval); Set resolvedAlgorithm = mpJwtPublicKeyAlgorithm; if (signatureAlgorithm.isPresent()) { if (signatureAlgorithm.get().getAlgorithm().startsWith("HS")) { if (!NONE.equals(resolvedVerifyKeyLocation) && resolvedVerifyKeyLocation == mpJwtLocation) { throw ConfigMessages.msg.hmacNotSupported(); } } else { ConfigLogging.log.replacedConfig("smallrye.jwt.verify.algorithm", "mp.jwt.verify.publickey.algorithm"); } resolvedAlgorithm = Set.of(signatureAlgorithm.get()); } checkKeyFormat(resolvedAlgorithm); contextInfo.setSignatureAlgorithm(resolvedAlgorithm); final Set theDecryptionKeyAlgorithm; if (!keyEncryptionAlgorithm.isEmpty()) { ConfigLogging.log.replacedConfig("smallrye.jwt.decrypt.algorithm", "mp.jwt.decrypt.key.algorithm"); theDecryptionKeyAlgorithm = Collections.singleton(keyEncryptionAlgorithm.get()); } else { theDecryptionKeyAlgorithm = mpJwtDecryptKeyAlgorithm; } contextInfo.setKeyEncryptionAlgorithm(theDecryptionKeyAlgorithm); contextInfo.setKeyFormat(keyFormat); contextInfo.setKeyProvider(keyProvider); contextInfo.setKeyCacheSize(keyCacheSize); contextInfo.setKeyCacheTimeToLive(keyCacheTimeToLive); if (mpJwtVerifyAudiences.isPresent()) { contextInfo.setExpectedAudience(mpJwtVerifyAudiences.get()); } else if (expectedAudience.isPresent()) { ConfigLogging.log.replacedConfig("smallrye.jwt.verify.aud", "mp.jwt.verify.audiences"); contextInfo.setExpectedAudience(expectedAudience.get()); } else { contextInfo.setExpectedAudience(null); } contextInfo.setGroupsSeparator(groupsSeparator); contextInfo.setRequiredClaims(requiredClaims.orElse(null)); contextInfo.setRelaxVerificationKeyValidation(relaxVerificationKeyValidation); contextInfo.setVerifyCertificateThumbprint(verifyCertificateThumbprint); return Optional.of(contextInfo); } private void checkKeyFormat(Set resolvedAlgorithm) { if (resolvedAlgorithm.size() > 1 && (keyFormat.equals(KeyFormat.PEM_KEY) || keyFormat.equals(KeyFormat.PEM_CERTIFICATE))) { ConfigMessages.msg.singleSignatureAlgorithmForPemOnly(); } } private PublicKey getVerificationKeyFromKeystore(String keyStorePath) throws Exception { KeyStore keyStore = KeyUtils.loadKeyStore(keyStorePath, keyStorePassword.get(), keyStoreType, keyStoreProvider); return keyStore.getCertificate(keyStoreVerifyKeyAlias.get()).getPublicKey(); } private PrivateKey getDecryptionKeyFromKeystore(String keyStorePath) throws Exception { KeyStore keyStore = KeyUtils.loadKeyStore(keyStorePath, keyStorePassword.get(), keyStoreType, keyStoreProvider); return (PrivateKey) keyStore.getKey(keyStoreDecryptKeyAlias.get(), keyStoreDecryptKeyPassword.orElse(keyStorePassword.get()).toCharArray()); } private boolean isPublicKeyInKeystore() { return keyStorePassword.isPresent() && keyStoreVerifyKeyAlias.isPresent(); } private boolean isPrivateKeyInKeystore() { return keyStorePassword.isPresent() && keyStoreDecryptKeyAlias.isPresent(); } @Produces @ApplicationScoped public JWTAuthContextInfo getContextInfo() { return getOptionalContextInfo().get(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy