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

AWS AppSync support #209

Closed
BerndWessels opened this issue Apr 2, 2019 · 12 comments
Closed

AWS AppSync support #209

BerndWessels opened this issue Apr 2, 2019 · 12 comments
Labels
wontfix This will not be worked on

Comments

@BerndWessels
Copy link

AppSync is a very popular, complete serverless GraphQL service by AWS.

It would be great if we could support this with a dedicated Link.

There is already a library that helps with the AWS authorization which could be used to get this to work.

AppSync also supports serverless GraphQL subscriptions, but I have no idea how the AWS authorization for that works. Would need help on that.

@micimize micimize added the wontfix This will not be worked on label Apr 18, 2019
@micimize
Copy link
Collaborator

This would be very cool, but I think it belongs in a stand-alone library. All you really need is an
AwsAuthLink using the logic outlined in the post, modeled on an auth link. This should work for subscriptions as well, as according to the AppSync docs:

To control authorization at connection time to a subscription, you can leverage controls such as IAM, Amazon Cognito identity pools, or Amazon Cognito user pools for field-level authorization

My stack is mostly on AWS, so if I get to the point where I'm doing user management myself I might get around to this, or contributing to a standalone / plugin if someone else starts work on it first 😉

@bobwiller
Copy link

@micimize @mainawycliffe I think I have the auth part working for this, however I believe there are other problems w/the way AppSync handles subscriptions. I spent most of the weekend walking through things, and would love to help out here. However I have what might be a silly question: What is the best way to trace what is actually going across the wire?

@micimize
Copy link
Collaborator

@bobwiller do you have a repo? A logging link would help log the request/response info.

I guess I should have read a bit more in the docs

Subscription connection management is handled automatically by the AWS AppSync client SDK using MQTT over WebSockets as the network protocol between the client and service.

So this might take an mqtt_client link also.

@bobwiller
Copy link

I don't have a repo at the moment. Still trying to prove I can connect at all. I have been playing w/the mqtt_client package as well just to see if i can create a connection - as soon as I have something i will post it. Right now I can't get past a connection error that my connection "was not upgraded to websocket" which I am assuming is because the Connection and Upgrade headers need to be set....

@bobwiller
Copy link

bobwiller commented May 3, 2019

@micimize I finally have this working in Dart/Flutter (but outside of this package). I don't know a lot about other GraphQL implementations, but for AWS Appsync, this is a two step process: An http query, and then the socket is built based on the response from that call. (The socket gets all the data it needs to connect from the response, including auth token and uri - and yes, it uses MQTT). I would love to contribute a "real" implementation back to this project if someone could help talk through how best to approach it and what changes would need to be made.

For those who are looking for how to set up a Dart-based AppSync Subscription, here is some pseudo-code (Note, this relies on mqtt_client.dart and amazon_cognito_identity_dart packages):

https://gist.github.com/bobwiller/b669f3476fcd2805b4f25b96243d859a

@Ibtesam-Mahmood
Copy link

Ibtesam-Mahmood commented Jul 30, 2019

@micimize I finally have this working in Dart/Flutter (but outside of this package). I don't know a lot about other GraphQL implementations, but for AWS Appsync, this is a two step process: An http query, and then the socket is built based on the response from that call. (The socket gets all the data it needs to connect from the response, including auth token and uri - and yes, it uses MQTT). I would love to contribute a "real" implementation back to this project if someone could help talk through how best to approach it and what changes would need to be made.

For those who are looking for how to set up a Dart-based AppSync Subscription, here is some pseudo-code (Note, this relies on mqtt_client.dart and amazon_cognito_identity_dart packages):

https://gist.github.com/bobwiller/b669f3476fcd2805b4f25b96243d859a

Hi, I was wondering if the link for the pseudocode for the aws appsync subscriptions could be reposted

@micimize
Copy link
Collaborator

@Ibtesam-Mahmood https://gist.github.com/bobwiller/b669f3476fcd2805b4f25b96243d859a - was a markdown typo.

