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

Is it possible to use aws-amplify for GraphQL queries? #434

Closed
ildar-icoosoft opened this issue Mar 12, 2018 · 8 comments
Closed

Is it possible to use aws-amplify for GraphQL queries? #434

ildar-icoosoft opened this issue Mar 12, 2018 · 8 comments
Labels
question General question

Comments

@ildar-icoosoft
Copy link

ildar-icoosoft commented Mar 12, 2018

I need to send GraphQL queries to AWS API Gateway (IAM authorizer). I could use a GraphQL client library (for example Apollo) but I have a problem with signing the request. I could use aws-amplify signer.js but I can't get any request object from Apollo library. Are there some examples of how to use aws-amplify with GraphQL?

@davegariepy
Copy link

davegariepy commented Mar 12, 2018

I'm using Apollo 2 with setContext link like this:

import ApolloClient from 'apollo-client';
import { ApolloLink } from 'apollo-link';
import { setContext } from 'apollo-link-context';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { withClientState } from 'apollo-link-state';
import { clientState } from './clientState';
import { Auth } from 'aws-amplify';

const cache = new InMemoryCache();

//TODO:  need to cache token
const authLink = setContext((request) => new Promise( (resolve, reject) => {
  Auth.currentSession()
  .then(session => {
    const token = session.idToken.jwtToken;
    resolve({
      headers: { Authorization: token }
    });
  })
}));

const stateLink = withClientState({ ...clientState, cache });

const client = new ApolloClient({
  cache,
  link: ApolloLink.from([
    authLink,
    stateLink, //near end but before HttpLink
    new HttpLink({uri: process.env.REACT_APP_GRAPHQL_ENDPOINT })
  ])
});

export default client;

also, don't forget to put this somewhere in a top level file like app.js:

Amplify.configure({
  Auth: {
    identityPoolId: '', //REQUIRED - Amazon Cognito Identity Pool ID
    region: '', // REQUIRED - Amazon Cognito Region
    userPoolId: '', //OPTIONAL - Amazon Cognito User Pool ID
    userPoolWebClientId: '', //OPTIONAL - Amazon Cognito Web Client ID
  },
  Storage: {
    bucket: '', //REQUIRED -  Amazon S3 bucket
    region: '', //OPTIONAL -  Amazon service region
  }
});

with regard to API gateway, you need to create a user pool and set the authorization to use that pool in the API gateway settings:

  • in API gateway, in your endpoint settings select 'authorizers'
  • type: 'cognito'
  • cognito user pool: your user pool
  • token source: 'Authorization'
  • token validation: blank

I did this a while ago, but if I remember correctly I also had to enable CORS and redeploy the resource.

I'm using lambda and lambda proxy and this is what I have in my handler:

import 'babel-polyfill';
import { graphqlLambda, graphiqlLambda } from 'apollo-server-lambda';
import lambdaPlayground from 'graphql-playground-middleware-lambda';
import { makeExecutableSchema } from 'graphql-tools';
import { typeDefs } from './data/typeDefs';
import { resolvers } from './data/resolvers';

const myGraphQLSchema = makeExecutableSchema({
  typeDefs,
  resolvers,
});

const CORS_ORIGIN = 'http://some-website.com';

exports.graphqlHandler = function(event, context, callback) {

  context.callbackWaitsForEmptyEventLoop = false;

  console.log("event", JSON.stringify(event));

  const requestOrigin = event.headers.origin,
  callbackFilter = function(error, output) {
   
    if (requestOrigin === CORS_ORIGIN) {
      output.headers['Access-Control-Allow-Origin'] = '*';
      output.headers['Access-Control-Allow-Credentials'] = 'true';
    }

    callback(error, output);
  };

  const handler = graphqlLambda((event, context) => {
    const headers = event.headers,
    functionName = context.functionName;

    const userId = process.env.NODE_ENV === 'development'
    ? process.env.TEST_USER_ID
    : event.requestContext.authorizer.claims.sub;

    return {
      schema: myGraphQLSchema,
      context: {
        userId,
        headers,
        functionName,
        event,
        context,
      },
    };
  });

  return handler(event, context, callbackFilter);
};

note that the userId is on event.requestContext.authorizer.claims.sub;

I can't remember the details, but as you are testing keep in mind that for AWS you need to set up IAM roles to allow some services to work together.

hope that helps

EDIT: updated handler to include Access-Control-Allow-Credentials: true and return callbackFilter

@mlabieniec mlabieniec added the question General question label Mar 12, 2018
@ildar-icoosoft
Copy link
Author

@mlabieniec Thank you! It is helpful for me, but I use IAM authorizer (NOT user pool authorizer like in your code). And the problem is that I can't just add authorizer header, because for IAM authorizer it is required to sign the request. I could sign the request using this code:

import Signer from 'aws-amplify/lib/Common/Signer';
...
Signer.sign(request, signerCredentials, serviceInfo);

So, I need to get the request object. And I don't know how to get request object in Apollo

@manueliglesias
Copy link
Contributor

manueliglesias commented Mar 13, 2018

@ildar-icoosoft

AWS AppSync is a managed GraphQL service that you might find useful as an alternative of API Gateway + Lambda + apollo-server.

I also mention AWS AppSync because it has a JavaScript client/sdk that can handle IAM Auth with sigv4 signing

You can probably take pieces from there and adapt as needed, e.g.:

Using the apollo client, you could add the auth-link from aws-appsync to the client link chain

You might need to customize the auth-link.js a little

I haven't tried this, but let me know how it goes!

@powerful23
Copy link
Contributor

Closing.

@honkskillet
Copy link

@manueliglesias @powerful23
How would you attach the user JWT in the headers when using AppSync? (When using IAM auth it is hard to get the User Pool data, instead AppSync give you the Identity Pool credentials.) The standard AppSync example looks something like this

import React from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
import appSyncConfig from "./AppSync";
import { ApolloProvider } from "react-apollo";
import AWSAppSyncClient from "aws-appsync";
import { Rehydrated } from "aws-appsync-react";
import { Auth } from 'aws-amplify'
import AWS from'aws-sdk';

import AllPosts from './Components/AllPosts';
// more routes

const Home = () => (
  <div > <AllPosts /> </div>
);

const App = () => (
  <div> <Router> <div> 
        <Route exact={true} path="/" component={Home} /> 
        //more routes
   </div> </Router> </div>
);

const client = new AWSAppSyncClient({
  url: appSyncConfig.graphqlEndpoint,
  region: appSyncConfig.region,  
  auth: {
    type: appSyncConfig.authenticationType,  //AWS_IAM
    apiKey: appSyncConfig.apiKey,  
    credentials: () => Auth.currentCredentials(),
});

const WithProvider = () => (
  <ApolloProvider client={client}>
    <Rehydrated>
      <App />
    </Rehydrated>
  </ApolloProvider>
);

export default WithProvider;

@honkskillet
Copy link

@mixja I've tried this. My question is specifically for using amplify w/ appSync. AppSync seems to over write the headers.

@github-actions
Copy link

This issue has been automatically locked since there hasn't been any recent activity after it was closed. Please open a new issue for related bugs.

Looking for a help forum? We recommend joining the Amplify Community Discord server *-help channels or Discussions for those types of questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 13, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
question General question
Projects
None yet
Development

No branches or pull requests

7 participants