aws-amplify / amplify-swift

A declarative library for application development using cloud services.
Apache License 2.0
462 stars 198 forks source link

AWS CloudFront URL signing using iOS SDK #1085

Closed saravr closed 1 year ago

saravr commented 3 years ago

Is your feature request related to a problem? Please describe. Instead of asking the app server to sign the URL before fetching from Cloudfront, is it possible to sign the URL directly in iOS SDK?

Describe the solution you'd like iOS SDK should allow signing Cloudfront URLs

Describe alternatives you've considered

Additional context My app uses lots of data that includes images and videos. Its impractical to get it signed each time nor is it easy to include them in the responses from my app server. I am using Amplify 1.5.0

palpatim commented 3 years ago

Hi @saravr

If I understand you correctly, you're asking to be able to sign arbitrary Amplify API calls with a SigV4 signer, correct? You might be able to do this by configuring a REST API call to your endpoint using IAM authentication. For example:

amplifyconfiguration.json

{
  "UserAgent": "aws-amplify-cli/2.0",
  "Version": "1.0",
  "api": {
    "plugins": {
        "arbitraryurl": {
          "endpointType": "REST",
          "endpoint": "https://my-endpoint.com",
          "region": "us-west-2",
          "authorizationType": "AWS_IAM"
        }
      }
    }
  },
  "auth": {
    "plugins": {
      "awsCognitoAuthPlugin": {
        ...standard auth config...
      }
    }
  }
}

MyApp.swift

do {
    try Amplify.add(plugin: AWSAPIPlugin())
    try Amplify.add(plugin: AWSCognitoAuthPlugin())
    try Amplify.configure()
} catch {
    assert(false, "Error initializing Amplify: \(error)")
}

MyView.swift

let request = RESTRequest(
    apiName: "arbitraryurl",
    path: "/my-path",
    headers: ["X-sample-custom-header": "custom-header-value"],
    queryParameters: ["sample-query-param": "custom-query-param-value"]
)

Amplify
    .API
    .get(request: request)
    .resultPublisher
    .receive(on: DispatchQueue.main)
    .catch { Just("\($0)".data(using: .utf8)!) }
    .map { String(data: $0, encoding: .utf8)! }
    .assign(to: \.currentStatus, on: self)
    .store(in: &cancellableSet)

Headers sent to service

{
  "x-forwarded-proto": "https",
  "x-forwarded-port": "443",
  "host": "my-endpoint.com",
  "x-amzn-trace-id": "Root=**REDACTED**",
  "content-length": "0",
  "content-type": "application/json",
  "accept": "*/*",
  "authorization": "AWS4-HMAC-SHA256 Credential=**REDACTED**/execute-api/aws4_request, SignedHeaders=content-type;host;user-agent;x-amz-date;x-amz-security-token;x-sample-custom-header, Signature=**REDACTED**",
  "x-sample-custom-header": "custom-header-value",
  "x-amz-date": "20210303T214142Z",
  "x-amz-security-token": "**REDACTED**",
  "accept-encoding": "gzip, deflate, br",
  "accept-language": "en-us",
  "user-agent": "amplify-iOS/1.6.0 iOS/14.4 en_US"
}

The main concern is the scope of the Credential, which you can see above is hardcoded to the APIGateway service name of /execute-api. Depending on which AWS service you are trying to call behind your CloudFront installation, this may not be appropriate.

We've discussed exposing a more user-friendly signature API specifically to handle arbitrary calls to AWS services, so we'll leave this open as a feature request, even if the above code snippets end up working out for you.

saravr commented 3 years ago

@palpatim Thanks for your response. And this will still make a call over network I suppose. I thought, at least server side solution, involves local signing using the certificate. Is such a thing possible at all in mobile client side? I want to keep network activity to minimum.

palpatim commented 3 years ago

To the best of my knowledge, SigV4 signing requires current credentials, so if the credentials are out-of-date, then yes this would make a network call to get refreshed credentials before signing. If the credentials are current, then it would not make a call for the signing operation.

Can you expand on your use case about "local signing using the certificate"? If you want mutual authentication with a client-side certificate, you'll have to use a completely custom interceptor chain for your API, which would definitely be an advanced use case. I don't think we have any docs on that just now, but rather than spout a bunch of code snippets, can you be very clear about the use case you're trying to solve? A step-by-step explanation of the calls you're trying to make and the behavior you expect would be very helpful.

github-actions[bot] commented 3 years ago

This issue is stale because it has been open for 14 days with no activity. Please, provide an update or it will be automatically closed in 7 days.

royjit commented 1 year ago

There are different ways to add authorization to your API request as mentioned here - https://docs.amplify.aws/lib/graphqlapi/authz/q/platform/ios/#aws-lambda-1 . Please reach us if this does not solves your use case. Closing this ticket due to no communication.