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

Authenticating User at API Gateway and then redirecting them to microservices #54

Open
ghost opened this issue Apr 1, 2019 · 7 comments
Labels

Comments

@ghost
Copy link

ghost commented Apr 1, 2019

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

@Yogu
Copy link
Member

Yogu commented Apr 1, 2019

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 extending HttpGraphQLClient. For example, you could override getHeaders, look into the request (passed as context), 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

@ghost
Copy link
Author

ghost commented Apr 1, 2019

@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?

@Yogu
Copy link
Member

Yogu commented Apr 1, 2019

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.

@ghost
Copy link
Author

ghost commented Apr 1, 2019

@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?

@Yogu
Copy link
Member

Yogu commented Apr 2, 2019 via email

@ghost
Copy link
Author

ghost commented Apr 12, 2019

@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?

@Yogu
Copy link
Member

Yogu commented Apr 13, 2019

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.

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

1 participant