Skip to content

Amplify Subscriptions supportΒ #1163

@ZRunner

Description

@ZRunner

I cannot seem to be able to use GraphQL subscriptions with my AWS server, by using Cognito authentication.
Queries and mutations work fine as far as I can tell, but I couldn't find how to connect to the amplify websocket.

Here is the code I tried, based on this comment: #682 (comment) (which I had to edit because Cognito tokens are not static)

import 'dart:async';
import 'dart:convert';

import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:flutter/material.dart';
import 'package:gql/language.dart';
import 'package:graphql_flutter/graphql_flutter.dart';

String toBase64(Map data) => base64.encode(utf8.encode(jsonEncode(data)));

const apiId = "XXX";
const zone = "eu-west-1";

class AppSyncRequest extends RequestSerializer {
  final Map<String, dynamic> authHeader;

  const AppSyncRequest({
    required this.authHeader,
  });

  @override
  Map<String, dynamic> serializeRequest(Request request) => {
        "data": jsonEncode({
          "query": printNode(request.operation.document),
          "variables": request.variables,
        }),
        "extensions": {
          "authorization": authHeader,
        }
      };
}

ValueNotifier<GraphQLClient> buildClient() {
  Future<String?> _fetchSession() async {
    try {
      final session = await Amplify.Auth.fetchAuthSession(
              options: CognitoSessionOptions(getAWSCredentials: true))
          as CognitoAuthSession;
      if (!session.isSignedIn) {
        return null;
      }
      return session.userPoolTokens?.accessToken;
    } on AuthException catch (e) {
      debugPrintStack(label: e.toString());
    }
    return null;
  }

  Future<String?> _getIdToken() async {
    try {
      final session = await Amplify.Auth.fetchAuthSession(
              options: CognitoSessionOptions(getAWSCredentials: true))
          as CognitoAuthSession;
      if (!session.isSignedIn) {
        return null;
      }
      return session.userPoolTokens?.idToken;
    } on AuthException catch (e) {
      debugPrintStack(label: e.toString());
    }
    return null;
  }

  final HttpLink httpLink =
      HttpLink("https://$apiId.appsync-api.$zone.amazonaws.com/graphql");

  final WebSocketLink wsLink = WebSocketLink(
    'wss://$apiId.appsync-realtime-api.$zone.amazonaws.com/graphql',
    config: SocketClientConfig(
      initialPayload: () async {
        final token = await _fetchSession();
        return {
          "headers": {
            "Authorization": '$token',
            "host": "$apiId.appsync-api.$zone.amazonaws.com",
          },
        };
      },
    ),
  );

  final AuthLink authLink = AuthLink(
    getToken: _fetchSession,
  );

  // final Link link = authLink.concat(httpLink).concat(wsLink);
  final Link link = Link.split((request) => request.isSubscription,
      authLink.concat(wsLink), authLink.concat(httpLink));

  ValueNotifier<GraphQLClient> client = ValueNotifier(
    GraphQLClient(
      link: link,
      defaultPolicies:
          DefaultPolicies(query: Policies(fetch: FetchPolicy.cacheAndNetwork)),
      cache: GraphQLCache(
        partialDataPolicy: PartialDataCachePolicy.accept,
        store: HiveStore(),
      ),
    ),
  );
  return client;
}

class ClientProvider extends StatefulWidget {
  final Widget child;

  const ClientProvider({
    Key? key,
    required this.child,
  }) : super(key: key);

  @override
  State<ClientProvider> createState() => _ClientProviderState();
}

class _ClientProviderState extends State<ClientProvider> {
  @override
  Widget build(BuildContext context) {
    return GraphQLProvider(
      client: buildClient(),
      child: widget.child,
    );
  }
}
  • If I use the Link.split() method, my console is spammed with flutter: Disconnected from websocket.
  • If I use the Link.concat() method, I get a HttpLinkServerException: GraphQLError(message: Subscriptions over MQTT is not supported., locations: null, path: null, extensions: null)

Naturally in both ways my Subscription widget doesn't work (infinite loading state).

Device / execution context

  • Executed on macOS 12.4 (MacBook Pro M1 Pro)
  • iOS 15.5
  • Flutter 3.0.2
  • graphql_flutter 5.1.0
  • amplify_flutter 0.5.1
  • amplify_api 0.5.1
  • XCode 13.4.1
  • VSCode 1.68.1

Metadata

Metadata

Labels

⚑ websocketWeb Socket RelatedPriority: HighHigh priority to include it inside next release

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions