aws-amplify / docs

AWS Amplify Framework Documentation
https://docs.amplify.aws
Apache License 2.0
482 stars 1.03k forks source link

API(Rest), document how to use ID token instead of access token #5661

Open PritamDutt opened 1 year ago

PritamDutt commented 1 year ago

Description

Currently API(Rest) is hard coded to use accessToken, which requires scope definition for using Cognito as COGNITO_USER_POOLS authorizer with API Gateway, and is not really a use case for us. We want to be able to pass idToken instead of accessToken, but there is no provision for the same.

It will be really great, if we could somehow choose it as part of config process, or may be while making the API calls?

COGNITO_USER_POOLS usage excerpt from Amazon API Gateway Developer Guide

To use an access token, do the following:

Choose the pencil icon next to OAuth Scopes.

Type one or more full names of a scope that has been configured when the Amazon Cognito user pool was created. For example, following the example given in Create an Amazon Cognito user pool for a REST API, one of the scopes is https://my-petstore-api.example.com/cats.read. Use a single space to separate multiple scopes.

At runtime, the method call succeeds if any scope that's specified on the method in this step matches a scope that's claimed in the incoming token. Otherwise, the call fails with a 401 Unauthorized response.

To save the setting, choose the check mark icon.

Issue @ https://github.com/aws-amplify/amplify-flutter/blob/19834beca7942700154c342505eda80a48355787/packages/auth/amplify_auth_cognito_dart/lib/src/util/cognito_user_pools_auth_provider.dart#L19

Categories

Steps to Reproduce

No response

Screenshots

No response

Platforms

Flutter Version

3.10.2

Amplify Flutter Version

1.2.0

Deployment Method

Amplify CLI

Schema

No response

ragingsquirrel3 commented 1 year ago

Hello! I think the easiest workaround here would be to specify the "Authorization" header when you call the REST API methods and pass it the ID token directly from the auth category. Let me know if this is helpful:

final authSession = await Amplify.Auth.fetchAuthSession();
final myIdToken = authSession.userPoolTokensResult.value.idToken.raw;
final res = await Amplify.API.get(path, headers: {'Authorization': myIdToken}).response;

Like you said, plugin automatically uses access token and difficult to override at config time, but if any "Authorization" header is already set on the request from the caller the plugin will not overwrite it. Another option would be to customize HTTP client behavior, which could be done at config time but it's a little more code to implement and pass to the baseHttpClient config option.

PritamDutt commented 1 year ago

@ragingsquirrel3 I think that did strike me but I wanted to avoid repeating it on every call... if you don't mind can you help me with a method where I could implement this functionality for once in entire App may be like implementing an interceptor or something better.

mjaydeep01 commented 1 year ago

@PritamDutt, you can read the instruction at https://docs.amplify.aws/lib/graphqlapi/advanced-workflows/q/platform/flutter/#adding-headers-to-outgoing-requests. Last example at the bottom of the Page.

@ragingsquirrel3, I am using graphql with datastore. It works when I use API operation directly (debug logs printed), but it seems not working for Datastore (no debug log getting printed). Any idea how to fix this?

ragingsquirrel3 commented 1 year ago

@PritamDutt the HTTP client implementation in your case would look like:

class IdTokenHttpClient extends AWSBaseHttpClient {
  @override
  Future<AWSBaseHttpRequest> transformRequest(
    AWSBaseHttpRequest request,
  ) async {
    final authSession =
        await Amplify.Auth.fetchAuthSession() as CognitoAuthSession;
    request.headers.addAll(
      {
        AWSHeaders.authorization:
            authSession.userPoolTokensResult.value.idToken.raw
      },
    );
    return request;
  }
}

and then you would pass to plugin like:

AmplifyAPI(baseHttpClient: IdTokenHttpClient())

Note this behavior will then apply for all HTTP methods in API category (including graphql) so if you need this to only happen for some endpoints/situations then you could write some checks in your implementation.

@mjaydeep01 the datastore will not use the HTTP implementation provided as it will use the android/swift HTTP behavior in the respective API category implementation and there is no way to customize that from Amplify Flutter.

PritamDutt commented 1 year ago

Thanks @ragingsquirrel3 .. May I suggest to have this included in the docs as well.. Thanks a lot

ragingsquirrel3 commented 1 year ago

Transferred this to docs repo, will update title to more accurately reflect what's needed. Though this came from Flutter, I suspect it applies to Android/iOS as well.

6eyu commented 7 months ago

I am facing the same issue, and solved it by using oidcAuthProvider and updating "authorizationType": "OPENID_CONNECT" in amplifyconfiguration.json,

val authProviders = ApiAuthProviders.builder()
    .oidcAuthProvider {
        val future = CompletableFuture<String>()
        Amplify.Auth.fetchAuthSession(
            { future.complete((it as AWSCognitoAuthSession).userPoolTokens.value?.idToken) },
            { future.completeExceptionally(it) }
        )
        future.get()
    }
    .build()
val plugin = AWSApiPlugin.builder()
    .apiAuthProviders(authProviders)
    .build()
Amplify.addPlugin(plugin)

reference see amplify document