aws-mobile-appsync-sdk-js icon indicating copy to clipboard operation
aws-mobile-appsync-sdk-js copied to clipboard

Use multi-auth with Apollo client

Open mdegrees opened this issue 5 years ago • 4 comments

When using Apollo client with Appsync, there seems to be no example showing the multi-auth scenario. On my schema I'm using @aws_api_key @aws_cognito_user_pools on multiples queries as in my use case some data is accessible to guests as well as to users.

What is the current behavior? Currently you can either use API key or Cognito Pool as demonstrated here: https://github.com/awslabs/aws-mobile-appsync-sdk-js#using-authorization-and-subscription-links-with-apollo-client-no-offline-support

What I expected Providing both the api Key and Jwt Token with, AppSync picks the appropriate

**What I have tried ** I tried the bellow hack but it seems too cumbersome + subscriptions do not work.

import { createAuthLink } from "aws-appsync-auth-link";
import { SubscriptionHandshakeLink } from "aws-appsync-subscription-link/lib/subscription-handshake-link";
import { NonTerminatingHttpLink } from "aws-appsync-subscription-link/lib/non-terminating-http-link";
import { getMainDefinition } from "apollo-utilities";
import { ApolloLink } from "apollo-link";
import { HttpLink } from "apollo-link-http";
import ApolloClient from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
import fetch from "isomorphic-unfetch";
import { setContext } from "apollo-link-context";
import config from "../../src/aws-exports";
import Amplify from "@aws-amplify/core";
import Auth from "@aws-amplify/auth";

Amplify.configure(config);

const url = config.aws_appsync_graphqlEndpoint;

const httpLink = new HttpLink({ uri: url, fetch });

const apolloAuthLink = setContext(async (_, { headers }) => {
  try {
    const session = await Auth.currentSession();
    return {
      headers: {
        ...headers,
        authorization: session.getIdToken().getJwtToken(),
      },
    };
  } catch (e) {
    return {
      headers: {
        ...headers,
        "X-Api-Key": config.aws_appsync_apiKey,
      },
    };
  }
});

const wsLink = ApolloLink.from([
  new NonTerminatingHttpLink("subsInfo", { uri: url }, true),
  new SubscriptionHandshakeLink("subsInfo"),
]);

let link;

if (process.browser) {
  link = ApolloLink.from([
    apolloAuthLink,
    ApolloLink.split(
      (operation) => {
        const { query } = operation;
        const definition = getMainDefinition(query);
        return definition.kind === "OperationDefinition" && definition.operation === "subscription";
      },
      wsLink,
      httpLink
    ),
  ]);
} else {
  link = ApolloLink.from([apolloAuthLink, httpLink]);
}

export default function createApolloClient(initialState, ctx) {
  // The `ctx` (NextPageContext) will only be present on the server.
  // use it to extract auth headers (ctx.req) or similar.
  return new ApolloClient({
    ssrMode: Boolean(ctx),
    link,
    cache: new InMemoryCache().restore(initialState),
  });
}

Thank you

mdegrees avatar May 13 '20 12:05 mdegrees

I'm using a similar hack except it's a little more cumbersome cos the fallback auth scheme I'm using is @aws_iam and not @ aws_api_key key which requires sigv4 signed headers.

boredcode avatar May 13 '20 17:05 boredcode

+1

dtelaroli avatar Jun 10 '20 13:06 dtelaroli

I used ApolloLink.split() to decide which link to use

import { ApolloClient, ApolloLink, HttpLink } from '@apollo/client'
import { createAuthLink } from 'aws-appsync-auth-link'

let accessToken

// a function to be called when user login/out or when access token expires
export const setAccessToken = (token) => {
  accessToken = token
}

function createApolloClient() {
  // API key link
  const apiAuthLink = createAuthLink({
    auth: {
      type: 'API_KEY',
      apiKey: 'ABC123...',
    },
  })

  // OpenID Connect link
  const oidcAuthLink = createAuthLink({
    auth: {
      type: 'OPENID_CONNECT',
      jwtToken: async () => accessToken,
    },
  })
  
  // decide which the proper link from above to use (directional link)
  const awsLink = ApolloLink.split((operation) => {
    // use your own conditions here to decide which link to use. e.g. Auth.currentSession()
    return (operation.operationName === `getUserDetails` || operation.variables.id === 1)
  }, oidcAuthLink, apiAuthLink)

  // http link (the terminating link in the chain)
  const httpLink = new HttpLink({
    uri: 'https://xxx.appsync.aws.com/graphql',
  })

  // create ApolloClient with AWS links and cache
  return new ApolloClient({
    link: ApolloLink.from([ awsLink, httpLink ]),
    cache: new InMemoryCache(),
  })
}

anasqadrei avatar Oct 09 '20 01:10 anasqadrei

I'm using a similar hack except it's a little more cumbersome cos the fallback auth scheme I'm using is @aws_iam and not @ aws_api_key key which requires sigv4 signed headers.

Hi @boredcode, could you paste some code? I think I'll need to go for something similar..

tobiasriemenschneider avatar Apr 05 '21 12:04 tobiasriemenschneider