CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-eclipse-jetty--jetty-client

Eclipse Jetty HTTP Client - A lightweight, asynchronous HTTP client library that supports HTTP/1.1, HTTP/2, WebSocket, and various authentication mechanisms, proxy configurations, and connection pooling strategies.

Pending
Overview
Eval results
Files

authentication.mddocs/

Authentication

The authentication capability provides comprehensive support for HTTP authentication mechanisms including Basic, Digest, and SPNEGO/Kerberos authentication with credential storage, automatic challenge handling, and per-request authentication configuration.

AuthenticationStore Interface

The central storage system for authentication credentials and results.

public interface AuthenticationStore {
    // Authentication management
    void addAuthentication(Authentication authentication);
    void removeAuthentication(Authentication authentication);
    void clearAuthentications();
    Collection<Authentication> getAuthentications();
    
    // Authentication result management
    void addAuthenticationResult(Authentication.Result result);
    void removeAuthenticationResult(Authentication.Result result);
    void clearAuthenticationResults();
    Authentication.Result findAuthenticationResult(URI uri);
}

Authentication Interface

The base interface for all authentication mechanisms.

public interface Authentication {
    // Authentication matching
    boolean matches(String type, URI uri, String realm);
    
    // Authentication execution
    Authentication.Result authenticate(Request request, ContentResponse response, 
                                     Authentication.HeaderInfo headerInfo, Attributes context);
    
    // Header information class
    static class HeaderInfo {
        String getType();
        String getRealm();
        String getBase64();
        Map<String, String> getParameters();
        String getParameter(String paramName);
    }
}

Authentication.Result Interface

Represents the result of an authentication attempt.

public interface Authentication.Result {
    URI getURI();
    
    void apply(Request request);
}

Basic Authentication

HTTP Basic authentication using username and password credentials.

BasicAuthentication Class

public class BasicAuthentication implements Authentication {
    public BasicAuthentication(URI uri, String realm, String user, String password);
    public BasicAuthentication(String user, String password);
    
    public String getUser();
    public String getPassword();
}

Usage Examples

// Basic authentication for specific URI and realm
URI apiUri = URI.create("https://api.example.com");
BasicAuthentication auth = new BasicAuthentication(apiUri, "API Realm", "username", "password");

// Add to authentication store
client.getAuthenticationStore().addAuthentication(auth);

// Make authenticated request
ContentResponse response = client.GET("https://api.example.com/protected");

// Global basic authentication (matches any realm)
BasicAuthentication globalAuth = new BasicAuthentication("admin", "secret123");
client.getAuthenticationStore().addAuthentication(globalAuth);

Preemptive Basic Authentication

// Add authentication result directly to avoid initial challenge
URI uri = URI.create("https://api.example.com");
String credentials = Base64.getEncoder().encodeToString("user:pass".getBytes());
AuthenticationResult result = AuthenticationResult.from(uri, null, "Authorization", "Basic " + credentials);

client.getAuthenticationStore().addAuthenticationResult(result);

// First request will include Authorization header immediately
ContentResponse response = client.GET("https://api.example.com/data");

Digest Authentication

HTTP Digest authentication providing improved security over Basic authentication.

DigestAuthentication Class

public class DigestAuthentication implements Authentication {
    public DigestAuthentication(URI uri, String realm, String user, String password);
    
    public String getUser();
    public String getPassword();
}

Usage Examples

// Digest authentication setup
URI secureUri = URI.create("https://secure.example.com");
DigestAuthentication digestAuth = new DigestAuthentication(
    secureUri, 
    "Secure Area", 
    "username", 
    "password"
);

client.getAuthenticationStore().addAuthentication(digestAuth);

// Make request - digest challenge will be handled automatically
ContentResponse response = client.GET("https://secure.example.com/data");

Digest Authentication Flow

// The client automatically handles the digest authentication flow:
// 1. Initial request without authentication
// 2. Server responds with 401 and WWW-Authenticate header
// 3. Client calculates digest response using provided nonce
// 4. Client retries request with Authorization header
// 5. Server validates digest and responds with requested content

DigestAuthentication auth = new DigestAuthentication(
    URI.create("https://api.example.com"), 
    "Protected", 
    "user", 
    "pass"
);

client.getAuthenticationStore().addAuthentication(auth);

// This request will automatically handle the digest challenge
ContentResponse response = client.GET("https://api.example.com/protected-resource");

SPNEGO Authentication

SPNEGO (Simple and Protected GSSAPI Negotiation Mechanism) authentication for Kerberos/Windows authentication.

SPNEGOAuthentication Class

public class SPNEGOAuthentication implements Authentication {
    public SPNEGOAuthentication(String serviceName);
    
    public String getServiceName();
}

Usage Examples

// SPNEGO authentication for Kerberos
SPNEGOAuthentication spnegoAuth = new SPNEGOAuthentication("HTTP/server.example.com");

