io.helidon.security.jwt.jwk.JwkKeys Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of helidon-security-jwt Show documentation
Show all versions of helidon-security-jwt Show documentation
Implementation of JWT and JWK to be used in other modules.
/*
* Copyright (c) 2018, 2024 Oracle and/or its affiliates.
*
* 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.helidon.security.jwt.jwk;
import java.io.IOException;
import java.io.InputStream;
import java.lang.System.Logger.Level;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import io.helidon.common.NativeImageHelper;
import io.helidon.common.configurable.Resource;
import jakarta.json.Json;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import jakarta.json.JsonReaderFactory;
/**
* A representation of the JSON web keys document - a map of key ids to corresponding web keys.
* The keys may be of different types and algorithms.
*
* Example (as used in unit test):
*
* JwkKeys keys = JwkKeys.builder()
* .resource("jwk_data.json")
* .build();
*
* Optional<Jwk> key = keys
* .forKeyId("cc34c0a0-bd5a-4a3c-a50d-a2a7db7643df");
*
*/
public final class JwkKeys {
private static final System.Logger LOGGER = System.getLogger(JwkKeys.class.getName());
private static final JsonReaderFactory JSON = Json.createReaderFactory(Collections.emptyMap());
private final Map keyMap = new HashMap<>();
private final List noKeyIdKeys = new LinkedList<>();
private static final boolean AUTOMATIC_CHARSET_DETECTION;
// Workaround for https://github.com/eclipse-ee4j/parsson/issues/121
static {
boolean utf32Available = false;
try {
Charset.forName("UTF-32LE");
Charset.forName("UTF-32BE");
utf32Available = true;
} catch (UnsupportedCharsetException e) {
if (NativeImageHelper.isNativeImage()) {
LOGGER.log(Level.TRACE, "Automatic JSON unicode detection not available."
+ " Add -H:+AddAllCharsets to build your native image with UTF-32 support.", e);
} else {
LOGGER.log(Level.TRACE, "Automatic JSON unicode detection not available.", e);
}
}
AUTOMATIC_CHARSET_DETECTION = utf32Available;
}
private JwkKeys(Builder builder) {
this.keyMap.putAll(builder.keyMap);
this.noKeyIdKeys.addAll(builder.noKeyIdKeys);
}
/**
* Create a new builder for this class.
*
* @return builder instance
*/
public static Builder builder() {
return new Builder();
}
/**
* Create Jwk keys from its JSON representation.
*
* @param json json with jwk keys
* @return keys set up from the provided json
*/
public static JwkKeys create(JsonObject json) {
return builder().json(json).build();
}
/**
* Get a JWK for defined key id if present.
*
* @param keyId keyId of the key to obtain from this keys
* @return Jwk if present
*/
public Optional forKeyId(String keyId) {
return Optional.ofNullable(keyMap.get(keyId));
}
/**
* List of keys in this instance.
*
* @return all keys configured
*/
public List keys() {
List result = new LinkedList<>();
result.addAll(noKeyIdKeys);
result.addAll(keyMap.values());
return result;
}
/**
* Builder of {@link JwkKeys}.
*/
public static final class Builder implements io.helidon.common.Builder {
private final List noKeyIdKeys = new LinkedList<>();
private final Map keyMap = new HashMap<>();
private Builder() {
}
/**
* Build a new keys instance.
*
* @return JwkKeys created from this builder
*/
@Override
public JwkKeys build() {
return new JwkKeys(this);
}
/**
* Add a new JWK to this keys.
*
* @param key the JWK instance
* @return updated builder instance
*/
public Builder addKey(Jwk key) {
Objects.requireNonNull(key, "Key must not be null");
if (null == key.keyId()) {
noKeyIdKeys.add(key);
} else {
keyMap.put(key.keyId(), key);
}
return this;
}
/**
* Load keys from a resource (must point to JSON text content).
*
* @param resource the resource with JSON data (file, classpath, URI etc.)
* @return updated builder instance
* @throws NullPointerException in case the path is null
*/
public Builder resource(Resource resource) {
Objects.requireNonNull(resource, "Json resource must not be null");
try (InputStream is = resource.stream()) {
JsonObject jsonObject = AUTOMATIC_CHARSET_DETECTION
? JSON.createReader(is).readObject()
: JSON.createReader(is, StandardCharsets.UTF_8).readObject();
addKeys(jsonObject);
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Failed to close input stream on resource: " + resource);
}
return this;
}
/**
* Load keys from JSON.
*
* @param json the JSON data
* @return updated builder instance
*/
public Builder json(JsonObject json) {
addKeys(json);
return this;
}
private void addKeys(JsonObject jsonObject) {
JsonArray keyArray = jsonObject.getJsonArray("keys");
keyArray.forEach(it -> {
JsonObject aKey = (JsonObject) it;
try {
addKey(Jwk.create(aKey));
} catch (Exception e) {
LOGGER.log(Level.WARNING,
"Could not process a key from JWK JSON, this key will not be available",
e);
}
});
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy