adam bien's blog

Invoking AWS_IAM Auth Lambda Function URL with Java 11+ HttpClient 📎

If your Lambda Function URL uses the AWS_IAM auth type:


import software.amazon.awscdk.services.lambda.FunctionUrlAuthType;
import software.amazon.awscdk.services.lambda.FunctionUrlOptions;
    
var functionUrl = function.addFunctionUrl(FunctionUrlOptions.builder()
.authType(FunctionUrlAuthType.AWS_IAM)
    .build());

you must sign each HTTP request using AWS Signature Version 4 (SigV4).

The class Aws4Signer ships with the dependency:


<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>auth</artifactId>
    <version>2.17.191</version>
</dependency>

...as well as the SDK:

<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>aws-sdk-java</artifactId>
    <version>2.17.191</version>
</dependency>

To sign a request:


import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse.BodyHandlers;
import java.util.List;

import org.junit.jupiter.api.Test;

import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.auth.signer.Aws4Signer;
import software.amazon.awssdk.auth.signer.params.Aws4SignerParams;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.regions.Region;

public class JavaHTTP11ClientTest {

    @Test
    public void signedHttpGET() throws IOException, InterruptedException {
        var uri = URI.create("https://mtpe2t7ucalsample62y0zkfwq.lambda-url.eu-central-1.on.aws/hello/");
        var host = uri.getHost();
        var path = uri.getPath();

...you have to create a SdkHttpFullRequest first:

        var fullRequest = SdkHttpFullRequest.builder()
                .encodedPath(path)
                .host(host)
                .protocol("https")
                .method(SdkHttpMethod.GET)
                .build();

The request is passed to AWS Signature Version 4 (SigV4) utility, which performs the signing:


        
        var signedRequest = Aws4Signer.create().sign(fullRequest,
            Aws4SignerParams.builder()
                    .signingName("lambda")
                    .awsCredentials(DefaultCredentialsProvider.create()
                                    .resolveCredentials())
                    .signingRegion(Region.EU_CENTRAL_1)
                    .build());

The signature is is passed as Authorization and X-Amz-Date headers to the Java 11 HttpClient:


        //Java 11+ HttpClient          
        var client = HttpClient.newHttpClient();
        var builder = HttpRequest.newBuilder(uri);

        signedRequest.headers().forEach((name, list) -> addHeader(builder, name, list));
        var request = builder.GET().build();
        var response = client.send(request, BodyHandlers.ofString());
        var status = response.statusCode();
        var body = response.body();
        //...
    }

    static void addHeader(HttpRequest.Builder requestBuilder,String name, List<String> values){
        if(name.equalsIgnoreCase("host")){
            return;
        }
        values.forEach(value -> requestBuilder.header(name, value));
    }    
}

The function URL Lambda from this example is based on the MicroProfile with Quarkus as AWS Lambda Function deployed with Cloud Development Kit (CDK) v2 for Java template.