In a previous post I wrote about using the Context Provider Pattern in order to pass around services, providers, and managers to your resolvers on a per-query lifecycle. One of the great uses of this pattern is the ability to handle authorization. In this post we are going to assume you are using a JWT token with token-bearer authorization. So that means you're passing up a JWT bearer token in the header like so:

{
    "Authorization": "Bearer YOUR_TOKEN"
}

Like before we are going to setup a new injectable service with inversify. In this case it will be the TokenProvider.

const ITokenProviderType = Symbol.for("ITokenProvider");
interface ITokenProvider {
    isLoggedIn: boolean;
    userId: number;
    roles: string[];
    fromRequest: (request: Request) => void;
}

In this case the Reqest is an ExpressJS request object that we will receive from the context callback when we configure our GraphQL endpoint. The point of this method is to take the request and hydrate the data in our TokenProvider based on the JWT in the header.

@injectable()
class TokenProvider implements ITokenProvider {
    private _userId: number = 0;
    private _isLoggedIn: boolean = false;
    private _roles: string[] = [];
    fromRequest(request: Request) {
        const authHeader = request.get("Authorization");
        if(auth) {
            const token = auth.replace("Bearer ", "");
            const claims = jwt.verify(token, ENDCODING_SECRET) as Claims;
            this._userId = claims.userId;
            this._isLoggedIn = true;
            this._roles = claims.roles;
        }
        catch {
            // do logging or whatever if the JWT can't be verified
        }
    }
    
    get userId() {
        return this._userId;
    }
    get isLoggedIn() {
        return this._isLoggedIn;
    }
    get roles() {
        return this._roles;
    }
}

In this case we are using the jsonwebtoken library to verify our token using the same encoding secret used to create it. Our claims model is a representation of the claims given to the token and you can setup the TokenProvider class to manage this information in whatever way best fits your application. You can also add QoL methods such as hasRole or isAdmin or whatever fits your needs.

We can now bind the TokenProvider in our IoC container and inject it into the ContextProvider we talked about in the previous post. You can also inject it directly into any service which is where I would suggest using it. Handling authorization in the service layer is ideal.

container.bind<ITokenProvider>(ITokenProviderType).to(TokenProvider).inRequestScope();

...

interface IContextProvider {
    bookingService: IBookingService;
    tokenProvider: ITokenProvider;
}

...

@injectable()
class ContextProvider implements IContextProvider {
    @inject(IBookingServiceType)
    public bookingService: IBookingService;
    @inject(ITokenProviderType)
    public tokenProvider: ITokenProvider;
}

The last step is to setup our context callback to hydrate our token provider with the request object.

app.use(
    '/graphql',
    cors(),
    bodyParser.json(),
    graphqlExpress((request) => (
        { 
            schema, 
            context: () => {
                const context = container.get<IContextProvider>(IContextProviderType);
                context.tokenProvider.fromRequest(request);
                return context;
            }
        }
    ))
);

Now, for the lifecycle of the query, each resolver and any service you inject TokenProvider into will have information on their authorized state.

Thanks for reading and happy coding!