Realtime/GraphQL: AppContext carries no request origin, so a resolver cannot enforce an embed allowlist

Open Beginner friendly
#3,910 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
72/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Active
Tech stack
graphql, typescript

Research direction

Start in packages/MJServer/src/context.ts at contextFunction and the existing requestDomain calculation. Confirm the agreed AppContext shape, then ensure the normalized request origin or request information reaches resolvers; done means a resolver can inspect the request origin for authorization decisions, including when the client sends no Origin header.

Written by the indexing model from the issue text.

Description

Summary

AppContext gives resolvers { dataSource, dataSources, userPayload, providers } and nothing about the HTTP request. So a consumer that wants to make an authorization decision based on where the request came from — an embed origin allowlist, the browser equivalent of an IP allowlist — cannot, at any layer it owns.

MJ already computes the value. packages/MJServer/src/context.ts does:

const requestDomain = parseRequestHostname(req.headers.origin);

and threads it through authentication and login auditing. It is simply never put on the context the resolvers receive (contextFunction returns { dataSource, dataSources, userPayload, providers }).

Verified on next @ 6.1.0-edge.2.

Why a consumer cannot work around it

Three routes, all bad:

  1. Take the origin as a mutation argument. Client-supplied and trivially forged, so it is not a control — but it looks like one in the Studio, which is worse than not offering it. This is the "authored field that silently does nothing" failure mode of #3374 / #3859 / #3854, in its most dangerous form: a security setting.
  2. Enforce in Express middleware instead. Works only for routes the consumer owns. An embedded widget on a customer's own site talks to /graphql directly, which is exactly the case an embed allowlist is for.
  3. Read it from an async-local store. Nothing exposes one, and reaching around the context to grab the request would be depending on an implementation detail.

The ask

Surface what is already computed. Either shape works:

export type AppContext = {
  …
  /** The request's `Origin` header, normalized, or undefined when the client sent none. */
  requestOrigin?: string;
};

or the raw headers / req itself, if that is more in keeping — the origin is what is needed, but a consumer with the request can answer questions nobody has asked yet.

requestDomain already exists a few lines away in the same function, so this looks like roughly a field on a type and a line in contextFunction. Happy to open the PR if the shape is agreed — I did not want to guess between "just the origin" and "the whole request".

What it unblocks downstream

A downstream product with an embeddable widget needs origin allow-listing, and today there is no MJ-side primitive to build it on — the host application cannot even SEE the request origin at the resolver layer, so any origin control has to be reinvented outside MJ per product. The column, grammar and inheritance for an allowlist are built and merged behind this; the authoring UI is deliberately withheld until the check can actually run, precisely so it does not become another silently-ignored setting.

Dominant language
TSQL
Stars
29
Forks
6
Avg merge
1d 1h
Merged PRs (30d)
295

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from MemberJunction/MJ

All issues in MemberJunction/MJ

Similar issues

More Backend & API Design issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.