client.getAuthenticationStore().addAuthentication(spnegoAuth);

// SPNEGO authentication requires proper Kerberos configuration
// System properties or JAAS configuration may be needed
System.setProperty("java.security.auth.login.config", "/path/to/jaas.conf");
System.setProperty("java.security.krb5.conf", "/path/to/krb5.conf");

ContentResponse response = client.GET("https://server.example.com/protected");

SPNEGO Configuration

// Example JAAS configuration (jaas.conf)
/*
Client {
    com.sun.security.auth.module.Krb5LoginModule required
    useTicketCache=true
    renewTGT=true
    doNotPrompt=true;
};
*/

// Example Kerberos configuration (krb5.conf)
/*
[libdefaults]
    default_realm = EXAMPLE.COM
    
[realms]
    EXAMPLE.COM = {
        kdc = kdc.example.com
        admin_server = admin.example.com
    }
*/

public class KerberosClient {
    public void configureSpnego() {
        // Set system properties for Kerberos
        System.setProperty("java.security.auth.login.config", "jaas.conf");
        System.setProperty("java.security.krb5.conf", "krb5.conf");
        System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
        
        // Create SPNEGO authentication
        SPNEGOAuthentication auth = new SPNEGOAuthentication("HTTP/api.example.com");
        client.getAuthenticationStore().addAuthentication(auth);
    }
}

Authentication Store Management

Adding Multiple Authentication Mechanisms

AuthenticationStore authStore = client.getAuthenticationStore();

// Add multiple authentication mechanisms
authStore.addAuthentication(new BasicAuthentication("user1", "pass1"));
authStore.addAuthentication(new DigestAuthentication(
    URI.create("https://secure.example.com"), 
    "Secure", 
    "user2", 
    "pass2"
));
authStore.addAuthentication(new SPNEGOAuthentication("HTTP/kerberos.example.com"));

// The client will automatically select the appropriate authentication
// based on server challenges

Authentication Result Management

AuthenticationStore authStore = client.getAuthenticationStore();

// Find existing authentication result
URI uri = URI.create("https://api.example.com");
AuthenticationResult existingResult = authStore.findAuthenticationResult(uri);

if (existingResult != null) {
    System.out.println("Found cached authentication for: " + uri);
} else {
    // Add preemptive authentication
    AuthenticationResult result = AuthenticationResult.from(
        uri, 
        "API", 
        "Authorization", 
        "Bearer " + accessToken
    );
    authStore.addAuthenticationResult(result);
}

// Clear authentication results when tokens expire
authStore.clearAuthenticationResults();

Custom Authentication

Implement custom authentication mechanisms by extending the Authentication interface.

Custom Token Authentication

public class BearerTokenAuthentication implements Authentication {
    private final URI uri;
    private final String token;
    
    public BearerTokenAuthentication(URI uri, String token) {
        this.uri = uri;
        this.token = token;
    }
    
    @Override
    public String getType() {
        return "Bearer";
    }
    
    @Override
    public URI getURI() {
        return uri;
    }
    
    @Override
    public String getRealm() {
        return null; // No realm for bearer tokens
    }
    
    @Override
    public boolean matches(String type, URI uri, String realm) {
        return "Bearer".equalsIgnoreCase(type) && 
               (this.uri == null || this.uri.equals(uri));
    }
    
    @Override
    public AuthenticationResult authenticate(Request request, ContentResponse response, 
                                           HeaderInfo headerInfo, Context context) {
        return AuthenticationResult.from(
            context.getURI(), 
            context.getRealm(), 
            "Authorization", 
            "Bearer " + token
        );
    }
}

// Usage
BearerTokenAuthentication tokenAuth = new BearerTokenAuthentication(
    URI.create("https://api.example.com"), 
    "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
);

client.getAuthenticationStore().addAuthentication(tokenAuth);

API Key Authentication

public class ApiKeyAuthentication implements Authentication {
    private final URI uri;
    private final String apiKey;
    private final String headerName;
    
    public ApiKeyAuthentication(URI uri, String headerName, String apiKey) {
        this.uri = uri;
        this.headerName = headerName;
        this.apiKey = apiKey;
    }
    
    @Override
    public String getType() {
        return "ApiKey";
    }
    
    @Override
    public URI getURI() {
        return uri;
    }
    
    @Override
    public String getRealm() {
        return null;
    }
    
    @Override
    public boolean matches(String type, URI uri, String realm) {
        return this.uri.getHost().equals(uri.getHost());
    }
    
    @Override
    public AuthenticationResult authenticate(Request request, ContentResponse response, 
                                           HeaderInfo headerInfo, Context context) {
        return AuthenticationResult.from(
            context.getURI(), 
            context.getRealm(), 
            headerName, 
            apiKey
        );
    }
}

// Usage
ApiKeyAuthentication apiKeyAuth = new ApiKeyAuthentication(
    URI.create("https://api.example.com"), 
    "X-API-Key", 
    "your-api-key-here"
);

