001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.rest.server.mcp.v20260728; 018 019import java.util.Optional; 020 021import javax.crypto.SecretKey; 022 023/** 024 * SPI for supplying the key(s) used to seal/unseal MCP MRTR {@code requestState} continuation tokens (see 025 * {@code AeadRequestStateCodec}). 026 * 027 * <p> 028 * A dedicated abstraction beneath the AEAD codec so an operator can supply a stable, shared sealing key (fixing 029 * horizontally-scaled RESUME behind a load balancer) and rotate keys via a {@code keyId}, while the codec's 030 * zero-config default ({@link EphemeralKeyProvider}) keeps today's per-process ephemeral-key behavior unchanged. 031 * 032 * <p> 033 * <b>Thread-safety.</b> A single provider instance is shared across all requests against a binding (the codec 034 * is per-binding), so implementations must be safe for concurrent calls to both {@link #currentKey()} and 035 * {@link #resolveKey(String)}. 036 * 037 * <p> 038 * <b>Never-throw contract.</b> {@link #resolveKey(String)} is called on the unseal path with an untrusted, 039 * client-supplied {@code keyId}. Implementations must treat any unknown or retired {@code keyId} as 040 * {@link Optional#empty()} and must never throw — the caller ({@code AeadRequestStateCodec#unseal}) has no 041 * exception-handling path for this call. 042 * 043 * <p> 044 * Not annotated {@code @FunctionalInterface}: unlike single-method SPIs such as 045 * {@link org.apache.juneau.rest.server.auth.ApiKeyStore}, this SPI declares two abstract methods. 046 * 047 * <h5 class='section'>See Also:</h5><ul> 048 * <li class='jc'>{@link KeyedSecret} 049 * <li class='jc'>{@link EphemeralKeyProvider} 050 * <li class='jc'>{@link StaticKeyProvider} 051 * </ul> 052 * 053 * @since 10.0.0 054 */ 055public interface KeyProvider { 056 057 /** 058 * The key to seal new tokens with, together with its identifier. 059 * 060 * <p> 061 * May return a different value over time — a changing return value is the sanctioned mechanism for 062 * live runtime key rotation in a custom provider. Built-in providers ({@link EphemeralKeyProvider}, 063 * {@link StaticKeyProvider}) return a fixed value for the lifetime of the instance. 064 * 065 * @return The current sealing key. Never <jk>null</jk>. 066 */ 067 KeyedSecret currentKey(); 068 069 /** 070 * Resolves a {@code keyId} seen on an incoming token to the key that sealed it. 071 * 072 * @param keyId The key identifier read from the untrusted, client-supplied token, before decryption. Never 073 * <jk>null</jk>. 074 * @return The resolved key, or {@link Optional#empty()} if {@code keyId} is unknown or retired. Never 075 * throws. 076 */ 077 Optional<SecretKey> resolveKey(String keyId); 078}