question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Complex authorization with schema stitching

See original GitHub issue

Hi, big thanks first of all to all contributors to graphql-tools and this awesome handbook repo. I was hesitating going the apollo federation route for quite some time and for good reason it appears.

The only big question I have still left is how to handle complex authorization requirements with a schema gateway and the graphql services behind it. If we stick to the shopping scenario, how would one handle the following in a good way?

The desired gateway schema should look similar to this:

type User {
  id: ID!
  ownedStorefronts: [Storefront]!
}

type Storefront {
  id: ID!
  name: String!
  owners: [User]!
  products: [Product]!
}

type Product {
  upc: ID!
  storefront: Storefront!
  name: String!
  price: Float!
  purchasingPrice: Float!
}

type Mutation  {
  updateProductPrice(upc: ID!, price: Float!): Product
}

In this case only users who own the product via the intermediate storefront relation should be able to update its price or see its purchasingPrice. But since the product service has only the storefront foreign ID to work with it knows nothing about users and therefore cannot check if the current user is actually allowed to update the product.

Since the number of products a user can own could become quite large it’s not feasable to store this information in the user token I fear.

To handle the logic for the authorization in the gateway service could be possible I guess, but feels wrong too. That’s kinda what we want to avoid with schema stitching in the first place, right? 😉

All I can think of right now is to send a request to the other service(s) from inside the product service resolvers. Would that be the best way to handle it or am I missing something obvious?

I hope this is the right place to ask these kind of questions and thanks in advance for any ideas or feedback.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:12 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
gmaccommented, Feb 5, 2021

@tsteenkamp Thanks for the question! It’s a good one, and it’s been asked before so it’s on my list to eventually add a chapter that covers it. The good news is that it’s not exceptionally difficult to handle auth. Here’s how:

  1. In the gateway server, pull any auth information out of the user request and add that into the GraphQL context object that is built for the request.
app.use(
  '/graphql',
  graphqlHTTP(async (request, response, graphQLParams) => ({
    schema: MyGraphQLSchema,
    context: {
      // extract "Bearer mytoken" into GraphQL context...
      userAuthToken: request.headers.authorization.split(' ').pop(),
    }
  })),
);
  1. In your remote executors, you will receive the GraphQL context object as one of the argument fields. So, it’s just a matter of transferring auth information from the gateway GraphQL context into the subservice request headers. It’d look something like this:
function makeRemoteExecutor(url) {
  return async ({ document, variables, context }) => {
    const query = typeof document === 'string' ? document : print(document);
    const fetchResult = await fetch(url, {
      method: 'POST',
      headers: { 
        'Authorization': `Bearer ${context.userAuthToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ query, variables }),
    });
    return fetchResult.json();
  };
};

That should be it! Now your subservice will receive the same information that the gateway did, and it can make its own localized decisions about how to handle the request.

0reactions
yaacovCRcommented, Apr 14, 2021

Agreeing with @gmac above, but to answer your question more specifically, it seems like you are asking an express-session question in terms of how to set a value within the session.

It seems like it is done as follows:

return async ({ document, variables, context }) => {
		const query = typeof document === "string" ? document : print(document);
		const fetchResult = await fetch(url, {
			method: "POST",
			headers: {
				// not editing below for now, see comments below this code
				cookie: context?.cookie,
				"Content-Type": "application/json",
			},
			body: JSON.stringify({ query, variables }),
		});
		//YOUR QUESTION:
		//console.log(fetchResult.headers); <-- if I console log this the cookie shows from the authentication microservice
		//how do I pass this up and set it to the users cookies?
		// 
		// MY THOUGHT:
		const { authToken, refreshToken } = extractAuthTokenFromFetchResult(fetchResult);
		context.req.session.token = { authToken, refreshToken };
		return fetchResult.json();
	};

I believe the idea of express-session is you get access to a serializable session object which is transparently associated with the correct client via a cookie, but with the session data perhaps stored server side in any compatible store, such that you get to set and retrieve whatever values you want simply by modifying and reading the session object.

Note the comment above in terms of how you are passing the refreshToken to the downstream service. I am not addressing that here, I imagine you would use the Authorization header with the bearer token scheme, and not just pass the cookie from the client.

Read more comments on GitHub >

github_iconTop Results From Across the Web

On GraphQL Schema Stitching & API Gateways
The schema gateway takes n number of schemas, merges them together, and extends the result with these “relations” we've been talking about.
Read more >
4 ways to stitch, integrate, compose & federate multiple ...
Implementing authentication and authorization for a single API is already a complex task, but it's doable. As you own the API implementation, ...
Read more >
Schema Stitching - Hot Chocolate v10
What is schema stitching actually? Schema stitching is the capability to merge multiple GraphQL schemas into one schema that can be queried.
Read more >
gmac/schema-stitching-handbook: Guided examples ... - GitHub
Guided examples of Schema Stitching doing awesome things. Focuses on the new (GraphQL Tools v6+) stitching using type merging, not legacy Apollo Stitching....
Read more >
GraphQL Schema Stitching with ASP.Net Core, Michael Staib
NET Summit 2019, MinskFreaky .NET TrackGeneral Partner - ArasCorp Development Center (https://companies.dev.by/arascorp-development-center) ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found