client.getAuthenticationStore().addAuthentication(apiKeyAuth);

Per-Request Authentication

Override authentication for specific requests without modifying the global authentication store.

Request-Specific Headers

// Override authentication for a single request
ContentResponse response = client.newRequest("https://api.example.com/data")
    .header("Authorization", "Bearer " + specificToken)
    .send();

// Use different API key for specific request
ContentResponse response2 = client.newRequest("https://different-api.com/data")
    .header("X-API-Key", "different-api-key")
    .send();

Temporary Authentication

public class TemporaryAuth {
    private final HttpClient client;
    private final AuthenticationStore originalStore;
    
    public TemporaryAuth(HttpClient client) {
        this.client = client;
        this.originalStore = client.getAuthenticationStore();
    }
    
    public ContentResponse requestWithAuth(String url, Authentication auth) throws Exception {
        // Create temporary authentication store
        AuthenticationStore tempStore = new HttpAuthenticationStore();
        tempStore.addAuthentication(auth);
        
        // Temporarily replace authentication store
        client.setAuthenticationStore(tempStore);
        
        try {
            return client.GET(url);
        } finally {
            // Restore original authentication store
            client.setAuthenticationStore(originalStore);
        }
    }
}

Authentication Error Handling

Handling Authentication Failures

client.newRequest("https://api.example.com/protected")
    .send(result -> {
        if (result.isSucceeded()) {
            Response response = result.getResponse();
            if (response.getStatus() == 401) {
                System.err.println("Authentication failed");
                
                // Check WWW-Authenticate header for challenge details
                String wwwAuth = response.getHeaders().get("WWW-Authenticate");
                System.err.println("Challenge: " + wwwAuth);
                
                // Handle specific authentication errors
                if (wwwAuth != null && wwwAuth.contains("expired_token")) {
                    refreshToken();
                }
            }
        } else {
            Throwable failure = result.getFailure();
            if (failure instanceof HttpResponseException) {
                HttpResponseException httpEx = (HttpResponseException) failure;
                if (httpEx.getResponse().getStatus() == 401) {
                    System.err.println("Authentication challenge failed");
                }
            }
        }
    });

Authentication Retry Logic

public class AuthenticatedClient {
    private final HttpClient client;
    private String accessToken;
    
    public ContentResponse authenticatedRequest(String url) throws Exception {
        // First attempt with current token
        ContentResponse response = client.newRequest(url)
            .header("Authorization", "Bearer " + accessToken)
            .send();
        
        if (response.getStatus() == 401) {
            // Token expired, refresh and retry
            refreshAccessToken();
            
            response = client.newRequest(url)
                .header("Authorization", "Bearer " + accessToken)
                .send();
                
            if (response.getStatus() == 401) {
                throw new SecurityException("Authentication failed after token refresh");
            }
        }
        
        return response;
    }
    
    private void refreshAccessToken() {
        // Implement token refresh logic
        // This could involve calling a refresh token endpoint
        // or re-authenticating with username/password
    }
}

Authentication Best Practices

Secure Credential Storage

// Avoid hardcoding credentials
public class SecureAuthConfig {
    public static BasicAuthentication createFromEnvironment() {
        String username = System.getenv("API_USERNAME");
        String password = System.getenv("API_PASSWORD");
        
        if (username == null || password == null) {
            throw new IllegalStateException("Authentication credentials not configured");
        }
        
        return new BasicAuthentication(username, password);
    }
    
    public static BearerTokenAuthentication createFromTokenFile(Path tokenFile) throws IOException {
        String token = Files.readString(tokenFile).trim();
        return new BearerTokenAuthentication(null, token);
    }
}

Authentication Logging

public class LoggingAuthenticationStore implements AuthenticationStore {
    private final AuthenticationStore delegate;
    private final Logger logger;
    
    @Override
    public void addAuthentication(Authentication authentication) {
        logger.info("Adding authentication for: {} - {}", 
                   authentication.getType(), 
                   authentication.getURI());
        delegate.addAuthentication(authentication);
    }
    
    @Override
    public AuthenticationResult findAuthenticationResult(URI uri) {
        AuthenticationResult result = delegate.findAuthenticationResult(uri);
        if (result != null) {
            logger.debug("Found cached authentication result for: {}", uri);
        } else {
            logger.debug("No cached authentication result for: {}", uri);
        }
        return result;
    }
    
    // Implement other methods...
}

// Usage
LoggingAuthenticationStore loggingStore = new LoggingAuthenticationStore(
    client.getAuthenticationStore(),
    LoggerFactory.getLogger("auth")
);

Install with Tessl CLI

npx tessl i tessl/maven-org-eclipse-jetty--jetty-client

docs

authentication.md

connection-pooling.md

content-management.md

http-operations.md

index.md

proxy-configuration.md

request-configuration.md

response-processing.md

tile.json