Why do I get a 403 error when connecting to an AWS API Gateway websocket API using the AWS SDK?

219 views Asked by At

I’m currently developing a Spring application and have encountered an issue while trying to post a message to a Websocket connection.

I’ve successfully set up a Websocket API in AWS API Gateway and can post to a specific connection using my IAM credentials via Postman.

However, when I attempt to do the same using the Java AWS SDK within my Spring application, I receive a 403 error.

Here is the snippet of code in Spring:

AwsSessionCredentials awsCreds = AwsSessionCredentials.create(
        "***********",
        "*********",
        "**********"
);

AwsCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(awsCreds);

ApiGatewayManagementApiClient testApiClient = ApiGatewayManagementApiClient.builder()
        .region(Region.US_WEST_2)
        .credentialsProvider(credentialsProvider)
        .endpointOverride(new URI("https://api-id.execute-api.us-west-2.amazonaws.com/@connections/stage"))
        .build();

String connectionId = "********";

GetConnectionResponse getResponse = testApiClient.getConnection(GetConnectionRequest.builder()
        .connectionId(connectionId)
        .build());

PostToConnectionResponse postResponse = testApiClient.postToConnection(PostToConnectionRequest.builder()
        .connectionId(connectionId)
        .data(SdkBytes.fromUtf8String("test message from Spring"))
        .build());

The 1st request is attempting to get information about the connection.

The 2nd request is attempting to send a message to the connection. Both return a 403 error.

I know that my credentials have the correct permissions as I am able to directly send the same request via Postman with the same credentials and it works.

What is the issue?

1

There are 1 answers

1
Ermiya Eskandary On BEST ANSWER

TLDR: Use endpointOverride value of https://{api-id}.execute-api.us-east-1.amazonaws.com/{stage}


You don't need to specify @connections within the endpointOverride.

Per the Javadoc, the SDK endpoint override follows the format of

https://{api-id}.execute-api.{region}.amazonaws.com/{stage}

not:

https://{api-id}.execute-api.{region}.amazonaws.com/@connections/{stage}

When using the SDK, the getConnection & postToConnection methods (as well as the deleteConnection method) will automatically append @connections to the endpoint of your deployed API.

GetConnectionRequestMarshaller.class:

@SdkInternalApi
public class GetConnectionRequestMarshaller implements Marshaller<GetConnectionRequest> {
    // ...

    static {
        SDK_OPERATION_BINDING = OperationInfo
            .builder()
            .requestUri("/@connections/{connectionId}")
            .httpMethod(SdkHttpMethod.GET)
            .hasExplicitPayloadMember(false)
            .hasImplicitPayloadMembers(false)
            .hasPayloadMembers(false)
            .build();
    }
}

PostToConnectionRequestMarshaller.class:

@SdkInternalApi
public class PostToConnectionRequestMarshaller implements Marshaller<PostToConnectionRequest> {
    // ...

    static {
        SDK_OPERATION_BINDING = OperationInfo
            .builder()
            .requestUri("/@connections/{connectionId}")
            .httpMethod(SdkHttpMethod.POST)
            .hasExplicitPayloadMember(true)
            .hasImplicitPayloadMembers(false)
            .hasPayloadMembers(true)
            .build();
    }
}

This should work instead:

URI endpointOverride = new URI("https://api-id.execute-api.us-west-2.amazonaws.com/stage");

ApiGatewayManagementApiClient testApiClient =
    ApiGatewayManagementApiClient
        .builder()
        .region(Region.US_WEST_2)
        .credentialsProvider(credentialsProvider)
        .endpointOverride()
        .build();

Even if you were to manually call the API without the SDK, the documented format is:

https://{api-id}.execute-api.{region}.amazonaws.com/{stage}/@connections/{connection_id}

/@connections/{stage} won't work at all.