Closed BerndWessels closed 5 years ago
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 😉
@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?
@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.
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....
@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
@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
@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
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 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.
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?
@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.
Hey @isaiahtaylor that was amazingly helpful. Thanks a lot
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.