aws-appsync-community icon indicating copy to clipboard operation
aws-appsync-community copied to clipboard

Do subscriptions actually work with OIDC?

Open a-h opened this issue 3 years ago • 5 comments

I created a GraphQL schema:

type Query {
  messages: [Message!]! @aws_iam @aws_oidc
}

type Message @aws_iam @aws_oidc {
  topic: ID
  text: String!
}

input SendMessageInput {
  topic: ID!
  text: String!
}
type Mutation {
  sendMessage(message: SendMessageInput!): Message
    @aws_iam
}
type Subscription {
  onSendMessage(topic: ID): Message @aws_subscribe(mutations: ["sendMessage"])
}

schema {
  query: Query
  mutation: Mutation
  subscription: Subscription
}

The idea is that only the server can send messages by executing the onSendMessage mutation. I had this working fine with API keys and IAM, but in actual usage, I need to have users authenticated.

I configured AppSync to use Google for OIDC and configured everything to allow this at the Google side.

    const api = new appsync.GraphqlApi(this, "subscriptionApi", {
      name: "subscriptionApi",
      schema: appsync.Schema.fromAsset(
        path.join(__dirname, "../graphql/schema.graphql")
      ),
      authorizationConfig: {
        defaultAuthorization: {
          authorizationType: appsync.AuthorizationType.OIDC,
          // Get from https://console.cloud.google.com/apis/api/cloudidentity.googleapis.com
          openIdConnectConfig: {
            oidcProvider: "https://accounts.google.com",
            clientId:
              "xxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
          },
        },
        additionalAuthorizationModes: [
          { authorizationType: appsync.AuthorizationType.IAM },
        ],
      },
      xrayEnabled: true,
    });

It looks like there's a bug in the AWS console that prevents subscriptions from working in there with OIDC. It silently fails but logs it in the console (that wasted a lot of time).

Screen Shot 2021-02-21 at 12 37 45

This error is also referenced here: https://stackoverflow.com/questions/62283062/how-can-i-authenticate-subscription-with-openid-connect-in-appsync

So I tried out a subscription with graphiql.app, modifying the headers to include my Google-issued JWT, unfortunately that doesn't support subscriptions in the way I expect, and just returned a web socket URL. This was interesting because it shows how it's really just using IOT with a presigned URL.

Screen Shot 2021-02-21 at 09 52 14

I wasted some time writing little test harness to access the websocket URL in the browser, but got:

WebSocket connection to 'wss://xxxxxxx failed: Error during WebSocket handshake: Unexpected response code: 426

So I abandoned that, and found graphqurl (https://github.com/hasura/graphqurl) and tried that out.

export GRAPHQL_ENDPOINT="https://xxxxxx.appsync-api.eu-west-2.amazonaws.com/graphql"
export GRAPHQL_AUTH_HEADER="Authorization: xxxxxxxxxxx"
npx graphqurl $GRAPHQL_ENDPOINT -H "$GRAPHQL_AUTH_HEADER" \
	-q 'subscription MySubscription { onSendMessage { topic text } }' -i

This pops up an interactive GraphiQL which does support subscriptions, but I still get an error.

Screen Shot 2021-02-21 at 12 53 47 Screen Shot 2021-02-21 at 12 52 25

I could see from the metrics that the Lambda function that backs the subscription wasn't getting executed at all.

    const onSendMessage = new lambdaNode.NodejsFunction(this, "onSendMessage", {
      runtime: lambda.Runtime.NODEJS_12_X,
      entry: path.join(
        __dirname,
        "../handlers/graphql/subscription/onSendMessage.ts"
      ),
      handler: "handler",
      memorySize: 1024,
      tracing: lambda.Tracing.ACTIVE,
    });

    api.addLambdaDataSource("onSendMessageDS", onSendMessage).createResolver({
      typeName: "Subscription",
      fieldName: "onSendMessage",
    });

So, after enabling detailed logs, I could see that the requests were coming in from the tool (localhost:4500) and being rejected.

8ef7f10a-47a9-4ee5-8ece-e3e9fdd72822 Request Headers: {sec-websocket-protocol=[graphql-ws], cloudfront-viewer-country=[GB], sec-websocket-extensions=[permessage-deflate; client_max_window_bits], origin=[http://localhost:4500], x-forwarded-port=[443], sec-websocket-version=[13], via=[1.1 ace508199bd84d41add2e7c3b2f8dada.cloudfront.net (CloudFront)], cloudfront-is-desktop-viewer=[true], host=[mabyg5omevh6ndymqjhrafc35a.appsync-api.eu-west-2.amazonaws.com], connection=[upgrade], sec-websocket-key=[xxxx], cache-control=[no-cache], accept-language=[en-US,en;q=0.9], x-forwarded-proto=[https], upgrade=[websocket], x-forwarded-for=[xxxx], pragma=[no-cache], cloudfront-is-smarttv-viewer=[false], x-amzn-trace-id=[Root=1-603258f7-367b56910492de020b69e80f], cloudfront-is-tablet-viewer=[false], cloudfront-forwarded-proto=[https], accept-encoding=[gzip, deflate, br], x-amz-cf-id=[BmSQVyr3ogCATYc8hXh66QAP1pntUvxbEqrZHIt4WQOahf4mdZC0xw==], user-agent=[Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36], cloudfront-is-mobile-viewer=[false]}
8ef7f10a-47a9-4ee5-8ece-e3e9fdd72822 Response Headers: {x-amzn-ErrorType=UnauthorizedException}

But... I can execute a query using OIDC just fine.

Screen Shot 2021-02-21 at 13 10 11

I tried adding @aws_oidc to the subscription just as a test, and it made no difference at all.

Are OIDC supported for subscriptions? I can't see what I'm missing.

a-h avatar Feb 21 '21 13:02 a-h

Sorry for the trouble. We have a bug right now where OIDC subscriptions do not work through the console. We will address this shortly.

jpignata avatar Mar 18 '21 02:03 jpignata

Thanks for the response @jpignata - did you see the bit where I use graphqurl to work with a subscription using OIDC and that didn't work either?

I understand that the AWS console doesn't currently work (and hasn't for at least 9 months), graphiql.app doesn't work either (probably because of a bug or missing feature in that), but from what I can see, graphqurl seems to be doing the right thing - sending the Authorization header to AppSync, but the connection is not established.

Based on that, I started to think that the problem was with the AWS side, hence the question - do AppSync subscriptions work with OIDC?

This documentation https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html#header-parameter-format-based-on-appsync-api-authorization-mode makes it look like it does, but I have to create custom websocket code rather than use a GraphQL library.

a-h avatar Mar 19 '21 17:03 a-h

@jpignata Hi. I'm experiencing the same problem. Are there any solution?

slavaptitsyn avatar Jun 04 '21 14:06 slavaptitsyn

I'm experiencing the same problem too with auth0 oidc integration. Everything work, except subscriptions.

LuisMourao avatar Jul 08 '21 22:07 LuisMourao

@LuisMourao - If it helps, I decided not to use AppSync for my project because I ran into so many problems. I wrote up my experience at https://adrianhesketh.com/2021/02/22/setting-up-appsync-graphql-subscriptions-with-typescript-and-cdk/

a-h avatar Jul 12 '21 08:07 a-h