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}