Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Public API for URL signing? #999

Open
trevjonez opened this issue Jul 27, 2023 · 7 comments
Open

Public API for URL signing? #999

trevjonez opened this issue Jul 27, 2023 · 7 comments
Labels
feature-request A feature should be added or improved.

Comments

@trevjonez
Copy link

Describe the issue

Currently trying to build an ApiGateway websocket driven app that is using IAM for auth and need a way to sign a wss URL in order to connect.

Steps to Reproduce

This SO answer gives a good description of what I am wanting to achieve.

I have got an answer from AWS support. I will need to sign the wss URL. So instead of
setting request headers in a HTTP request, the signature information will be passed to the
url in the query string parameters. A signed wss URL looks like: wss://API_ID.execute-
api.region.amazonaws.com/dev?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-
Credential=ACCESSKEY/20200131/region/execute-api/aws4_request&X-Amz-Date=20200131T100233Z&X-
Amz-Security-Token=SECURITY_TOKEN&X-Amz-SignedHeaders=host&X-Amz-Signature=SIGNATURE.

To generate the signed URL, I can use Signer.signUrl method from @aws-amplify/core library.

Current behavior

All of the signing details are buried in the Smithy SDK as an implementation detail of the things that provide pre-signing options. (IE: S3)

AWS Kotlin SDK version used

0.29.0-beta

Platform (JVM/JS/Native)

JVM

Operating System and version

N/A

@trevjonez trevjonez added guidance Question that needs advice or information. needs-triage This issue or PR still needs to be triaged. labels Jul 27, 2023
@lauzadis
Copy link
Member

Thanks for the question. Can you share more information about the specific SDK operation you're trying to create a presigned URL for? You're correct that we don't expose pre-signing capabilities and instead generate them for a few specific services. We may be able to add it for API Gateway depending on the use case.

@lauzadis lauzadis removed the needs-triage This issue or PR still needs to be triaged. label Jul 28, 2023
@trevjonez
Copy link
Author

My usecase is I believe very similar to the SO answer.

I want to sign a wss://API_ID.execute-api.REGION.amazonaws.com/STAGE?potentiallyWithQueryArgs=true URL so that I can use AWS_IAM as the auth type.

In my case it will be from the JVM most likely using OKHttp as the client implementation.

Current best looking workaround I think will be using the com.amazonaws.auth.AWS4Signer from the android aws core sdk. But I do think having some sort of public API similar to what the android sdk provides would be appropriate for the kotlin SDK.

@ianbotsf ianbotsf added feature-request A feature should be added or improved. and removed guidance Question that needs advice or information. labels Mar 13, 2024
@lauzadis
Copy link
Member

Hi and sorry for the delay in our response.

This is already somewhat possible with the Kotlin SDK, here is an example. You will need to @OptIn(InternalApi::class) to call signer.sign(...). We will discuss as a team whether that API should be made public.

val signer = DefaultAwsSigner

val parsedUrl = Url.parse("wss://$API_ID.execute-api.$REGION.amazonaws.com/$STAGE")
val req = HttpRequest(method = HttpMethod.GET, url = parsedUrl)

val credentialsProvider = // your AWS credentials provider
val signingConfig = AwsSigningConfig {
    algorithm = AwsSigningAlgorithm.SIGV4
    signatureType = AwsSignatureType.HTTP_REQUEST_VIA_QUERY_PARAMS
    credentials = credentialsProvider.resolve()
    region = "us-west-2"
    service = "execute-api"
}

val signedUrl = signer.sign(req, signingConfig).output.url
// use signedUrl as needed...

What sort of things would you like to see changed to make it easier to sign URLs?

@cloudshiftchris
Copy link

There are a few AWS services, such as Lambda function URLs, that aren't an AWS SDK call - they're an HTTPS endpoint that requires Sigv4 for AWS_IAM auth.

Struggling to see how to use the Kotlin AWS SDK for this - it isn't a general-purpose "HTTP Client". Its often preferable to use other Http clients to make those REST/HTTPS/whatever requests (there are many other non-SDK considerations - marshalling request/response payloads, etc) - but the sensitive logic on signing is baked into the SDK code, assumes that the request will be made by the SDK HTTP client. One could, of course, re-implement the signing logic, though that seems fragile and not an effective use of time when it already exists(ish).

Perhaps the signing logic could be decoupled from the HttpRequest such that is can be used elsewhere?

@lauzadis
Copy link
Member

I think HttpRequest is the correct abstraction for signing. It is formed by an HTTP method, URL endpoint, and optional request headers / body. You can create an HttpRequest from a Lambda function URL and then sign it by doing something similar to my example code above.

After signing, if you don't want to use our SDK's HTTP client to complete the request, you can convert the request to your desired HTTP client's request type.

Do you have a different idea of what a decoupled signer would look like? What should it take as input?

@cloudshiftchris
Copy link

A few thoughts now that we managed to make this work:

  1. It isn't clear that signing, specifically DefaultAwsSigner, is intended to be part of the SDK public API; its from an ancillary aws.smithy.kotlin:aws-signing-default dependency, which makes if fuzzy as to whether its stable for consumption (or exposed for consumption by the SDK). It comes across as "hey, we found this random transitive dependency and made it work" rather than a supported part of the API.
  2. DefaultAwsSigner changes logging behaviour when used from SDK vs standalone; when used standalone there is no telemetryProvider registered, hence no logging provider, hence no debug/trace log output emitted;
  3. There doesn't appear to be a way to have an unsigned payload - the code expects a pre-calculated hash, or hashes the payload;
  4. The shouldSignHeader predicate in AwsSigningConfig is poorly documented: The default predicate is to not reject signing any headers (i. e., _ -> true). - double negative, "reject" is odd. Perhaps something like "The default predicate signs all headers".

A documentation page on patterns for manually signing requests, e.g. lambda function URLs etc, would go a long way here.

@lauzadis
Copy link
Member

@cloudshiftchris Thanks for the detailed feedback. We've added some backlog tasks to clean up the documentation and improve functionality when using the signer standalone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-request A feature should be added or improved.
Projects
None yet
Development

No branches or pull requests

4 participants