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.client.mcp;
018
019
020import java.util.function.*;
021
022import org.apache.juneau.rest.client.*;
023import static org.apache.juneau.commons.utils.Shorts.*;
024
025/**
026 * A builder-configurable auth-token seam implemented as a {@link RestCallInterceptor}.
027 *
028 * <p>
029 * On {@link #onInit(RestRequest)}, sets the {@code Authorization} request header to {@code "Bearer " + token},
030 * where {@code token} comes from a caller-supplied {@link Supplier}.
031 * The OAuth/OIDC flows in juneau-rest-client-mcp-auth plug into this seam by supplying their own token-supplier
032 * implementation (e.g. one that refreshes an expired access token); this class implements no OAuth/OIDC flow itself.
033 *
034 * <p>
035 * If the supplied token is <jk>null</jk> <b>or blank</b> (empty or all-whitespace), no {@code Authorization}
036 * header is set for that request (the call proceeds unauthenticated) - a blank token is never sent as a
037 * credential-less {@code Authorization: Bearer } header. If the token supplier itself throws,
038 * {@link RestRequest#run()}'s documented interceptor-exception handling aborts the call before it is sent.
039 *
040 * <p>
041 * <b>Note:</b> when a token is set, enabling {@code FINE}/{@code FINEST}-level logging on the client's debug
042 * logger (see {@link org.apache.juneau.rest.client.RestClient.Builder#debugLoggerName(String) RestClient.Builder#debugLoggerName(String)}) logs the full
043 * outgoing request including all headers, so enabling debug logging on a client using this interceptor will log
044 * the {@code Authorization} header (and therefore the bearer token) in plain text.
045 *
046 * @since 10.0.0
047 */
048public class McpAuthInterceptor implements RestCallInterceptor {
049
050   private final Supplier<String> tokenSupplier;
051
052   /**
053    * Constructor.
054    *
055    * @param tokenSupplier Supplies the bearer token for each request. Must not be <jk>null</jk>.
056    */
057   public McpAuthInterceptor(Supplier<String> tokenSupplier) {
058      this.tokenSupplier = reqnn("tokenSupplier", tokenSupplier);
059   }
060
061   /**
062    * Creates an interceptor that always sends the same static bearer token.
063    *
064    * @param token The static bearer token. Must not be <jk>null</jk>.
065    * @return A new interceptor. Never <jk>null</jk>.
066    */
067   public static McpAuthInterceptor ofStaticBearer(String token) {
068      reqnn("token", token);
069      return new McpAuthInterceptor(() -> token);
070   }
071
072   @Override /* Overridden from RestCallInterceptor */
073   public void onInit(RestRequest req) throws Exception {
074      var token = tokenSupplier.get();
075      if (inb(token))
076         req.header("Authorization", "Bearer " + token);
077   }
078}