Skip to content
This repository has been archived by the owner on Sep 20, 2022. It is now read-only.

Question: how to forward the origin extra http header ? #6

Closed
gengjiawen opened this issue Sep 18, 2017 · 8 comments
Closed

Question: how to forward the origin extra http header ? #6

gengjiawen opened this issue Sep 18, 2017 · 8 comments
Labels

Comments

@gengjiawen
Copy link

gengjiawen commented Sep 18, 2017

I need to pass the token to the server in my situation.

@Yogu
Copy link
Member

Yogu commented Sep 18, 2017

Good question; we faced this use case, too.

You can pass a custom GraphQLClient instance for endpoints to completely customize how data is fetched. In your situation, I'd suggest to extend HttpGraphQLClient:

class AuthForwardingGraphQLClient extends HttpGraphQLClient {
    protected async getHeaders(document: DocumentNode, variables?: { [name: string]: any }, context?: any, introspect?: boolean): Promise<{ [index: string]: string }> {
        const headers = await super.getHeaders(document, context, introspect);
        return {
            ...headers,
            Authentication: getAuthTokenFromGraphQLContext(context)
        };
    }
}

const schema: GraphQLSchema = await weaveSchemas({
    endpoints: [{
        namespace: 'library',
        client: new AuthForwardingGraphQLClient('http://example.com/graphql')
    }]
});

Then, you somehow need to implement getAuthTokenFromGraphQLContext. How this is done depends on your GraphQL server implementation. If you use express-graphql, the express Request object is passed as context, by default. Then, something like this would do the job:

function getAuthTokenFromGraphQLContext(context: any) {
    if (!context) {
        return undefined;
    }
    return context.header('Authentication');
}

There is one pitfall: When you call weaveSchemas, the endpoints are already called with an introspection query. At this time, there is no context. If you need an authentication token for introspection queries, you need to provide it thorugh a different manner, e.g. by configuration. Use the introspect argument to getHeaders to distinguish those initial introspection queries from normal queries by users.

@Yogu Yogu added the question label Sep 18, 2017
@gengjiawen
Copy link
Author

gengjiawen commented Sep 20, 2017

I followed your advice , but I still can't make it work, this is the demo code https://github.com/gengjiawen/schema-stitching-demo/tree/feature/pass_header, can you take a look when you have free time ? Thanks.

@Yogu
Copy link
Member

Yogu commented Sep 20, 2017

Sorry, I confused Authentication and Authorization. Also, express seems to convert the header names to lower case for some reason. I tested it locally and it at least dumped the correct token, though I did not test it with a real auth token. See this pull request in your project for the changes.

I only now realized you wrote Origin. Do you mean the HTTP Origin header? I assumed you meant authorization because you wrote token. If you want to forward the origin, just replace the header name.

@gengjiawen
Copy link
Author

thanks, I will give it a try tomorrow.

@gengjiawen
Copy link
Author

gengjiawen commented Sep 22, 2017

It works like as expected now, thanks. But why when i first running, I got log looks like this in the sample https://github.com/gengjiawen/schema-stitching-demo/tree/feature/pass_header? looks like the context not passed when get schema.

pass undefined
pass undefined
not valid undefined
not valid undefined

@gengjiawen
Copy link
Author

I see, that's because that time express-graphql is not used. Thanks for your great patience and help.

@gengjiawen
Copy link
Author

For anyone want to pass all the original headers, you can do something like this:
Notice to remove content-length from original header, or you will get timeout exception. This is found by @FX-HAO

class AuthForwardingGraphQLClient extends HttpGraphQLClient {
  protected async getHeaders(document: DocumentNode, variables?: { [name: string]: any }, context?: any, introspect?: boolean): Promise<{ [index: string]: string }> {
    const headers = await super.getHeaders(document, context, introspect);
    console.log('headers', headers)
    if (context) {
      console.log('context headers', context.headers)
      delete context.headers['content-length']
      return context.headers
    } else {
      return headers
    }
  }
}

@adamkl
Copy link

adamkl commented Sep 24, 2017

Hey Yogu, I was just working on this exact scenario last week and came up with pretty much the same solution you suggested.
It was very easy to take a look at your code to see how it could be extended. You guys did a great job with the design and API!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

3 participants