graphql-weaver
graphql-weaver copied to clipboard
Authenticating User at API Gateway and then redirecting them to microservices
I am trying to build an API Gateway with GraphQL for my microservices architecture. Here's how I am trying to implement this:
- Suppose we have 4 microservices named as M1, M2, M3, and M4
- The M1 microservice is the one which is responsible for logging in the user and generating JWT token
- Whenever user makes a request for other microservices like M2, M3, or M4, he must be logged in to the system
Following are the expected API Gateway features:
- Wherever a request comes, if it's for microservice M1 the API Gateway should just redirect the user to that gateway
- If user requests for M2, M3 or M4 microservices the API Gateway should check for JWT token, decode it, and append the data into the header.
Following are the things is not expected from the API Gateway:
- Writing all schemas from all microservices in the API Gateway. Since it is not practical, if I change the schema in Microservice M3 I have to change it at the API gateway also which I don't want to do
This framework is all cool, but I don't find any examples or methods through which I can achieve this. Maybe someone can guide me through.
Perhaps @Yogu you can help me.
Thanks
To write custom authentication logic, you need to do two things:
- Make sure that you have access to the request data in the GraphQL context object. For example, if you're using express-graphql, the express request will be passed as
context
, so you're all set. - Implement a custom custom
GraphQLClient
client, e.g. by extendingHttpGraphQLClient
. For example, you could overridegetHeaders
, look into the request (passed ascontext
), do the token decoding and set appropiate headers for the proxied request.
Then, you can use a config like this:
const schema = weaveSchemas({
endpoints: [, {
namespace: 'M1',
url: 'http://m1/...'
}, {
namespace: 'M2',
client: new CustomHttpGraphQLClient('http://m2/...')
}, /* ... */
]
});
I hope this helps
@Yogu Few doubts:
- What is the `CustomHttpGraphQLClient?
- Where would I write the code for decoding the JWT token?
- Do I have to pass the client property for microservices M3 and M4 also?
CustomGraphQLClient
would be your subclass of HttpGraphQLClient
, something like this:
class CustomGraphQLClient extends HttpGraphQLClient {
protected async getHeaders(document: DocumentNode, variables?: { [name: string]: any }, context?: any, introspect?: boolean): Promise<{ [index: string]: string }> {
const request = context as Request;
// implement your logic here for decoding the token based on the express request
const regularHeaders = super.getHeaders(document, variables, context, introspect);
return {
...regularHeaders,
// add your headers here
};
}
}
Do I have to pass the client property for microservices M3 and M4 also?
I understood that M2, M3 and M4 are basically the same, so yes you would need to add endpoints with the custom client for all three of them.
@Yogu I understand that above mentioned solution will add a header (in which token data will be available for other to use) to the original request but I still don't understand how it will stop the request at the API Gateway if no JWT Token is found int the request header?
It's completely up to you how you implement that method, you can just throw an Error if the user is not authenticated.
@Yogu Hey what if I am not using Typescript?
class CustomGraphQLClient extends HttpGraphQLClient { protected async getHeaders(document: DocumentNode, variables?: { [name: string]: any }, context?: any, introspect?: boolean): Promise<{ [index: string]: string }> { const request = context as Request; // implement your logic here for decoding the token based on the express request const regularHeaders = super.getHeaders(document, variables, context, introspect); return { ...regularHeaders, // add your headers here }; } }
Then how can I implement this custom class?
Ah, just remove some parts:
class CustomGraphQLClient extends HttpGraphQLClient {
async getHeaders(document, variables) {
const request = context;
// implement your logic here for decoding the token based on the express request
const regularHeaders = super.getHeaders(document, variables, context, introspect);
return {
...regularHeaders,
// add your headers here
};
}
}
If you need to target an older version of JavaScript, just use a transpiler of your choice.