Skip to content

Commit

Permalink
feat: Setup AsyncLocalStorage context for Express.User throughout cal…
Browse files Browse the repository at this point in the history
…lstack
  • Loading branch information
DafyddLlyr committed Sep 20, 2023
1 parent 5a2c937 commit 5e069db
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 3 deletions.
33 changes: 31 additions & 2 deletions api.planx.uk/client/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { CoreDomainClient } from "@opensystemslab/planx-core";
import { userContext } from "../modules/auth/middleware";
import { ServerError } from "../errors";

/**
* core doesn't expose a graphql interface like the graphql/hasura clients do
* instead, they encapsulates query and business logic to only expose declarative interfaces
* @deprecated This client's permissions set are higher than required.
* Should only be used by trusted service-to-service calls (e.g Hasura -> API).
* Calls made by users should be directed through $public or the role-scoped getClient().
*
* Consider removing this and replacing with an "api" role using "backend-only" operations in Hasura
*/
export const $admin = new CoreDomainClient({
auth: { adminSecret: process.env.HASURA_GRAPHQL_ADMIN_SECRET! },
Expand All @@ -11,3 +17,26 @@ export const $admin = new CoreDomainClient({
export const $public = new CoreDomainClient({
targetURL: process.env.HASURA_GRAPHQL_URL!,
});

/**
* Get a planx-core client with permissions scoped to the current user.
* This client instance ensures that all operations are restricted
* to the permissions of the user who initiated the request.
*/
export const getClient = () => {
const store = userContext.getStore();
if (!store)
throw new ServerError({
status: 500,
message: "Missing user context",
});

const client = new CoreDomainClient({
targetURL: process.env.HASURA_GRAPHQL_URL!,
auth: {
jwt: store.user.jwt,
},
});

return client;
};
16 changes: 15 additions & 1 deletion api.planx.uk/modules/auth/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import passport from "passport";

import { RequestHandler } from "http-proxy-middleware";
import { Role } from "@opensystemslab/planx-core/types";
import { AsyncLocalStorage } from "async_hooks";

export const userContext = new AsyncLocalStorage<{ user: Express.User }>();

/**
* Validate that a provided string (e.g. API key) matches the expected value
Expand Down Expand Up @@ -153,7 +156,18 @@ export const useRoleAuth: UseRoleAuth =
});
}

next();
// Establish a context for the current request/response call stack using AsyncLocalStorage
// The validated user will be accessible to all subsequent functions
// Store the raw JWT to pass on to plan-core client
userContext.run(
{
user: {
...req.user,
jwt: req.cookies.jwt,
},
},
() => next(),
);
});
};

Expand Down

0 comments on commit 5e069db

Please sign in to comment.