@bobwiller I think you'd want to implement a custom link with a mqtt client, similar in structure to how the websocket link works

@isaiahtaylorhh
Copy link
Contributor

If you're only needing Cognito pool authorization, you can do that out of the box using AuthLink by passing in your JWT token.

final token = session.getAccessToken().getJwtToken(); // Comes from amazon_cognito_identity_dart_2

final HttpLink httpLink = HttpLink(
  uri: '[your graphql url]',
);

final AuthLink authLink = AuthLink(
  getToken: () => token, // pass your Cognito JWT token to the auth link
);

final Link link = authLink.concat(httpLink);

ValueNotifier<GraphQLClient> client = ValueNotifier(
  GraphQLClient(
    cache: InMemoryCache(),
    link: link,
  ),
);

Then, pass this client in to the provider:

return GraphQLProvider(
    client: client,
    child: MaterialApp(
      title: 'Flutter Demo',
      ...
    ),
  );

Versions:

amazon_cognito_identity_dart_2: ^0.1.9
graphql_flutter: ^3.0.0

@subhendukundu
Copy link

subhendukundu commented Mar 19, 2020

@isaiahtaylor I think this a great start. It would be great if we can create a small demo project to explain how it works that will be so much easy to visualize how to use it. I will try this and if it works probably will try to create a PR.

@NimaSoroush
Copy link
Contributor

If you're only needing Cognito pool authorization, you can do that out of the box using AuthLink by passing in your JWT token.

final token = session.getAccessToken().getJwtToken(); // Comes from amazon_cognito_identity_dart_2

final HttpLink httpLink = HttpLink(
  uri: '[your graphql url]',
);

final AuthLink authLink = AuthLink(
  getToken: () => token, // pass your Cognito JWT token to the auth link
);

final Link link = authLink.concat(httpLink);

ValueNotifier<GraphQLClient> client = ValueNotifier(
  GraphQLClient(
    cache: InMemoryCache(),
    link: link,
  ),
);

Then, pass this client in to the provider:

return GraphQLProvider(
    client: client,
    child: MaterialApp(
      title: 'Flutter Demo',
      ...
    ),
  );

Versions:

amazon_cognito_identity_dart_2: ^0.1.9
graphql_flutter: ^3.0.0

@isaiahtaylor : This is very useful but what if I need to get that amazon_cognito_identity_dart_2 token from my login page which further down into my app routes. Basically the authentication happens asynchronously. So at the start time I don't have the token. How would you solve that problem?

@isaiahtaylorhh
Copy link
Contributor

@NimaSoroush notice that the AuthLink getToken parameter does not take a String, but a FutureOr<String>. This means that you can pass it an async function that resolves the token, whenever it's available. The GraphQL library will await the result of calling your function until your token is ready.

Pass getToken a function which returns a Future<String>, and complete that future when your user signs in.

Quick pseudo-code:

abstract class TokenHolder {
  static Completer<String> tokenCompleter = new Completer();

  static Future<String> get token => tokenCompleter.future;
  static set token(String tokenValue) => tokenCompleter.complete(tokenValue);
}

// at top level / wherever you want to initialize
final AuthLink authLink = AuthLink(
  getToken: () => TokenHolder.token,
);

// in your sign in flow
void yourSignInFunction(String token) {
  TokenHolder.token = token;
}

Alternatively, rather than having a class, you can pass your completer down to the widgets that need it.

void runApp() {
  final tokenCompleter = new Completer<String>();

  final AuthLink authLink = AuthLink(
    getToken: () => tokenCompleter.future,
  );

  // now, pass `tokenCompleter` down the chain
  // to your SignIn component, and when the user signs in,
  // call tokenCompleter.complete(token).
}

Hope this helps.

@NimaSoroush
Copy link
Contributor

Hey @isaiahtaylor that was amazingly helpful. Thanks a lot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
wontfix This will not be worked on
Projects
None yet
Development

No branches or pull requests

7 participants