@makerx/graphql-core 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -137,15 +137,15 @@ By default, if `claims` (decoded token `JwtPayload`) are available, the `GraphQL
137
137
 
138
138
  The User class adds some handy getters over raw claims (decodedJWT payload) and provides access to the JWT (access token) for on-behalf-of downstream authentication flows. Note this may represent a user or service principal (system) identity.
139
139
 
140
- | Property | Description |
141
- | -------- | ------------------------------------------------------------------------------------------------------------------- |
142
- | claims | The decoded JWT payload, set via the RequestInput.user field. |
143
- | token | The bearer token from the request authorization header. |
144
- | email | The user's email via coalesced claim values: email, emails, preferred_username, unique_name, upn. |
145
- | name | The user's name (via the name or given_name and family_name claims). |
146
- | id | The user's unique and immutable ID, useful for contextual differentiation e.g. session keys (the oid or sub claim). |
147
- | scopes | The user's scopes, via the scp claim split into an array of scopes. |
148
- | roles | The user's roles (via the roles claim). |
140
+ | Property | Description |
141
+ | -------- | --------------------------------------------------------------------------------------------------------------------------- |
142
+ | claims | The decoded JWT payload, set via the RequestInput.user field. |
143
+ | token | The access token. By default, the bearer token from the `Authorization` header (see [Token extraction](#token-extraction)). |
144
+ | email | The user's email via coalesced claim values: email, emails, preferred_username, unique_name, upn. |
145
+ | name | The user's name (via the name or given_name and family_name claims). |
146
+ | id | The user's unique and immutable ID, useful for contextual differentiation e.g. session keys (the oid or sub claim). |
147
+ | scopes | The user's scopes, via the scp claim split into an array of scopes. |
148
+ | roles | The user's roles (via the roles claim). |
149
149
 
150
150
  ### Custom user
151
151
 
@@ -163,6 +163,19 @@ const graphqlServer = createServer({
163
163
  })
164
164
  ```
165
165
 
166
+ ### Token extraction
167
+
168
+ The default `createUser` sets `User.token` to the bearer token from the `Authorization` header. If clients authenticate another way, for example with a JWT assertion header that a proxy adds to the request, supply `extractToken` to return the token:
169
+
170
+ ```ts
171
+ const createContext = createContextFactory({
172
+ requestLogger,
173
+ extractToken: ({ req }) => req.headers['x-goog-iap-jwt-assertion'] as string | undefined,
174
+ })
175
+ ```
176
+
177
+ `extractToken` is not called when you supply `createUser`. For subscriptions, see [GraphQL subscriptions](#graphql-subscriptions).
178
+
166
179
  ## Shield (authorization)
167
180
 
168
181
  The `shield` module provides typed helpers around [graphql-shield](https://the-guild.dev/graphql/shield) for defining authorization middleware against your generated `Resolvers` type.
@@ -363,7 +376,7 @@ This library includes a `subscriptions` module to provide simple setup using the
363
376
  1. Create a subscriptions server, using the ws-server cleanup function in your server lifecycle.
364
377
 
365
378
  The `useSubscriptionsServer` function sets up:
366
- - Auth token validation as part of establishing (or rejecting) the connection (behaviour defined by `verifyToken` and `requireAuth` args)
379
+ - Auth token validation as part of establishing (or rejecting) the connection (behaviour defined by `verifyToken`, `extractToken` and `requireAuth` args)
367
380
  - GraphQL context creation
368
381
  - Logging from the server `onConnect`, `onDisconnect`, `onOperation`, `onNext` and `onError` callbacks
369
382
 
@@ -400,6 +413,17 @@ This library includes a `subscriptions` module to provide simple setup using the
400
413
 
401
414
  1. For authorisation, clients can include a connection parameter named `authorization` or `Authorization` using the HTTP header format `Bearer <token>`. Note: [Apollo Sandbox](https://studio.apollographql.com/sandbox/explorer) will include an `Authorization` connection parameter when you specify an HTTP `Authorization` header via the UI.
402
415
 
416
+ If the token arrives another way, supply an `extractToken` function to both `useSubscriptionsServer` (which passes the token to `verifyToken`) and `createSubscriptionContextFactory` (which sets it as `User.token`). It receives the `connectRequest` (the HTTP upgrade request) and the `connectionParams`, and returns the token or `undefined`. For example, to read a JWT assertion header that a proxy adds to the upgrade request (browsers cannot set custom headers on a websocket request):
417
+
418
+ ```ts
419
+ const extractToken: ExtractSubscriptionToken = ({ connectRequest }) =>
420
+ connectRequest.headers['x-goog-iap-jwt-assertion'] as string | undefined
421
+
422
+ const createSubscriptionContext = createSubscriptionContextFactory({ requestLogger, extractToken })
423
+
424
+ useSubscriptionsServer({ schema, httpServer, logger, createSubscriptionContext, extractToken, requireAuth: true, verifyToken })
425
+ ```
426
+
403
427
  ### wrapSubscriptionIterator
404
428
 
405
429
  Wraps an `AsyncIterableIterator` (e.g. one returned by a pubsub `asyncIterableIterator(...)` call) with two optional behaviours:
package/context.d.cts CHANGED
@@ -51,12 +51,19 @@ export interface ContextInput {
51
51
  export type CreateContext<TContext = GraphQLContext> = (input: ContextInput) => Promise<TContext>;
52
52
  export type CreateRequestLogger<TUser = User | undefined, TLogger extends Logger = Logger> = (requestMetadata: Record<string, unknown>, user: TUser) => TLogger;
53
53
  export type AugmentRequestInfo = (input: ContextInput) => Record<string, unknown>;
54
+ export type ExtractToken = (input: ContextInput) => string | undefined;
54
55
  export type CreateContextConfig<TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger> = {
55
56
  requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger;
56
57
  augmentRequestInfo?: AugmentRequestInfo;
57
58
  claimsToLog?: string[];
58
59
  requestInfoToLog?: Array<RequestInfoLogKey>;
59
60
  augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>;
61
+ /**
62
+ * Returns the token that the default `createUser` sets as `User.token`, e.g. from a JWT assertion
63
+ * header. Not called when you supply `createUser`. Defaults to the bearer token in the
64
+ * `Authorization` header.
65
+ */
66
+ extractToken?: ExtractToken;
60
67
  } & ([User | undefined] extends [TUser] ? {
61
68
  createUser?: CreateUser<TUser>;
62
69
  } : {
package/context.d.mts CHANGED
@@ -51,12 +51,19 @@ export interface ContextInput {
51
51
  export type CreateContext<TContext = GraphQLContext> = (input: ContextInput) => Promise<TContext>;
52
52
  export type CreateRequestLogger<TUser = User | undefined, TLogger extends Logger = Logger> = (requestMetadata: Record<string, unknown>, user: TUser) => TLogger;
53
53
  export type AugmentRequestInfo = (input: ContextInput) => Record<string, unknown>;
54
+ export type ExtractToken = (input: ContextInput) => string | undefined;
54
55
  export type CreateContextConfig<TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger> = {
55
56
  requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger;
56
57
  augmentRequestInfo?: AugmentRequestInfo;
57
58
  claimsToLog?: string[];
58
59
  requestInfoToLog?: Array<RequestInfoLogKey>;
59
60
  augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>;
61
+ /**
62
+ * Returns the token that the default `createUser` sets as `User.token`, e.g. from a JWT assertion
63
+ * header. Not called when you supply `createUser`. Defaults to the bearer token in the
64
+ * `Authorization` header.
65
+ */
66
+ extractToken?: ExtractToken;
60
67
  } & ([User | undefined] extends [TUser] ? {
61
68
  createUser?: CreateUser<TUser>;
62
69
  } : {
package/context.d.ts CHANGED
@@ -51,12 +51,19 @@ export interface ContextInput {
51
51
  export type CreateContext<TContext = GraphQLContext> = (input: ContextInput) => Promise<TContext>;
52
52
  export type CreateRequestLogger<TUser = User | undefined, TLogger extends Logger = Logger> = (requestMetadata: Record<string, unknown>, user: TUser) => TLogger;
53
53
  export type AugmentRequestInfo = (input: ContextInput) => Record<string, unknown>;
54
+ export type ExtractToken = (input: ContextInput) => string | undefined;
54
55
  export type CreateContextConfig<TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger> = {
55
56
  requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger;
56
57
  augmentRequestInfo?: AugmentRequestInfo;
57
58
  claimsToLog?: string[];
58
59
  requestInfoToLog?: Array<RequestInfoLogKey>;
59
60
  augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>;
61
+ /**
62
+ * Returns the token that the default `createUser` sets as `User.token`, e.g. from a JWT assertion
63
+ * header. Not called when you supply `createUser`. Defaults to the bearer token in the
64
+ * `Authorization` header.
65
+ */
66
+ extractToken?: ExtractToken;
60
67
  } & ([User | undefined] extends [TUser] ? {
61
68
  createUser?: CreateUser<TUser>;
62
69
  } : {
package/context.js CHANGED
@@ -8,7 +8,7 @@ const createContextFactory = (config) => {
8
8
  const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config;
9
9
  // The conditional type on CreateContextConfig guarantees `createUser` is provided when TUser is
10
10
  // narrower than `User | undefined`, so defaulting to defaultCreateUser is sound here.
11
- const createUser = (config.createUser ?? defaultCreateUser);
11
+ const createUser = (config.createUser ?? buildDefaultCreateUser(config.extractToken ?? defaultExtractToken));
12
12
  return async (input) => {
13
13
  const { req, claims, context } = input;
14
14
  // build base request info from the request
@@ -57,12 +57,13 @@ const createContextFactory = (config) => {
57
57
  return augmentedGraphQLContext;
58
58
  };
59
59
  };
60
- const defaultCreateUser = ({ req, claims }) => {
61
- if (!claims)
60
+ const defaultExtractToken = ({ req }) => req.headers.authorization?.startsWith('Bearer') ? req.headers.authorization.substring(7) : undefined;
61
+ const buildDefaultCreateUser = (extractToken) => (input) => {
62
+ if (!input.claims)
62
63
  return Promise.resolve(undefined);
63
- const accessToken = req.headers.authorization?.startsWith('Bearer') ? (req.headers.authorization?.substring(7) ?? '') : '';
64
- return Promise.resolve(new User.User(claims, accessToken));
64
+ return Promise.resolve(new User.User(input.claims, extractToken(input) ?? ''));
65
65
  };
66
+ const defaultCreateUser = buildDefaultCreateUser(defaultExtractToken);
66
67
 
67
68
  exports.createContextFactory = createContextFactory;
68
69
  exports.defaultCreateUser = defaultCreateUser;
package/context.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sources":["../src/context.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { Request } from 'express'\nimport { buildBaseRequestInfo, type BaseRequestInfo } from './request-info'\nimport { User } from './User'\n\nexport interface GraphQLContext<\n TLogger extends Logger = Logger,\n TRequestInfo extends BaseRequestInfo = RequestInfo,\n TUser = User | undefined,\n> {\n logger: TLogger\n requestInfo: TRequestInfo\n user: TUser\n started: number\n}\n\nexport type AnyGraphqlContext = GraphQLContext<any, any, any>\n\nexport interface LambdaContext {\n functionName?: string\n awsRequestId?: string\n}\nexport type LambdaEvent = never\nexport type LambdaRequestInfo = BaseRequestInfo & LambdaContext\nexport type RequestInfo = BaseRequestInfo | LambdaRequestInfo\n\n// Strips the `[key: string]: unknown` index signature so `keyof` returns only the declared keys.\ntype KnownKeys<T> = keyof {\n [K in keyof T as string extends K ? never : number extends K ? never : K]: T[K]\n}\n// Known keys for autocomplete, plus `(string & {})` to keep arbitrary augmented fields assignable.\nexport type RequestInfoLogKey = KnownKeys<BaseRequestInfo> | keyof LambdaContext | (string & {})\n\n// standard claims https://datatracker.ietf.org/doc/html/rfc7519#section-4.1\nexport interface JwtPayload {\n [key: string]: unknown\n name?: string | undefined\n email?: string | undefined\n given_name?: string | undefined\n family_name?: string | undefined\n preferred_username?: string | undefined\n upn?: string | undefined\n unique_name?: string | undefined\n oid?: string | undefined\n iss?: string | undefined\n sub?: string | undefined\n aud?: string | string[] | undefined\n roles?: string[] | undefined\n scp?: string | undefined\n exp?: number | undefined\n nbf?: number | undefined\n iat?: number | undefined\n jti?: string | undefined\n}\n\nexport type InferUserFromContext<TContext extends AnyGraphqlContext> =\n TContext extends GraphQLContext<any, any, infer TUser> ? TUser : never\nexport type CreateUser<T = User | undefined> = (input: Omit<ContextInput, 'createUser'>) => Promise<T> | T\nexport interface ContextInput {\n req: Request\n claims?: JwtPayload\n context?: LambdaContext\n event?: LambdaEvent\n}\nexport type CreateContext<TContext = GraphQLContext> = (input: ContextInput) => Promise<TContext>\nexport type CreateRequestLogger<TUser = User | undefined, TLogger extends Logger = Logger> = (\n requestMetadata: Record<string, unknown>,\n user: TUser,\n) => TLogger\nexport type AugmentRequestInfo = (input: ContextInput) => Record<string, unknown>\n\n// `createUser` is optional when TUser is compatible with the default `User | undefined`\n// (i.e. `defaultCreateUser` can satisfy it), and required when TUser is narrower.\nexport type CreateContextConfig<\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n> = {\n requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger\n augmentRequestInfo?: AugmentRequestInfo\n claimsToLog?: string[]\n requestInfoToLog?: Array<RequestInfoLogKey>\n augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>\n} & ([User | undefined] extends [TUser] ? { createUser?: CreateUser<TUser> } : { createUser: CreateUser<TUser> })\n\nexport const createContextFactory = <\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n>(\n config: CreateContextConfig<TUser, TAugment, TLogger>,\n): CreateContext<GraphQLContext<TLogger, RequestInfo, TUser> & TAugment> => {\n const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config\n // The conditional type on CreateContextConfig guarantees `createUser` is provided when TUser is\n // narrower than `User | undefined`, so defaulting to defaultCreateUser is sound here.\n const createUser = (config.createUser ?? defaultCreateUser) as CreateUser<TUser>\n\n return async (input: ContextInput) => {\n const { req, claims, context } = input\n\n // build base request info from the request\n const baseRequestInfo: BaseRequestInfo = buildBaseRequestInfo(req)\n\n // add lambda info from the context, if present\n let lambdaRequestInfo: LambdaRequestInfo | undefined\n if (context) {\n const { awsRequestId, functionName } = context\n lambdaRequestInfo = {\n awsRequestId,\n functionName,\n ...baseRequestInfo,\n }\n }\n\n // augment request info with the supplied function, if present\n let requestInfo: RequestInfo = lambdaRequestInfo ?? baseRequestInfo\n if (augmentRequestInfo)\n requestInfo = {\n ...requestInfo,\n ...augmentRequestInfo(input),\n }\n\n const user = await createUser(input)\n\n // create request logger\n let logger: TLogger\n if (typeof requestLogger === 'function') {\n // build request logger metadata\n const requestLoggerMetadata: Record<string, unknown> = {}\n // add request info to log\n if (requestInfoToLog?.length) requestLoggerMetadata.request = pick(requestInfo, requestInfoToLog)\n // add user claims to log\n if (claims && claimsToLog?.length) requestLoggerMetadata.user = pick(claims, claimsToLog)\n // build the request logger\n logger = requestLogger(requestLoggerMetadata, user)\n } else logger = requestLogger\n\n const graphqlContext: GraphQLContext<TLogger, RequestInfo, TUser> = {\n requestInfo,\n logger,\n user,\n started: Date.now(),\n }\n\n const augmentedGraphQLContext = augmentContext ? { ...graphqlContext, ...(await augmentContext(graphqlContext)) } : graphqlContext\n\n return augmentedGraphQLContext as GraphQLContext<TLogger, RequestInfo, TUser> & TAugment\n }\n}\n\nexport const defaultCreateUser: CreateUser<User | undefined> = ({ req, claims }) => {\n if (!claims) return Promise.resolve(undefined)\n const accessToken = req.headers.authorization?.startsWith('Bearer') ? (req.headers.authorization?.substring(7) ?? '') : ''\n return Promise.resolve(new User(claims, accessToken))\n}\n"],"names":["buildBaseRequestInfo","requestInfo","pick","User"],"mappings":";;;;;;AAsFO,MAAM,oBAAoB,GAAG,CAKlC,MAAqD,KACoB;AACzE,IAAA,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,gBAAgB,EAAE,cAAc,EAAE,GAAG,MAAM;;;IAGnG,MAAM,UAAU,IAAI,MAAM,CAAC,UAAU,IAAI,iBAAiB,CAAsB;AAEhF,IAAA,OAAO,OAAO,KAAmB,KAAI;QACnC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK;;AAGtC,QAAA,MAAM,eAAe,GAAoBA,gCAAoB,CAAC,GAAG,CAAC;;AAGlE,QAAA,IAAI,iBAAgD;QACpD,IAAI,OAAO,EAAE;AACX,YAAA,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,OAAO;AAC9C,YAAA,iBAAiB,GAAG;gBAClB,YAAY;gBACZ,YAAY;AACZ,gBAAA,GAAG,eAAe;aACnB;QACH;;AAGA,QAAA,IAAIC,aAAW,GAAgB,iBAAiB,IAAI,eAAe;AACnE,QAAA,IAAI,kBAAkB;AACpB,YAAAA,aAAW,GAAG;AACZ,gBAAA,GAAGA,aAAW;gBACd,GAAG,kBAAkB,CAAC,KAAK,CAAC;aAC7B;AAEH,QAAA,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC;;AAGpC,QAAA,IAAI,MAAe;AACnB,QAAA,IAAI,OAAO,aAAa,KAAK,UAAU,EAAE;;YAEvC,MAAM,qBAAqB,GAA4B,EAAE;;YAEzD,IAAI,gBAAgB,EAAE,MAAM;gBAAE,qBAAqB,CAAC,OAAO,GAAGC,WAAI,CAACD,aAAW,EAAE,gBAAgB,CAAC;;AAEjG,YAAA,IAAI,MAAM,IAAI,WAAW,EAAE,MAAM;gBAAE,qBAAqB,CAAC,IAAI,GAAGC,WAAI,CAAC,MAAM,EAAE,WAAW,CAAC;;AAEzF,YAAA,MAAM,GAAG,aAAa,CAAC,qBAAqB,EAAE,IAAI,CAAC;QACrD;;YAAO,MAAM,GAAG,aAAa;AAE7B,QAAA,MAAM,cAAc,GAAgD;yBAClED,aAAW;YACX,MAAM;YACN,IAAI;AACJ,YAAA,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;SACpB;QAED,MAAM,uBAAuB,GAAG,cAAc,GAAG,EAAE,GAAG,cAAc,EAAE,IAAI,MAAM,cAAc,CAAC,cAAc,CAAC,CAAC,EAAE,GAAG,cAAc;AAElI,QAAA,OAAO,uBAAiF;AAC1F,IAAA,CAAC;AACH;AAEO,MAAM,iBAAiB,GAAiC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAI;AACjF,IAAA,IAAI,CAAC,MAAM;AAAE,QAAA,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;AAC9C,IAAA,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,UAAU,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE;AAC1H,IAAA,OAAO,OAAO,CAAC,OAAO,CAAC,IAAIE,SAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AACvD;;;;;"}
1
+ {"version":3,"file":"context.js","sources":["../src/context.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { Request } from 'express'\nimport { buildBaseRequestInfo, type BaseRequestInfo } from './request-info'\nimport { User } from './User'\n\nexport interface GraphQLContext<\n TLogger extends Logger = Logger,\n TRequestInfo extends BaseRequestInfo = RequestInfo,\n TUser = User | undefined,\n> {\n logger: TLogger\n requestInfo: TRequestInfo\n user: TUser\n started: number\n}\n\nexport type AnyGraphqlContext = GraphQLContext<any, any, any>\n\nexport interface LambdaContext {\n functionName?: string\n awsRequestId?: string\n}\nexport type LambdaEvent = never\nexport type LambdaRequestInfo = BaseRequestInfo & LambdaContext\nexport type RequestInfo = BaseRequestInfo | LambdaRequestInfo\n\n// Strips the `[key: string]: unknown` index signature so `keyof` returns only the declared keys.\ntype KnownKeys<T> = keyof {\n [K in keyof T as string extends K ? never : number extends K ? never : K]: T[K]\n}\n// Known keys for autocomplete, plus `(string & {})` to keep arbitrary augmented fields assignable.\nexport type RequestInfoLogKey = KnownKeys<BaseRequestInfo> | keyof LambdaContext | (string & {})\n\n// standard claims https://datatracker.ietf.org/doc/html/rfc7519#section-4.1\nexport interface JwtPayload {\n [key: string]: unknown\n name?: string | undefined\n email?: string | undefined\n given_name?: string | undefined\n family_name?: string | undefined\n preferred_username?: string | undefined\n upn?: string | undefined\n unique_name?: string | undefined\n oid?: string | undefined\n iss?: string | undefined\n sub?: string | undefined\n aud?: string | string[] | undefined\n roles?: string[] | undefined\n scp?: string | undefined\n exp?: number | undefined\n nbf?: number | undefined\n iat?: number | undefined\n jti?: string | undefined\n}\n\nexport type InferUserFromContext<TContext extends AnyGraphqlContext> =\n TContext extends GraphQLContext<any, any, infer TUser> ? TUser : never\nexport type CreateUser<T = User | undefined> = (input: Omit<ContextInput, 'createUser'>) => Promise<T> | T\nexport interface ContextInput {\n req: Request\n claims?: JwtPayload\n context?: LambdaContext\n event?: LambdaEvent\n}\nexport type CreateContext<TContext = GraphQLContext> = (input: ContextInput) => Promise<TContext>\nexport type CreateRequestLogger<TUser = User | undefined, TLogger extends Logger = Logger> = (\n requestMetadata: Record<string, unknown>,\n user: TUser,\n) => TLogger\nexport type AugmentRequestInfo = (input: ContextInput) => Record<string, unknown>\nexport type ExtractToken = (input: ContextInput) => string | undefined\n\n// `createUser` is optional when TUser is compatible with the default `User | undefined`\n// (i.e. `defaultCreateUser` can satisfy it), and required when TUser is narrower.\nexport type CreateContextConfig<\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n> = {\n requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger\n augmentRequestInfo?: AugmentRequestInfo\n claimsToLog?: string[]\n requestInfoToLog?: Array<RequestInfoLogKey>\n augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>\n /**\n * Returns the token that the default `createUser` sets as `User.token`, e.g. from a JWT assertion\n * header. Not called when you supply `createUser`. Defaults to the bearer token in the\n * `Authorization` header.\n */\n extractToken?: ExtractToken\n} & ([User | undefined] extends [TUser] ? { createUser?: CreateUser<TUser> } : { createUser: CreateUser<TUser> })\n\nexport const createContextFactory = <\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n>(\n config: CreateContextConfig<TUser, TAugment, TLogger>,\n): CreateContext<GraphQLContext<TLogger, RequestInfo, TUser> & TAugment> => {\n const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config\n // The conditional type on CreateContextConfig guarantees `createUser` is provided when TUser is\n // narrower than `User | undefined`, so defaulting to defaultCreateUser is sound here.\n const createUser = (config.createUser ?? buildDefaultCreateUser(config.extractToken ?? defaultExtractToken)) as CreateUser<TUser>\n\n return async (input: ContextInput) => {\n const { req, claims, context } = input\n\n // build base request info from the request\n const baseRequestInfo: BaseRequestInfo = buildBaseRequestInfo(req)\n\n // add lambda info from the context, if present\n let lambdaRequestInfo: LambdaRequestInfo | undefined\n if (context) {\n const { awsRequestId, functionName } = context\n lambdaRequestInfo = {\n awsRequestId,\n functionName,\n ...baseRequestInfo,\n }\n }\n\n // augment request info with the supplied function, if present\n let requestInfo: RequestInfo = lambdaRequestInfo ?? baseRequestInfo\n if (augmentRequestInfo)\n requestInfo = {\n ...requestInfo,\n ...augmentRequestInfo(input),\n }\n\n const user = await createUser(input)\n\n // create request logger\n let logger: TLogger\n if (typeof requestLogger === 'function') {\n // build request logger metadata\n const requestLoggerMetadata: Record<string, unknown> = {}\n // add request info to log\n if (requestInfoToLog?.length) requestLoggerMetadata.request = pick(requestInfo, requestInfoToLog)\n // add user claims to log\n if (claims && claimsToLog?.length) requestLoggerMetadata.user = pick(claims, claimsToLog)\n // build the request logger\n logger = requestLogger(requestLoggerMetadata, user)\n } else logger = requestLogger\n\n const graphqlContext: GraphQLContext<TLogger, RequestInfo, TUser> = {\n requestInfo,\n logger,\n user,\n started: Date.now(),\n }\n\n const augmentedGraphQLContext = augmentContext ? { ...graphqlContext, ...(await augmentContext(graphqlContext)) } : graphqlContext\n\n return augmentedGraphQLContext as GraphQLContext<TLogger, RequestInfo, TUser> & TAugment\n }\n}\n\nconst defaultExtractToken: ExtractToken = ({ req }) =>\n req.headers.authorization?.startsWith('Bearer') ? req.headers.authorization.substring(7) : undefined\n\nconst buildDefaultCreateUser =\n (extractToken: ExtractToken): CreateUser<User | undefined> =>\n (input) => {\n if (!input.claims) return Promise.resolve(undefined)\n return Promise.resolve(new User(input.claims, extractToken(input) ?? ''))\n }\n\nexport const defaultCreateUser = buildDefaultCreateUser(defaultExtractToken)\n"],"names":["buildBaseRequestInfo","requestInfo","pick","User"],"mappings":";;;;;;AA6FO,MAAM,oBAAoB,GAAG,CAKlC,MAAqD,KACoB;AACzE,IAAA,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,gBAAgB,EAAE,cAAc,EAAE,GAAG,MAAM;;;AAGnG,IAAA,MAAM,UAAU,IAAI,MAAM,CAAC,UAAU,IAAI,sBAAsB,CAAC,MAAM,CAAC,YAAY,IAAI,mBAAmB,CAAC,CAAsB;AAEjI,IAAA,OAAO,OAAO,KAAmB,KAAI;QACnC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK;;AAGtC,QAAA,MAAM,eAAe,GAAoBA,gCAAoB,CAAC,GAAG,CAAC;;AAGlE,QAAA,IAAI,iBAAgD;QACpD,IAAI,OAAO,EAAE;AACX,YAAA,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,OAAO;AAC9C,YAAA,iBAAiB,GAAG;gBAClB,YAAY;gBACZ,YAAY;AACZ,gBAAA,GAAG,eAAe;aACnB;QACH;;AAGA,QAAA,IAAIC,aAAW,GAAgB,iBAAiB,IAAI,eAAe;AACnE,QAAA,IAAI,kBAAkB;AACpB,YAAAA,aAAW,GAAG;AACZ,gBAAA,GAAGA,aAAW;gBACd,GAAG,kBAAkB,CAAC,KAAK,CAAC;aAC7B;AAEH,QAAA,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC;;AAGpC,QAAA,IAAI,MAAe;AACnB,QAAA,IAAI,OAAO,aAAa,KAAK,UAAU,EAAE;;YAEvC,MAAM,qBAAqB,GAA4B,EAAE;;YAEzD,IAAI,gBAAgB,EAAE,MAAM;gBAAE,qBAAqB,CAAC,OAAO,GAAGC,WAAI,CAACD,aAAW,EAAE,gBAAgB,CAAC;;AAEjG,YAAA,IAAI,MAAM,IAAI,WAAW,EAAE,MAAM;gBAAE,qBAAqB,CAAC,IAAI,GAAGC,WAAI,CAAC,MAAM,EAAE,WAAW,CAAC;;AAEzF,YAAA,MAAM,GAAG,aAAa,CAAC,qBAAqB,EAAE,IAAI,CAAC;QACrD;;YAAO,MAAM,GAAG,aAAa;AAE7B,QAAA,MAAM,cAAc,GAAgD;yBAClED,aAAW;YACX,MAAM;YACN,IAAI;AACJ,YAAA,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;SACpB;QAED,MAAM,uBAAuB,GAAG,cAAc,GAAG,EAAE,GAAG,cAAc,EAAE,IAAI,MAAM,cAAc,CAAC,cAAc,CAAC,CAAC,EAAE,GAAG,cAAc;AAElI,QAAA,OAAO,uBAAiF;AAC1F,IAAA,CAAC;AACH;AAEA,MAAM,mBAAmB,GAAiB,CAAC,EAAE,GAAG,EAAE,KAChD,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS;AAEtG,MAAM,sBAAsB,GAC1B,CAAC,YAA0B,KAC3B,CAAC,KAAK,KAAI;IACR,IAAI,CAAC,KAAK,CAAC,MAAM;AAAE,QAAA,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;AACpD,IAAA,OAAO,OAAO,CAAC,OAAO,CAAC,IAAIE,SAAI,CAAC,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AAC3E,CAAC;MAEU,iBAAiB,GAAG,sBAAsB,CAAC,mBAAmB;;;;;"}
package/context.mjs CHANGED
@@ -6,7 +6,7 @@ const createContextFactory = (config) => {
6
6
  const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config;
7
7
  // The conditional type on CreateContextConfig guarantees `createUser` is provided when TUser is
8
8
  // narrower than `User | undefined`, so defaulting to defaultCreateUser is sound here.
9
- const createUser = (config.createUser ?? defaultCreateUser);
9
+ const createUser = (config.createUser ?? buildDefaultCreateUser(config.extractToken ?? defaultExtractToken));
10
10
  return async (input) => {
11
11
  const { req, claims, context } = input;
12
12
  // build base request info from the request
@@ -55,12 +55,13 @@ const createContextFactory = (config) => {
55
55
  return augmentedGraphQLContext;
56
56
  };
57
57
  };
58
- const defaultCreateUser = ({ req, claims }) => {
59
- if (!claims)
58
+ const defaultExtractToken = ({ req }) => req.headers.authorization?.startsWith('Bearer') ? req.headers.authorization.substring(7) : undefined;
59
+ const buildDefaultCreateUser = (extractToken) => (input) => {
60
+ if (!input.claims)
60
61
  return Promise.resolve(undefined);
61
- const accessToken = req.headers.authorization?.startsWith('Bearer') ? (req.headers.authorization?.substring(7) ?? '') : '';
62
- return Promise.resolve(new User(claims, accessToken));
62
+ return Promise.resolve(new User(input.claims, extractToken(input) ?? ''));
63
63
  };
64
+ const defaultCreateUser = buildDefaultCreateUser(defaultExtractToken);
64
65
 
65
66
  export { createContextFactory, defaultCreateUser };
66
67
  //# sourceMappingURL=context.mjs.map
package/context.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"context.mjs","sources":["../src/context.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { Request } from 'express'\nimport { buildBaseRequestInfo, type BaseRequestInfo } from './request-info'\nimport { User } from './User'\n\nexport interface GraphQLContext<\n TLogger extends Logger = Logger,\n TRequestInfo extends BaseRequestInfo = RequestInfo,\n TUser = User | undefined,\n> {\n logger: TLogger\n requestInfo: TRequestInfo\n user: TUser\n started: number\n}\n\nexport type AnyGraphqlContext = GraphQLContext<any, any, any>\n\nexport interface LambdaContext {\n functionName?: string\n awsRequestId?: string\n}\nexport type LambdaEvent = never\nexport type LambdaRequestInfo = BaseRequestInfo & LambdaContext\nexport type RequestInfo = BaseRequestInfo | LambdaRequestInfo\n\n// Strips the `[key: string]: unknown` index signature so `keyof` returns only the declared keys.\ntype KnownKeys<T> = keyof {\n [K in keyof T as string extends K ? never : number extends K ? never : K]: T[K]\n}\n// Known keys for autocomplete, plus `(string & {})` to keep arbitrary augmented fields assignable.\nexport type RequestInfoLogKey = KnownKeys<BaseRequestInfo> | keyof LambdaContext | (string & {})\n\n// standard claims https://datatracker.ietf.org/doc/html/rfc7519#section-4.1\nexport interface JwtPayload {\n [key: string]: unknown\n name?: string | undefined\n email?: string | undefined\n given_name?: string | undefined\n family_name?: string | undefined\n preferred_username?: string | undefined\n upn?: string | undefined\n unique_name?: string | undefined\n oid?: string | undefined\n iss?: string | undefined\n sub?: string | undefined\n aud?: string | string[] | undefined\n roles?: string[] | undefined\n scp?: string | undefined\n exp?: number | undefined\n nbf?: number | undefined\n iat?: number | undefined\n jti?: string | undefined\n}\n\nexport type InferUserFromContext<TContext extends AnyGraphqlContext> =\n TContext extends GraphQLContext<any, any, infer TUser> ? TUser : never\nexport type CreateUser<T = User | undefined> = (input: Omit<ContextInput, 'createUser'>) => Promise<T> | T\nexport interface ContextInput {\n req: Request\n claims?: JwtPayload\n context?: LambdaContext\n event?: LambdaEvent\n}\nexport type CreateContext<TContext = GraphQLContext> = (input: ContextInput) => Promise<TContext>\nexport type CreateRequestLogger<TUser = User | undefined, TLogger extends Logger = Logger> = (\n requestMetadata: Record<string, unknown>,\n user: TUser,\n) => TLogger\nexport type AugmentRequestInfo = (input: ContextInput) => Record<string, unknown>\n\n// `createUser` is optional when TUser is compatible with the default `User | undefined`\n// (i.e. `defaultCreateUser` can satisfy it), and required when TUser is narrower.\nexport type CreateContextConfig<\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n> = {\n requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger\n augmentRequestInfo?: AugmentRequestInfo\n claimsToLog?: string[]\n requestInfoToLog?: Array<RequestInfoLogKey>\n augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>\n} & ([User | undefined] extends [TUser] ? { createUser?: CreateUser<TUser> } : { createUser: CreateUser<TUser> })\n\nexport const createContextFactory = <\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n>(\n config: CreateContextConfig<TUser, TAugment, TLogger>,\n): CreateContext<GraphQLContext<TLogger, RequestInfo, TUser> & TAugment> => {\n const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config\n // The conditional type on CreateContextConfig guarantees `createUser` is provided when TUser is\n // narrower than `User | undefined`, so defaulting to defaultCreateUser is sound here.\n const createUser = (config.createUser ?? defaultCreateUser) as CreateUser<TUser>\n\n return async (input: ContextInput) => {\n const { req, claims, context } = input\n\n // build base request info from the request\n const baseRequestInfo: BaseRequestInfo = buildBaseRequestInfo(req)\n\n // add lambda info from the context, if present\n let lambdaRequestInfo: LambdaRequestInfo | undefined\n if (context) {\n const { awsRequestId, functionName } = context\n lambdaRequestInfo = {\n awsRequestId,\n functionName,\n ...baseRequestInfo,\n }\n }\n\n // augment request info with the supplied function, if present\n let requestInfo: RequestInfo = lambdaRequestInfo ?? baseRequestInfo\n if (augmentRequestInfo)\n requestInfo = {\n ...requestInfo,\n ...augmentRequestInfo(input),\n }\n\n const user = await createUser(input)\n\n // create request logger\n let logger: TLogger\n if (typeof requestLogger === 'function') {\n // build request logger metadata\n const requestLoggerMetadata: Record<string, unknown> = {}\n // add request info to log\n if (requestInfoToLog?.length) requestLoggerMetadata.request = pick(requestInfo, requestInfoToLog)\n // add user claims to log\n if (claims && claimsToLog?.length) requestLoggerMetadata.user = pick(claims, claimsToLog)\n // build the request logger\n logger = requestLogger(requestLoggerMetadata, user)\n } else logger = requestLogger\n\n const graphqlContext: GraphQLContext<TLogger, RequestInfo, TUser> = {\n requestInfo,\n logger,\n user,\n started: Date.now(),\n }\n\n const augmentedGraphQLContext = augmentContext ? { ...graphqlContext, ...(await augmentContext(graphqlContext)) } : graphqlContext\n\n return augmentedGraphQLContext as GraphQLContext<TLogger, RequestInfo, TUser> & TAugment\n }\n}\n\nexport const defaultCreateUser: CreateUser<User | undefined> = ({ req, claims }) => {\n if (!claims) return Promise.resolve(undefined)\n const accessToken = req.headers.authorization?.startsWith('Bearer') ? (req.headers.authorization?.substring(7) ?? '') : ''\n return Promise.resolve(new User(claims, accessToken))\n}\n"],"names":[],"mappings":";;;;AAsFO,MAAM,oBAAoB,GAAG,CAKlC,MAAqD,KACoB;AACzE,IAAA,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,gBAAgB,EAAE,cAAc,EAAE,GAAG,MAAM;;;IAGnG,MAAM,UAAU,IAAI,MAAM,CAAC,UAAU,IAAI,iBAAiB,CAAsB;AAEhF,IAAA,OAAO,OAAO,KAAmB,KAAI;QACnC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK;;AAGtC,QAAA,MAAM,eAAe,GAAoB,oBAAoB,CAAC,GAAG,CAAC;;AAGlE,QAAA,IAAI,iBAAgD;QACpD,IAAI,OAAO,EAAE;AACX,YAAA,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,OAAO;AAC9C,YAAA,iBAAiB,GAAG;gBAClB,YAAY;gBACZ,YAAY;AACZ,gBAAA,GAAG,eAAe;aACnB;QACH;;AAGA,QAAA,IAAI,WAAW,GAAgB,iBAAiB,IAAI,eAAe;AACnE,QAAA,IAAI,kBAAkB;AACpB,YAAA,WAAW,GAAG;AACZ,gBAAA,GAAG,WAAW;gBACd,GAAG,kBAAkB,CAAC,KAAK,CAAC;aAC7B;AAEH,QAAA,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC;;AAGpC,QAAA,IAAI,MAAe;AACnB,QAAA,IAAI,OAAO,aAAa,KAAK,UAAU,EAAE;;YAEvC,MAAM,qBAAqB,GAA4B,EAAE;;YAEzD,IAAI,gBAAgB,EAAE,MAAM;gBAAE,qBAAqB,CAAC,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,gBAAgB,CAAC;;AAEjG,YAAA,IAAI,MAAM,IAAI,WAAW,EAAE,MAAM;gBAAE,qBAAqB,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;;AAEzF,YAAA,MAAM,GAAG,aAAa,CAAC,qBAAqB,EAAE,IAAI,CAAC;QACrD;;YAAO,MAAM,GAAG,aAAa;AAE7B,QAAA,MAAM,cAAc,GAAgD;YAClE,WAAW;YACX,MAAM;YACN,IAAI;AACJ,YAAA,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;SACpB;QAED,MAAM,uBAAuB,GAAG,cAAc,GAAG,EAAE,GAAG,cAAc,EAAE,IAAI,MAAM,cAAc,CAAC,cAAc,CAAC,CAAC,EAAE,GAAG,cAAc;AAElI,QAAA,OAAO,uBAAiF;AAC1F,IAAA,CAAC;AACH;AAEO,MAAM,iBAAiB,GAAiC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAI;AACjF,IAAA,IAAI,CAAC,MAAM;AAAE,QAAA,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;AAC9C,IAAA,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,UAAU,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE;AAC1H,IAAA,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AACvD;;;;"}
1
+ {"version":3,"file":"context.mjs","sources":["../src/context.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { Request } from 'express'\nimport { buildBaseRequestInfo, type BaseRequestInfo } from './request-info'\nimport { User } from './User'\n\nexport interface GraphQLContext<\n TLogger extends Logger = Logger,\n TRequestInfo extends BaseRequestInfo = RequestInfo,\n TUser = User | undefined,\n> {\n logger: TLogger\n requestInfo: TRequestInfo\n user: TUser\n started: number\n}\n\nexport type AnyGraphqlContext = GraphQLContext<any, any, any>\n\nexport interface LambdaContext {\n functionName?: string\n awsRequestId?: string\n}\nexport type LambdaEvent = never\nexport type LambdaRequestInfo = BaseRequestInfo & LambdaContext\nexport type RequestInfo = BaseRequestInfo | LambdaRequestInfo\n\n// Strips the `[key: string]: unknown` index signature so `keyof` returns only the declared keys.\ntype KnownKeys<T> = keyof {\n [K in keyof T as string extends K ? never : number extends K ? never : K]: T[K]\n}\n// Known keys for autocomplete, plus `(string & {})` to keep arbitrary augmented fields assignable.\nexport type RequestInfoLogKey = KnownKeys<BaseRequestInfo> | keyof LambdaContext | (string & {})\n\n// standard claims https://datatracker.ietf.org/doc/html/rfc7519#section-4.1\nexport interface JwtPayload {\n [key: string]: unknown\n name?: string | undefined\n email?: string | undefined\n given_name?: string | undefined\n family_name?: string | undefined\n preferred_username?: string | undefined\n upn?: string | undefined\n unique_name?: string | undefined\n oid?: string | undefined\n iss?: string | undefined\n sub?: string | undefined\n aud?: string | string[] | undefined\n roles?: string[] | undefined\n scp?: string | undefined\n exp?: number | undefined\n nbf?: number | undefined\n iat?: number | undefined\n jti?: string | undefined\n}\n\nexport type InferUserFromContext<TContext extends AnyGraphqlContext> =\n TContext extends GraphQLContext<any, any, infer TUser> ? TUser : never\nexport type CreateUser<T = User | undefined> = (input: Omit<ContextInput, 'createUser'>) => Promise<T> | T\nexport interface ContextInput {\n req: Request\n claims?: JwtPayload\n context?: LambdaContext\n event?: LambdaEvent\n}\nexport type CreateContext<TContext = GraphQLContext> = (input: ContextInput) => Promise<TContext>\nexport type CreateRequestLogger<TUser = User | undefined, TLogger extends Logger = Logger> = (\n requestMetadata: Record<string, unknown>,\n user: TUser,\n) => TLogger\nexport type AugmentRequestInfo = (input: ContextInput) => Record<string, unknown>\nexport type ExtractToken = (input: ContextInput) => string | undefined\n\n// `createUser` is optional when TUser is compatible with the default `User | undefined`\n// (i.e. `defaultCreateUser` can satisfy it), and required when TUser is narrower.\nexport type CreateContextConfig<\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n> = {\n requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger\n augmentRequestInfo?: AugmentRequestInfo\n claimsToLog?: string[]\n requestInfoToLog?: Array<RequestInfoLogKey>\n augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>\n /**\n * Returns the token that the default `createUser` sets as `User.token`, e.g. from a JWT assertion\n * header. Not called when you supply `createUser`. Defaults to the bearer token in the\n * `Authorization` header.\n */\n extractToken?: ExtractToken\n} & ([User | undefined] extends [TUser] ? { createUser?: CreateUser<TUser> } : { createUser: CreateUser<TUser> })\n\nexport const createContextFactory = <\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n>(\n config: CreateContextConfig<TUser, TAugment, TLogger>,\n): CreateContext<GraphQLContext<TLogger, RequestInfo, TUser> & TAugment> => {\n const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config\n // The conditional type on CreateContextConfig guarantees `createUser` is provided when TUser is\n // narrower than `User | undefined`, so defaulting to defaultCreateUser is sound here.\n const createUser = (config.createUser ?? buildDefaultCreateUser(config.extractToken ?? defaultExtractToken)) as CreateUser<TUser>\n\n return async (input: ContextInput) => {\n const { req, claims, context } = input\n\n // build base request info from the request\n const baseRequestInfo: BaseRequestInfo = buildBaseRequestInfo(req)\n\n // add lambda info from the context, if present\n let lambdaRequestInfo: LambdaRequestInfo | undefined\n if (context) {\n const { awsRequestId, functionName } = context\n lambdaRequestInfo = {\n awsRequestId,\n functionName,\n ...baseRequestInfo,\n }\n }\n\n // augment request info with the supplied function, if present\n let requestInfo: RequestInfo = lambdaRequestInfo ?? baseRequestInfo\n if (augmentRequestInfo)\n requestInfo = {\n ...requestInfo,\n ...augmentRequestInfo(input),\n }\n\n const user = await createUser(input)\n\n // create request logger\n let logger: TLogger\n if (typeof requestLogger === 'function') {\n // build request logger metadata\n const requestLoggerMetadata: Record<string, unknown> = {}\n // add request info to log\n if (requestInfoToLog?.length) requestLoggerMetadata.request = pick(requestInfo, requestInfoToLog)\n // add user claims to log\n if (claims && claimsToLog?.length) requestLoggerMetadata.user = pick(claims, claimsToLog)\n // build the request logger\n logger = requestLogger(requestLoggerMetadata, user)\n } else logger = requestLogger\n\n const graphqlContext: GraphQLContext<TLogger, RequestInfo, TUser> = {\n requestInfo,\n logger,\n user,\n started: Date.now(),\n }\n\n const augmentedGraphQLContext = augmentContext ? { ...graphqlContext, ...(await augmentContext(graphqlContext)) } : graphqlContext\n\n return augmentedGraphQLContext as GraphQLContext<TLogger, RequestInfo, TUser> & TAugment\n }\n}\n\nconst defaultExtractToken: ExtractToken = ({ req }) =>\n req.headers.authorization?.startsWith('Bearer') ? req.headers.authorization.substring(7) : undefined\n\nconst buildDefaultCreateUser =\n (extractToken: ExtractToken): CreateUser<User | undefined> =>\n (input) => {\n if (!input.claims) return Promise.resolve(undefined)\n return Promise.resolve(new User(input.claims, extractToken(input) ?? ''))\n }\n\nexport const defaultCreateUser = buildDefaultCreateUser(defaultExtractToken)\n"],"names":[],"mappings":";;;;AA6FO,MAAM,oBAAoB,GAAG,CAKlC,MAAqD,KACoB;AACzE,IAAA,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,gBAAgB,EAAE,cAAc,EAAE,GAAG,MAAM;;;AAGnG,IAAA,MAAM,UAAU,IAAI,MAAM,CAAC,UAAU,IAAI,sBAAsB,CAAC,MAAM,CAAC,YAAY,IAAI,mBAAmB,CAAC,CAAsB;AAEjI,IAAA,OAAO,OAAO,KAAmB,KAAI;QACnC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK;;AAGtC,QAAA,MAAM,eAAe,GAAoB,oBAAoB,CAAC,GAAG,CAAC;;AAGlE,QAAA,IAAI,iBAAgD;QACpD,IAAI,OAAO,EAAE;AACX,YAAA,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,OAAO;AAC9C,YAAA,iBAAiB,GAAG;gBAClB,YAAY;gBACZ,YAAY;AACZ,gBAAA,GAAG,eAAe;aACnB;QACH;;AAGA,QAAA,IAAI,WAAW,GAAgB,iBAAiB,IAAI,eAAe;AACnE,QAAA,IAAI,kBAAkB;AACpB,YAAA,WAAW,GAAG;AACZ,gBAAA,GAAG,WAAW;gBACd,GAAG,kBAAkB,CAAC,KAAK,CAAC;aAC7B;AAEH,QAAA,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC;;AAGpC,QAAA,IAAI,MAAe;AACnB,QAAA,IAAI,OAAO,aAAa,KAAK,UAAU,EAAE;;YAEvC,MAAM,qBAAqB,GAA4B,EAAE;;YAEzD,IAAI,gBAAgB,EAAE,MAAM;gBAAE,qBAAqB,CAAC,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,gBAAgB,CAAC;;AAEjG,YAAA,IAAI,MAAM,IAAI,WAAW,EAAE,MAAM;gBAAE,qBAAqB,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;;AAEzF,YAAA,MAAM,GAAG,aAAa,CAAC,qBAAqB,EAAE,IAAI,CAAC;QACrD;;YAAO,MAAM,GAAG,aAAa;AAE7B,QAAA,MAAM,cAAc,GAAgD;YAClE,WAAW;YACX,MAAM;YACN,IAAI;AACJ,YAAA,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;SACpB;QAED,MAAM,uBAAuB,GAAG,cAAc,GAAG,EAAE,GAAG,cAAc,EAAE,IAAI,MAAM,cAAc,CAAC,cAAc,CAAC,CAAC,EAAE,GAAG,cAAc;AAElI,QAAA,OAAO,uBAAiF;AAC1F,IAAA,CAAC;AACH;AAEA,MAAM,mBAAmB,GAAiB,CAAC,EAAE,GAAG,EAAE,KAChD,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS;AAEtG,MAAM,sBAAsB,GAC1B,CAAC,YAA0B,KAC3B,CAAC,KAAK,KAAI;IACR,IAAI,CAAC,KAAK,CAAC,MAAM;AAAE,QAAA,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;AACpD,IAAA,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AAC3E,CAAC;MAEU,iBAAiB,GAAG,sBAAsB,CAAC,mBAAmB;;;;"}
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "**"
5
5
  ],
6
6
  "name": "@makerx/graphql-core",
7
- "version": "3.3.0",
7
+ "version": "3.4.0",
8
8
  "private": false,
9
9
  "description": "A set of core GraphQL utilities that MakerX uses to build GraphQL APIs",
10
10
  "author": "MakerX",
@@ -10,12 +10,19 @@ export interface SubscriptionContextInput {
10
10
  export type CreateSubscriptionUser<T = User | undefined> = (input: SubscriptionContextInput) => Promise<T> | T;
11
11
  export type CreateSubscriptionContext<TContext = GraphQLContext> = (input: SubscriptionContextInput) => Promise<TContext>;
12
12
  export type AugmentSubscriptionRequestInfo = (input: SubscriptionContextInput) => Record<string, unknown>;
13
+ export type ExtractSubscriptionToken = (input: Omit<SubscriptionContextInput, 'claims'>) => string | undefined;
13
14
  export type CreateSubscriptionContextConfig<TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger> = {
14
15
  requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger;
15
16
  augmentRequestInfo?: AugmentSubscriptionRequestInfo;
16
17
  claimsToLog?: string[];
17
18
  requestInfoToLog?: Array<RequestInfoLogKey>;
18
19
  augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>;
20
+ /**
21
+ * Returns the token that the default `createUser` sets as `User.token`. Use the same function as
22
+ * the `useSubscriptionsServer` `extractToken` option. Not called when you supply `createUser`.
23
+ * Defaults to the bearer token in the `authorization` or `Authorization` connection parameter.
24
+ */
25
+ extractToken?: ExtractSubscriptionToken;
19
26
  } & ([User | undefined] extends [TUser] ? {
20
27
  createUser?: CreateSubscriptionUser<TUser>;
21
28
  } : {
@@ -10,12 +10,19 @@ export interface SubscriptionContextInput {
10
10
  export type CreateSubscriptionUser<T = User | undefined> = (input: SubscriptionContextInput) => Promise<T> | T;
11
11
  export type CreateSubscriptionContext<TContext = GraphQLContext> = (input: SubscriptionContextInput) => Promise<TContext>;
12
12
  export type AugmentSubscriptionRequestInfo = (input: SubscriptionContextInput) => Record<string, unknown>;
13
+ export type ExtractSubscriptionToken = (input: Omit<SubscriptionContextInput, 'claims'>) => string | undefined;
13
14
  export type CreateSubscriptionContextConfig<TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger> = {
14
15
  requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger;
15
16
  augmentRequestInfo?: AugmentSubscriptionRequestInfo;
16
17
  claimsToLog?: string[];
17
18
  requestInfoToLog?: Array<RequestInfoLogKey>;
18
19
  augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>;
20
+ /**
21
+ * Returns the token that the default `createUser` sets as `User.token`. Use the same function as
22
+ * the `useSubscriptionsServer` `extractToken` option. Not called when you supply `createUser`.
23
+ * Defaults to the bearer token in the `authorization` or `Authorization` connection parameter.
24
+ */
25
+ extractToken?: ExtractSubscriptionToken;
19
26
  } & ([User | undefined] extends [TUser] ? {
20
27
  createUser?: CreateSubscriptionUser<TUser>;
21
28
  } : {
@@ -10,12 +10,19 @@ export interface SubscriptionContextInput {
10
10
  export type CreateSubscriptionUser<T = User | undefined> = (input: SubscriptionContextInput) => Promise<T> | T;
11
11
  export type CreateSubscriptionContext<TContext = GraphQLContext> = (input: SubscriptionContextInput) => Promise<TContext>;
12
12
  export type AugmentSubscriptionRequestInfo = (input: SubscriptionContextInput) => Record<string, unknown>;
13
+ export type ExtractSubscriptionToken = (input: Omit<SubscriptionContextInput, 'claims'>) => string | undefined;
13
14
  export type CreateSubscriptionContextConfig<TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger> = {
14
15
  requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger;
15
16
  augmentRequestInfo?: AugmentSubscriptionRequestInfo;
16
17
  claimsToLog?: string[];
17
18
  requestInfoToLog?: Array<RequestInfoLogKey>;
18
19
  augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>;
20
+ /**
21
+ * Returns the token that the default `createUser` sets as `User.token`. Use the same function as
22
+ * the `useSubscriptionsServer` `extractToken` option. Not called when you supply `createUser`.
23
+ * Defaults to the bearer token in the `authorization` or `Authorization` connection parameter.
24
+ */
25
+ extractToken?: ExtractSubscriptionToken;
19
26
  } & ([User | undefined] extends [TUser] ? {
20
27
  createUser?: CreateSubscriptionUser<TUser>;
21
28
  } : {
@@ -9,7 +9,8 @@ const createSubscriptionContextFactory = (config) => {
9
9
  const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config;
10
10
  // The conditional type on CreateSubscriptionContextConfig guarantees `createUser` is provided
11
11
  // when TUser is narrower than `User | undefined`, so defaulting is sound here.
12
- const createUser = (config.createUser ?? defaultCreateUser);
12
+ const createUser = (config.createUser ??
13
+ buildDefaultCreateUser(config.extractToken ?? utils.defaultExtractSubscriptionToken));
13
14
  return async (input) => {
14
15
  const { connectRequest: req, claims } = input;
15
16
  // build request info from the connect request and socket
@@ -44,11 +45,10 @@ const createSubscriptionContextFactory = (config) => {
44
45
  return augmentedGraphQLContext;
45
46
  };
46
47
  };
47
- const defaultCreateUser = ({ claims, connectionParams }) => {
48
- if (!claims)
48
+ const buildDefaultCreateUser = (extractToken) => (input) => {
49
+ if (!input.claims)
49
50
  return Promise.resolve(undefined);
50
- const accessToken = utils.extractTokenFromConnectionParams(connectionParams);
51
- return Promise.resolve(new User.User(claims, accessToken ?? ''));
51
+ return Promise.resolve(new User.User(input.claims, extractToken(input) ?? ''));
52
52
  };
53
53
 
54
54
  exports.createSubscriptionContextFactory = createSubscriptionContextFactory;
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sources":["../../src/subscriptions/context.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { IncomingMessage } from 'http'\nimport { User } from '../User'\nimport type { CreateRequestLogger, GraphQLContext, JwtPayload, RequestInfo, RequestInfoLogKey } from '../context'\nimport { buildConnectRequestInfo } from '../request-info'\nimport { extractTokenFromConnectionParams } from './utils'\n\nexport interface SubscriptionContextInput {\n connectRequest: IncomingMessage\n connectionParams?: Readonly<Record<string, unknown>>\n claims?: JwtPayload\n}\n\nexport type CreateSubscriptionUser<T = User | undefined> = (input: SubscriptionContextInput) => Promise<T> | T\nexport type CreateSubscriptionContext<TContext = GraphQLContext> = (input: SubscriptionContextInput) => Promise<TContext>\nexport type AugmentSubscriptionRequestInfo = (input: SubscriptionContextInput) => Record<string, unknown>\n\n// `createUser` is optional when TUser is compatible with the default `User | undefined`\n// (i.e. defaultCreateUser can satisfy it), and required when TUser is narrower.\nexport type CreateSubscriptionContextConfig<\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n> = {\n requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger\n augmentRequestInfo?: AugmentSubscriptionRequestInfo\n claimsToLog?: string[]\n requestInfoToLog?: Array<RequestInfoLogKey>\n augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>\n} & ([User | undefined] extends [TUser] ? { createUser?: CreateSubscriptionUser<TUser> } : { createUser: CreateSubscriptionUser<TUser> })\n\nexport const createSubscriptionContextFactory = <\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n>(\n config: CreateSubscriptionContextConfig<TUser, TAugment, TLogger>,\n): CreateSubscriptionContext<GraphQLContext<TLogger, RequestInfo, TUser> & TAugment> => {\n const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config\n // The conditional type on CreateSubscriptionContextConfig guarantees `createUser` is provided\n // when TUser is narrower than `User | undefined`, so defaulting is sound here.\n const createUser = (config.createUser ?? defaultCreateUser) as CreateSubscriptionUser<TUser>\n\n return async (input: SubscriptionContextInput) => {\n const { connectRequest: req, claims } = input\n\n // build request info from the connect request and socket\n const requestInfo: RequestInfo = {\n ...buildConnectRequestInfo(req),\n ...augmentRequestInfo?.(input),\n }\n\n const user = await createUser(input)\n\n // create request logger\n let logger: TLogger\n if (typeof requestLogger === 'function') {\n // build request logger metadata\n const requestLoggerMetadata: Record<string, unknown> = {}\n // add request info to log\n if (requestInfoToLog?.length) requestLoggerMetadata.request = pick(requestInfo, requestInfoToLog)\n // add user claims to log\n if (claims && claimsToLog?.length) requestLoggerMetadata.user = pick(claims, claimsToLog)\n // build the request logger\n logger = requestLogger(requestLoggerMetadata, user)\n } else logger = requestLogger\n\n const graphqlContext: GraphQLContext<TLogger, RequestInfo, TUser> = {\n requestInfo,\n logger,\n user,\n started: Date.now(),\n }\n\n const augmentedGraphQLContext = augmentContext ? { ...graphqlContext, ...(await augmentContext(graphqlContext)) } : graphqlContext\n\n return augmentedGraphQLContext as GraphQLContext<TLogger, RequestInfo, TUser> & TAugment\n }\n}\n\nconst defaultCreateUser: CreateSubscriptionUser<User | undefined> = ({ claims, connectionParams }) => {\n if (!claims) return Promise.resolve(undefined)\n const accessToken = extractTokenFromConnectionParams(connectionParams)\n return Promise.resolve(new User(claims, accessToken ?? ''))\n}\n"],"names":["requestInfo","buildConnectRequestInfo","pick","extractTokenFromConnectionParams","User"],"mappings":";;;;;;;AAgCO,MAAM,gCAAgC,GAAG,CAK9C,MAAiE,KACoB;AACrF,IAAA,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,gBAAgB,EAAE,cAAc,EAAE,GAAG,MAAM;;;IAGnG,MAAM,UAAU,IAAI,MAAM,CAAC,UAAU,IAAI,iBAAiB,CAAkC;AAE5F,IAAA,OAAO,OAAO,KAA+B,KAAI;QAC/C,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,KAAK;;AAG7C,QAAA,MAAMA,aAAW,GAAgB;YAC/B,GAAGC,mCAAuB,CAAC,GAAG,CAAC;AAC/B,YAAA,GAAG,kBAAkB,GAAG,KAAK,CAAC;SAC/B;AAED,QAAA,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC;;AAGpC,QAAA,IAAI,MAAe;AACnB,QAAA,IAAI,OAAO,aAAa,KAAK,UAAU,EAAE;;YAEvC,MAAM,qBAAqB,GAA4B,EAAE;;YAEzD,IAAI,gBAAgB,EAAE,MAAM;gBAAE,qBAAqB,CAAC,OAAO,GAAGC,WAAI,CAACF,aAAW,EAAE,gBAAgB,CAAC;;AAEjG,YAAA,IAAI,MAAM,IAAI,WAAW,EAAE,MAAM;gBAAE,qBAAqB,CAAC,IAAI,GAAGE,WAAI,CAAC,MAAM,EAAE,WAAW,CAAC;;AAEzF,YAAA,MAAM,GAAG,aAAa,CAAC,qBAAqB,EAAE,IAAI,CAAC;QACrD;;YAAO,MAAM,GAAG,aAAa;AAE7B,QAAA,MAAM,cAAc,GAAgD;yBAClEF,aAAW;YACX,MAAM;YACN,IAAI;AACJ,YAAA,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;SACpB;QAED,MAAM,uBAAuB,GAAG,cAAc,GAAG,EAAE,GAAG,cAAc,EAAE,IAAI,MAAM,cAAc,CAAC,cAAc,CAAC,CAAC,EAAE,GAAG,cAAc;AAElI,QAAA,OAAO,uBAAiF;AAC1F,IAAA,CAAC;AACH;AAEA,MAAM,iBAAiB,GAA6C,CAAC,EAAE,MAAM,EAAE,gBAAgB,EAAE,KAAI;AACnG,IAAA,IAAI,CAAC,MAAM;AAAE,QAAA,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;AAC9C,IAAA,MAAM,WAAW,GAAGG,sCAAgC,CAAC,gBAAgB,CAAC;AACtE,IAAA,OAAO,OAAO,CAAC,OAAO,CAAC,IAAIC,SAAI,CAAC,MAAM,EAAE,WAAW,IAAI,EAAE,CAAC,CAAC;AAC7D,CAAC;;;;"}
1
+ {"version":3,"file":"context.js","sources":["../../src/subscriptions/context.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { IncomingMessage } from 'http'\nimport { User } from '../User'\nimport type { CreateRequestLogger, GraphQLContext, JwtPayload, RequestInfo, RequestInfoLogKey } from '../context'\nimport { buildConnectRequestInfo } from '../request-info'\nimport { defaultExtractSubscriptionToken } from './utils'\n\nexport interface SubscriptionContextInput {\n connectRequest: IncomingMessage\n connectionParams?: Readonly<Record<string, unknown>>\n claims?: JwtPayload\n}\n\nexport type CreateSubscriptionUser<T = User | undefined> = (input: SubscriptionContextInput) => Promise<T> | T\nexport type CreateSubscriptionContext<TContext = GraphQLContext> = (input: SubscriptionContextInput) => Promise<TContext>\nexport type AugmentSubscriptionRequestInfo = (input: SubscriptionContextInput) => Record<string, unknown>\n// The token is extracted before the connection is authenticated, so the input has no claims.\nexport type ExtractSubscriptionToken = (input: Omit<SubscriptionContextInput, 'claims'>) => string | undefined\n\n// `createUser` is optional when TUser is compatible with the default `User | undefined`\n// (i.e. defaultCreateUser can satisfy it), and required when TUser is narrower.\nexport type CreateSubscriptionContextConfig<\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n> = {\n requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger\n augmentRequestInfo?: AugmentSubscriptionRequestInfo\n claimsToLog?: string[]\n requestInfoToLog?: Array<RequestInfoLogKey>\n augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>\n /**\n * Returns the token that the default `createUser` sets as `User.token`. Use the same function as\n * the `useSubscriptionsServer` `extractToken` option. Not called when you supply `createUser`.\n * Defaults to the bearer token in the `authorization` or `Authorization` connection parameter.\n */\n extractToken?: ExtractSubscriptionToken\n} & ([User | undefined] extends [TUser] ? { createUser?: CreateSubscriptionUser<TUser> } : { createUser: CreateSubscriptionUser<TUser> })\n\nexport const createSubscriptionContextFactory = <\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n>(\n config: CreateSubscriptionContextConfig<TUser, TAugment, TLogger>,\n): CreateSubscriptionContext<GraphQLContext<TLogger, RequestInfo, TUser> & TAugment> => {\n const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config\n // The conditional type on CreateSubscriptionContextConfig guarantees `createUser` is provided\n // when TUser is narrower than `User | undefined`, so defaulting is sound here.\n const createUser = (config.createUser ??\n buildDefaultCreateUser(config.extractToken ?? defaultExtractSubscriptionToken)) as CreateSubscriptionUser<TUser>\n\n return async (input: SubscriptionContextInput) => {\n const { connectRequest: req, claims } = input\n\n // build request info from the connect request and socket\n const requestInfo: RequestInfo = {\n ...buildConnectRequestInfo(req),\n ...augmentRequestInfo?.(input),\n }\n\n const user = await createUser(input)\n\n // create request logger\n let logger: TLogger\n if (typeof requestLogger === 'function') {\n // build request logger metadata\n const requestLoggerMetadata: Record<string, unknown> = {}\n // add request info to log\n if (requestInfoToLog?.length) requestLoggerMetadata.request = pick(requestInfo, requestInfoToLog)\n // add user claims to log\n if (claims && claimsToLog?.length) requestLoggerMetadata.user = pick(claims, claimsToLog)\n // build the request logger\n logger = requestLogger(requestLoggerMetadata, user)\n } else logger = requestLogger\n\n const graphqlContext: GraphQLContext<TLogger, RequestInfo, TUser> = {\n requestInfo,\n logger,\n user,\n started: Date.now(),\n }\n\n const augmentedGraphQLContext = augmentContext ? { ...graphqlContext, ...(await augmentContext(graphqlContext)) } : graphqlContext\n\n return augmentedGraphQLContext as GraphQLContext<TLogger, RequestInfo, TUser> & TAugment\n }\n}\n\nconst buildDefaultCreateUser =\n (extractToken: ExtractSubscriptionToken): CreateSubscriptionUser<User | undefined> =>\n (input) => {\n if (!input.claims) return Promise.resolve(undefined)\n return Promise.resolve(new User(input.claims, extractToken(input) ?? ''))\n }\n"],"names":["defaultExtractSubscriptionToken","requestInfo","buildConnectRequestInfo","pick","User"],"mappings":";;;;;;;AAwCO,MAAM,gCAAgC,GAAG,CAK9C,MAAiE,KACoB;AACrF,IAAA,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,gBAAgB,EAAE,cAAc,EAAE,GAAG,MAAM;;;AAGnG,IAAA,MAAM,UAAU,IAAI,MAAM,CAAC,UAAU;QACnC,sBAAsB,CAAC,MAAM,CAAC,YAAY,IAAIA,qCAA+B,CAAC,CAAkC;AAElH,IAAA,OAAO,OAAO,KAA+B,KAAI;QAC/C,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,KAAK;;AAG7C,QAAA,MAAMC,aAAW,GAAgB;YAC/B,GAAGC,mCAAuB,CAAC,GAAG,CAAC;AAC/B,YAAA,GAAG,kBAAkB,GAAG,KAAK,CAAC;SAC/B;AAED,QAAA,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC;;AAGpC,QAAA,IAAI,MAAe;AACnB,QAAA,IAAI,OAAO,aAAa,KAAK,UAAU,EAAE;;YAEvC,MAAM,qBAAqB,GAA4B,EAAE;;YAEzD,IAAI,gBAAgB,EAAE,MAAM;gBAAE,qBAAqB,CAAC,OAAO,GAAGC,WAAI,CAACF,aAAW,EAAE,gBAAgB,CAAC;;AAEjG,YAAA,IAAI,MAAM,IAAI,WAAW,EAAE,MAAM;gBAAE,qBAAqB,CAAC,IAAI,GAAGE,WAAI,CAAC,MAAM,EAAE,WAAW,CAAC;;AAEzF,YAAA,MAAM,GAAG,aAAa,CAAC,qBAAqB,EAAE,IAAI,CAAC;QACrD;;YAAO,MAAM,GAAG,aAAa;AAE7B,QAAA,MAAM,cAAc,GAAgD;yBAClEF,aAAW;YACX,MAAM;YACN,IAAI;AACJ,YAAA,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;SACpB;QAED,MAAM,uBAAuB,GAAG,cAAc,GAAG,EAAE,GAAG,cAAc,EAAE,IAAI,MAAM,cAAc,CAAC,cAAc,CAAC,CAAC,EAAE,GAAG,cAAc;AAElI,QAAA,OAAO,uBAAiF;AAC1F,IAAA,CAAC;AACH;AAEA,MAAM,sBAAsB,GAC1B,CAAC,YAAsC,KACvC,CAAC,KAAK,KAAI;IACR,IAAI,CAAC,KAAK,CAAC,MAAM;AAAE,QAAA,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;AACpD,IAAA,OAAO,OAAO,CAAC,OAAO,CAAC,IAAIG,SAAI,CAAC,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AAC3E,CAAC;;;;"}
@@ -1,13 +1,14 @@
1
1
  import { pick } from 'es-toolkit/compat';
2
2
  import { User } from '../User.mjs';
3
3
  import { buildConnectRequestInfo } from '../request-info.mjs';
4
- import { extractTokenFromConnectionParams } from './utils.mjs';
4
+ import { defaultExtractSubscriptionToken } from './utils.mjs';
5
5
 
6
6
  const createSubscriptionContextFactory = (config) => {
7
7
  const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config;
8
8
  // The conditional type on CreateSubscriptionContextConfig guarantees `createUser` is provided
9
9
  // when TUser is narrower than `User | undefined`, so defaulting is sound here.
10
- const createUser = (config.createUser ?? defaultCreateUser);
10
+ const createUser = (config.createUser ??
11
+ buildDefaultCreateUser(config.extractToken ?? defaultExtractSubscriptionToken));
11
12
  return async (input) => {
12
13
  const { connectRequest: req, claims } = input;
13
14
  // build request info from the connect request and socket
@@ -42,11 +43,10 @@ const createSubscriptionContextFactory = (config) => {
42
43
  return augmentedGraphQLContext;
43
44
  };
44
45
  };
45
- const defaultCreateUser = ({ claims, connectionParams }) => {
46
- if (!claims)
46
+ const buildDefaultCreateUser = (extractToken) => (input) => {
47
+ if (!input.claims)
47
48
  return Promise.resolve(undefined);
48
- const accessToken = extractTokenFromConnectionParams(connectionParams);
49
- return Promise.resolve(new User(claims, accessToken ?? ''));
49
+ return Promise.resolve(new User(input.claims, extractToken(input) ?? ''));
50
50
  };
51
51
 
52
52
  export { createSubscriptionContextFactory };
@@ -1 +1 @@
1
- {"version":3,"file":"context.mjs","sources":["../../src/subscriptions/context.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { IncomingMessage } from 'http'\nimport { User } from '../User'\nimport type { CreateRequestLogger, GraphQLContext, JwtPayload, RequestInfo, RequestInfoLogKey } from '../context'\nimport { buildConnectRequestInfo } from '../request-info'\nimport { extractTokenFromConnectionParams } from './utils'\n\nexport interface SubscriptionContextInput {\n connectRequest: IncomingMessage\n connectionParams?: Readonly<Record<string, unknown>>\n claims?: JwtPayload\n}\n\nexport type CreateSubscriptionUser<T = User | undefined> = (input: SubscriptionContextInput) => Promise<T> | T\nexport type CreateSubscriptionContext<TContext = GraphQLContext> = (input: SubscriptionContextInput) => Promise<TContext>\nexport type AugmentSubscriptionRequestInfo = (input: SubscriptionContextInput) => Record<string, unknown>\n\n// `createUser` is optional when TUser is compatible with the default `User | undefined`\n// (i.e. defaultCreateUser can satisfy it), and required when TUser is narrower.\nexport type CreateSubscriptionContextConfig<\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n> = {\n requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger\n augmentRequestInfo?: AugmentSubscriptionRequestInfo\n claimsToLog?: string[]\n requestInfoToLog?: Array<RequestInfoLogKey>\n augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>\n} & ([User | undefined] extends [TUser] ? { createUser?: CreateSubscriptionUser<TUser> } : { createUser: CreateSubscriptionUser<TUser> })\n\nexport const createSubscriptionContextFactory = <\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n>(\n config: CreateSubscriptionContextConfig<TUser, TAugment, TLogger>,\n): CreateSubscriptionContext<GraphQLContext<TLogger, RequestInfo, TUser> & TAugment> => {\n const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config\n // The conditional type on CreateSubscriptionContextConfig guarantees `createUser` is provided\n // when TUser is narrower than `User | undefined`, so defaulting is sound here.\n const createUser = (config.createUser ?? defaultCreateUser) as CreateSubscriptionUser<TUser>\n\n return async (input: SubscriptionContextInput) => {\n const { connectRequest: req, claims } = input\n\n // build request info from the connect request and socket\n const requestInfo: RequestInfo = {\n ...buildConnectRequestInfo(req),\n ...augmentRequestInfo?.(input),\n }\n\n const user = await createUser(input)\n\n // create request logger\n let logger: TLogger\n if (typeof requestLogger === 'function') {\n // build request logger metadata\n const requestLoggerMetadata: Record<string, unknown> = {}\n // add request info to log\n if (requestInfoToLog?.length) requestLoggerMetadata.request = pick(requestInfo, requestInfoToLog)\n // add user claims to log\n if (claims && claimsToLog?.length) requestLoggerMetadata.user = pick(claims, claimsToLog)\n // build the request logger\n logger = requestLogger(requestLoggerMetadata, user)\n } else logger = requestLogger\n\n const graphqlContext: GraphQLContext<TLogger, RequestInfo, TUser> = {\n requestInfo,\n logger,\n user,\n started: Date.now(),\n }\n\n const augmentedGraphQLContext = augmentContext ? { ...graphqlContext, ...(await augmentContext(graphqlContext)) } : graphqlContext\n\n return augmentedGraphQLContext as GraphQLContext<TLogger, RequestInfo, TUser> & TAugment\n }\n}\n\nconst defaultCreateUser: CreateSubscriptionUser<User | undefined> = ({ claims, connectionParams }) => {\n if (!claims) return Promise.resolve(undefined)\n const accessToken = extractTokenFromConnectionParams(connectionParams)\n return Promise.resolve(new User(claims, accessToken ?? ''))\n}\n"],"names":[],"mappings":";;;;;AAgCO,MAAM,gCAAgC,GAAG,CAK9C,MAAiE,KACoB;AACrF,IAAA,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,gBAAgB,EAAE,cAAc,EAAE,GAAG,MAAM;;;IAGnG,MAAM,UAAU,IAAI,MAAM,CAAC,UAAU,IAAI,iBAAiB,CAAkC;AAE5F,IAAA,OAAO,OAAO,KAA+B,KAAI;QAC/C,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,KAAK;;AAG7C,QAAA,MAAM,WAAW,GAAgB;YAC/B,GAAG,uBAAuB,CAAC,GAAG,CAAC;AAC/B,YAAA,GAAG,kBAAkB,GAAG,KAAK,CAAC;SAC/B;AAED,QAAA,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC;;AAGpC,QAAA,IAAI,MAAe;AACnB,QAAA,IAAI,OAAO,aAAa,KAAK,UAAU,EAAE;;YAEvC,MAAM,qBAAqB,GAA4B,EAAE;;YAEzD,IAAI,gBAAgB,EAAE,MAAM;gBAAE,qBAAqB,CAAC,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,gBAAgB,CAAC;;AAEjG,YAAA,IAAI,MAAM,IAAI,WAAW,EAAE,MAAM;gBAAE,qBAAqB,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;;AAEzF,YAAA,MAAM,GAAG,aAAa,CAAC,qBAAqB,EAAE,IAAI,CAAC;QACrD;;YAAO,MAAM,GAAG,aAAa;AAE7B,QAAA,MAAM,cAAc,GAAgD;YAClE,WAAW;YACX,MAAM;YACN,IAAI;AACJ,YAAA,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;SACpB;QAED,MAAM,uBAAuB,GAAG,cAAc,GAAG,EAAE,GAAG,cAAc,EAAE,IAAI,MAAM,cAAc,CAAC,cAAc,CAAC,CAAC,EAAE,GAAG,cAAc;AAElI,QAAA,OAAO,uBAAiF;AAC1F,IAAA,CAAC;AACH;AAEA,MAAM,iBAAiB,GAA6C,CAAC,EAAE,MAAM,EAAE,gBAAgB,EAAE,KAAI;AACnG,IAAA,IAAI,CAAC,MAAM;AAAE,QAAA,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;AAC9C,IAAA,MAAM,WAAW,GAAG,gCAAgC,CAAC,gBAAgB,CAAC;AACtE,IAAA,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,WAAW,IAAI,EAAE,CAAC,CAAC;AAC7D,CAAC;;;;"}
1
+ {"version":3,"file":"context.mjs","sources":["../../src/subscriptions/context.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { IncomingMessage } from 'http'\nimport { User } from '../User'\nimport type { CreateRequestLogger, GraphQLContext, JwtPayload, RequestInfo, RequestInfoLogKey } from '../context'\nimport { buildConnectRequestInfo } from '../request-info'\nimport { defaultExtractSubscriptionToken } from './utils'\n\nexport interface SubscriptionContextInput {\n connectRequest: IncomingMessage\n connectionParams?: Readonly<Record<string, unknown>>\n claims?: JwtPayload\n}\n\nexport type CreateSubscriptionUser<T = User | undefined> = (input: SubscriptionContextInput) => Promise<T> | T\nexport type CreateSubscriptionContext<TContext = GraphQLContext> = (input: SubscriptionContextInput) => Promise<TContext>\nexport type AugmentSubscriptionRequestInfo = (input: SubscriptionContextInput) => Record<string, unknown>\n// The token is extracted before the connection is authenticated, so the input has no claims.\nexport type ExtractSubscriptionToken = (input: Omit<SubscriptionContextInput, 'claims'>) => string | undefined\n\n// `createUser` is optional when TUser is compatible with the default `User | undefined`\n// (i.e. defaultCreateUser can satisfy it), and required when TUser is narrower.\nexport type CreateSubscriptionContextConfig<\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n> = {\n requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger\n augmentRequestInfo?: AugmentSubscriptionRequestInfo\n claimsToLog?: string[]\n requestInfoToLog?: Array<RequestInfoLogKey>\n augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>\n /**\n * Returns the token that the default `createUser` sets as `User.token`. Use the same function as\n * the `useSubscriptionsServer` `extractToken` option. Not called when you supply `createUser`.\n * Defaults to the bearer token in the `authorization` or `Authorization` connection parameter.\n */\n extractToken?: ExtractSubscriptionToken\n} & ([User | undefined] extends [TUser] ? { createUser?: CreateSubscriptionUser<TUser> } : { createUser: CreateSubscriptionUser<TUser> })\n\nexport const createSubscriptionContextFactory = <\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n>(\n config: CreateSubscriptionContextConfig<TUser, TAugment, TLogger>,\n): CreateSubscriptionContext<GraphQLContext<TLogger, RequestInfo, TUser> & TAugment> => {\n const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config\n // The conditional type on CreateSubscriptionContextConfig guarantees `createUser` is provided\n // when TUser is narrower than `User | undefined`, so defaulting is sound here.\n const createUser = (config.createUser ??\n buildDefaultCreateUser(config.extractToken ?? defaultExtractSubscriptionToken)) as CreateSubscriptionUser<TUser>\n\n return async (input: SubscriptionContextInput) => {\n const { connectRequest: req, claims } = input\n\n // build request info from the connect request and socket\n const requestInfo: RequestInfo = {\n ...buildConnectRequestInfo(req),\n ...augmentRequestInfo?.(input),\n }\n\n const user = await createUser(input)\n\n // create request logger\n let logger: TLogger\n if (typeof requestLogger === 'function') {\n // build request logger metadata\n const requestLoggerMetadata: Record<string, unknown> = {}\n // add request info to log\n if (requestInfoToLog?.length) requestLoggerMetadata.request = pick(requestInfo, requestInfoToLog)\n // add user claims to log\n if (claims && claimsToLog?.length) requestLoggerMetadata.user = pick(claims, claimsToLog)\n // build the request logger\n logger = requestLogger(requestLoggerMetadata, user)\n } else logger = requestLogger\n\n const graphqlContext: GraphQLContext<TLogger, RequestInfo, TUser> = {\n requestInfo,\n logger,\n user,\n started: Date.now(),\n }\n\n const augmentedGraphQLContext = augmentContext ? { ...graphqlContext, ...(await augmentContext(graphqlContext)) } : graphqlContext\n\n return augmentedGraphQLContext as GraphQLContext<TLogger, RequestInfo, TUser> & TAugment\n }\n}\n\nconst buildDefaultCreateUser =\n (extractToken: ExtractSubscriptionToken): CreateSubscriptionUser<User | undefined> =>\n (input) => {\n if (!input.claims) return Promise.resolve(undefined)\n return Promise.resolve(new User(input.claims, extractToken(input) ?? ''))\n }\n"],"names":[],"mappings":";;;;;AAwCO,MAAM,gCAAgC,GAAG,CAK9C,MAAiE,KACoB;AACrF,IAAA,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,gBAAgB,EAAE,cAAc,EAAE,GAAG,MAAM;;;AAGnG,IAAA,MAAM,UAAU,IAAI,MAAM,CAAC,UAAU;QACnC,sBAAsB,CAAC,MAAM,CAAC,YAAY,IAAI,+BAA+B,CAAC,CAAkC;AAElH,IAAA,OAAO,OAAO,KAA+B,KAAI;QAC/C,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,KAAK;;AAG7C,QAAA,MAAM,WAAW,GAAgB;YAC/B,GAAG,uBAAuB,CAAC,GAAG,CAAC;AAC/B,YAAA,GAAG,kBAAkB,GAAG,KAAK,CAAC;SAC/B;AAED,QAAA,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC;;AAGpC,QAAA,IAAI,MAAe;AACnB,QAAA,IAAI,OAAO,aAAa,KAAK,UAAU,EAAE;;YAEvC,MAAM,qBAAqB,GAA4B,EAAE;;YAEzD,IAAI,gBAAgB,EAAE,MAAM;gBAAE,qBAAqB,CAAC,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,gBAAgB,CAAC;;AAEjG,YAAA,IAAI,MAAM,IAAI,WAAW,EAAE,MAAM;gBAAE,qBAAqB,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;;AAEzF,YAAA,MAAM,GAAG,aAAa,CAAC,qBAAqB,EAAE,IAAI,CAAC;QACrD;;YAAO,MAAM,GAAG,aAAa;AAE7B,QAAA,MAAM,cAAc,GAAgD;YAClE,WAAW;YACX,MAAM;YACN,IAAI;AACJ,YAAA,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;SACpB;QAED,MAAM,uBAAuB,GAAG,cAAc,GAAG,EAAE,GAAG,cAAc,EAAE,IAAI,MAAM,cAAc,CAAC,cAAc,CAAC,CAAC,EAAE,GAAG,cAAc;AAElI,QAAA,OAAO,uBAAiF;AAC1F,IAAA,CAAC;AACH;AAEA,MAAM,sBAAsB,GAC1B,CAAC,YAAsC,KACvC,CAAC,KAAK,KAAI;IACR,IAAI,CAAC,KAAK,CAAC,MAAM;AAAE,QAAA,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;AACpD,IAAA,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AAC3E,CAAC;;;;"}
@@ -2,8 +2,8 @@ import type { Logger } from '@makerx/node-common';
2
2
  import type { GraphQLSchema } from 'graphql';
3
3
  import type { Server } from 'http';
4
4
  import type { GraphQLContext, JwtPayload } from '../context';
5
- import type { CreateSubscriptionContext } from './context';
6
- export declare function useSubscriptionsServer<TLogger extends Logger = Logger>({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel, path, verifyToken, requireAuth, jwtClaimsToLog, resolveSubscriptionOperationLogger, includeDeprecatedElements, }: {
5
+ import type { CreateSubscriptionContext, ExtractSubscriptionToken } from './context';
6
+ export declare function useSubscriptionsServer<TLogger extends Logger = Logger>({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel, path, verifyToken, extractToken, requireAuth, jwtClaimsToLog, resolveSubscriptionOperationLogger, includeDeprecatedElements, }: {
7
7
  schema: GraphQLSchema;
8
8
  httpServer: Server;
9
9
  createSubscriptionContext: CreateSubscriptionContext;
@@ -11,6 +11,12 @@ export declare function useSubscriptionsServer<TLogger extends Logger = Logger>(
11
11
  operationLogLevel?: keyof TLogger;
12
12
  path?: string;
13
13
  verifyToken?: (host: string, token: string) => Promise<JwtPayload>;
14
+ /**
15
+ * Returns the token to pass to `verifyToken`, e.g. from a JWT assertion header that a proxy adds
16
+ * to the connect request. Defaults to the bearer token in the `authorization` or `Authorization`
17
+ * connection parameter. Pass the same function to `createSubscriptionContextFactory`.
18
+ */
19
+ extractToken?: ExtractSubscriptionToken;
14
20
  requireAuth?: boolean;
15
21
  jwtClaimsToLog?: string[];
16
22
  resolveSubscriptionOperationLogger?: (context: GraphQLContext) => TLogger;
@@ -2,8 +2,8 @@ import type { Logger } from '@makerx/node-common';
2
2
  import type { GraphQLSchema } from 'graphql';
3
3
  import type { Server } from 'http';
4
4
  import type { GraphQLContext, JwtPayload } from '../context.mjs';
5
- import type { CreateSubscriptionContext } from './context.mjs';
6
- export declare function useSubscriptionsServer<TLogger extends Logger = Logger>({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel, path, verifyToken, requireAuth, jwtClaimsToLog, resolveSubscriptionOperationLogger, includeDeprecatedElements, }: {
5
+ import type { CreateSubscriptionContext, ExtractSubscriptionToken } from './context.mjs';
6
+ export declare function useSubscriptionsServer<TLogger extends Logger = Logger>({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel, path, verifyToken, extractToken, requireAuth, jwtClaimsToLog, resolveSubscriptionOperationLogger, includeDeprecatedElements, }: {
7
7
  schema: GraphQLSchema;
8
8
  httpServer: Server;
9
9
  createSubscriptionContext: CreateSubscriptionContext;
@@ -11,6 +11,12 @@ export declare function useSubscriptionsServer<TLogger extends Logger = Logger>(
11
11
  operationLogLevel?: keyof TLogger;
12
12
  path?: string;
13
13
  verifyToken?: (host: string, token: string) => Promise<JwtPayload>;
14
+ /**
15
+ * Returns the token to pass to `verifyToken`, e.g. from a JWT assertion header that a proxy adds
16
+ * to the connect request. Defaults to the bearer token in the `authorization` or `Authorization`
17
+ * connection parameter. Pass the same function to `createSubscriptionContextFactory`.
18
+ */
19
+ extractToken?: ExtractSubscriptionToken;
14
20
  requireAuth?: boolean;
15
21
  jwtClaimsToLog?: string[];
16
22
  resolveSubscriptionOperationLogger?: (context: GraphQLContext) => TLogger;
@@ -2,8 +2,8 @@ import type { Logger } from '@makerx/node-common';
2
2
  import type { GraphQLSchema } from 'graphql';
3
3
  import type { Server } from 'http';
4
4
  import type { GraphQLContext, JwtPayload } from '../context';
5
- import type { CreateSubscriptionContext } from './context';
6
- export declare function useSubscriptionsServer<TLogger extends Logger = Logger>({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel, path, verifyToken, requireAuth, jwtClaimsToLog, resolveSubscriptionOperationLogger, includeDeprecatedElements, }: {
5
+ import type { CreateSubscriptionContext, ExtractSubscriptionToken } from './context';
6
+ export declare function useSubscriptionsServer<TLogger extends Logger = Logger>({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel, path, verifyToken, extractToken, requireAuth, jwtClaimsToLog, resolveSubscriptionOperationLogger, includeDeprecatedElements, }: {
7
7
  schema: GraphQLSchema;
8
8
  httpServer: Server;
9
9
  createSubscriptionContext: CreateSubscriptionContext;
@@ -11,6 +11,12 @@ export declare function useSubscriptionsServer<TLogger extends Logger = Logger>(
11
11
  operationLogLevel?: keyof TLogger;
12
12
  path?: string;
13
13
  verifyToken?: (host: string, token: string) => Promise<JwtPayload>;
14
+ /**
15
+ * Returns the token to pass to `verifyToken`, e.g. from a JWT assertion header that a proxy adds
16
+ * to the connect request. Defaults to the bearer token in the `authorization` or `Authorization`
17
+ * connection parameter. Pass the same function to `createSubscriptionContextFactory`.
18
+ */
19
+ extractToken?: ExtractSubscriptionToken;
14
20
  requireAuth?: boolean;
15
21
  jwtClaimsToLog?: string[];
16
22
  resolveSubscriptionOperationLogger?: (context: GraphQLContext) => TLogger;
@@ -7,7 +7,7 @@ var ws = require('ws');
7
7
  var logging = require('../logging.js');
8
8
  var utils = require('./utils.js');
9
9
 
10
- function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel = 'info', path = '/graphql', verifyToken, requireAuth, jwtClaimsToLog = ['oid', 'iss'], resolveSubscriptionOperationLogger, includeDeprecatedElements, }) {
10
+ function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel = 'info', path = '/graphql', verifyToken, extractToken = utils.defaultExtractSubscriptionToken, requireAuth, jwtClaimsToLog = ['oid', 'iss'], resolveSubscriptionOperationLogger, includeDeprecatedElements, }) {
11
11
  if (requireAuth && !verifyToken)
12
12
  throw new Error('verifyToken must be supplied when requireAuth is true');
13
13
  const wsServer = new ws.WebSocketServer({
@@ -25,10 +25,10 @@ function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext,
25
25
  logger.info(connectionEstablished);
26
26
  return true;
27
27
  }
28
- const token = utils.extractTokenFromConnectionParams(ctx.connectionParams);
28
+ const token = extractToken({ connectRequest: ctx.extra.request, connectionParams: ctx.connectionParams });
29
29
  if (!token) {
30
30
  if (requireAuth) {
31
- logger.error('No authorization parameter was supplied via websocket connection params');
31
+ logger.error('No auth token was supplied with the websocket connection');
32
32
  return false;
33
33
  }
34
34
  logger.info(connectionEstablished);
@@ -50,10 +50,10 @@ function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext,
50
50
  onSubscribe: async (ctx) => {
51
51
  if (!verifyToken)
52
52
  return;
53
- const token = utils.extractTokenFromConnectionParams(ctx.connectionParams);
53
+ const token = extractToken({ connectRequest: ctx.extra.request, connectionParams: ctx.connectionParams });
54
54
  if (!token) {
55
55
  if (requireAuth) {
56
- logger.error('No authorization parameter was supplied via websocket connection params');
56
+ logger.error('No auth token was supplied with the websocket connection');
57
57
  ctx.extra.socket.close(graphqlWs.CloseCode.Forbidden, 'Forbidden');
58
58
  }
59
59
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sources":["../../src/subscriptions/server.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { GraphQLSchema } from 'graphql'\nimport { CloseCode } from 'graphql-ws'\nimport { useServer } from 'graphql-ws/use/ws'\nimport type { Server } from 'http'\nimport { WebSocketServer } from 'ws'\nimport type { GraphQLContext, JwtPayload } from '../context'\nimport { logSubscriptionOperation } from '../logging'\nimport type { CreateSubscriptionContext } from './context'\nimport { extractTokenFromConnectionParams, getHost } from './utils'\n\nexport function useSubscriptionsServer<TLogger extends Logger = Logger>({\n schema,\n httpServer,\n createSubscriptionContext,\n logger,\n operationLogLevel = 'info',\n path = '/graphql',\n verifyToken,\n requireAuth,\n jwtClaimsToLog = ['oid', 'iss'],\n resolveSubscriptionOperationLogger,\n includeDeprecatedElements,\n}: {\n schema: GraphQLSchema\n httpServer: Server\n createSubscriptionContext: CreateSubscriptionContext\n logger: TLogger\n operationLogLevel?: keyof TLogger\n path?: string\n verifyToken?: (host: string, token: string) => Promise<JwtPayload>\n requireAuth?: boolean\n jwtClaimsToLog?: string[]\n resolveSubscriptionOperationLogger?: (context: GraphQLContext) => TLogger\n /**\n * If true, deprecated schema elements a subscription uses are collected and logged when it is\n * established. Not collected per emitted payload: the usage is a property of the operation.\n */\n includeDeprecatedElements?: boolean\n}) {\n if (requireAuth && !verifyToken) throw new Error('verifyToken must be supplied when requireAuth is true')\n\n const wsServer = new WebSocketServer({\n server: httpServer,\n path,\n })\n\n return useServer(\n {\n schema,\n onError(_ctx, message, errors) {\n logger.error('GraphQL subscriptions server error', { message, errors })\n },\n onConnect: async (ctx) => {\n const connectionEstablished = 'Subscription connection established'\n if (!verifyToken) {\n logger.info(connectionEstablished)\n return true\n }\n\n const token = extractTokenFromConnectionParams(ctx.connectionParams)\n if (!token) {\n if (requireAuth) {\n logger.error('No authorization parameter was supplied via websocket connection params')\n return false\n }\n logger.info(connectionEstablished)\n return true\n }\n\n try {\n const claims = await verifyToken(getHost(ctx.extra.request), token)\n ctx.extra.claims = claims as unknown as undefined\n logger.info(connectionEstablished, {\n claims: pick(claims, jwtClaimsToLog),\n })\n return true\n } catch (error) {\n logger.error('Failed to verify subscription connection auth token', { error })\n return false\n }\n },\n onSubscribe: async (ctx) => {\n if (!verifyToken) return\n const token = extractTokenFromConnectionParams(ctx.connectionParams)\n if (!token) {\n if (requireAuth) {\n logger.error('No authorization parameter was supplied via websocket connection params')\n ctx.extra.socket.close(CloseCode.Forbidden, 'Forbidden')\n }\n return\n }\n try {\n await verifyToken(getHost(ctx.extra.request), token)\n } catch (error) {\n logger.warn('Subscription connection auth token is no longer valid', { claims: pick(ctx.extra.claims, jwtClaimsToLog), error })\n ctx.extra.socket.close(CloseCode.Forbidden, 'Forbidden')\n }\n },\n onDisconnect({ extra: { claims } }) {\n logger.info('Subscription connection disconnected', { claims: pick(claims, jwtClaimsToLog) })\n },\n context: async (ctx) => {\n return createSubscriptionContext({\n connectRequest: ctx.extra.request,\n connectionParams: ctx.connectionParams,\n claims: ctx.extra.claims as JwtPayload | undefined,\n })\n },\n onOperation(_ctx, id, _payload, args) {\n logSubscriptionOperation({\n id,\n args,\n logLevel: operationLogLevel,\n resolveLogger: resolveSubscriptionOperationLogger,\n includeDeprecatedElements,\n })\n },\n onNext(_ctx, id, _payload, args, { data, ...result }) {\n logSubscriptionOperation({ id, args, logLevel: operationLogLevel, result, resolveLogger: resolveSubscriptionOperationLogger })\n },\n },\n wsServer,\n )\n}\n"],"names":["WebSocketServer","useServer","extractTokenFromConnectionParams","getHost","pick","CloseCode","logSubscriptionOperation"],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sources":["../../src/subscriptions/server.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { GraphQLSchema } from 'graphql'\nimport { CloseCode } from 'graphql-ws'\nimport { useServer } from 'graphql-ws/use/ws'\nimport type { Server } from 'http'\nimport { WebSocketServer } from 'ws'\nimport type { GraphQLContext, JwtPayload } from '../context'\nimport { logSubscriptionOperation } from '../logging'\nimport type { CreateSubscriptionContext, ExtractSubscriptionToken } from './context'\nimport { defaultExtractSubscriptionToken, getHost } from './utils'\n\nexport function useSubscriptionsServer<TLogger extends Logger = Logger>({\n schema,\n httpServer,\n createSubscriptionContext,\n logger,\n operationLogLevel = 'info',\n path = '/graphql',\n verifyToken,\n extractToken = defaultExtractSubscriptionToken,\n requireAuth,\n jwtClaimsToLog = ['oid', 'iss'],\n resolveSubscriptionOperationLogger,\n includeDeprecatedElements,\n}: {\n schema: GraphQLSchema\n httpServer: Server\n createSubscriptionContext: CreateSubscriptionContext\n logger: TLogger\n operationLogLevel?: keyof TLogger\n path?: string\n verifyToken?: (host: string, token: string) => Promise<JwtPayload>\n /**\n * Returns the token to pass to `verifyToken`, e.g. from a JWT assertion header that a proxy adds\n * to the connect request. Defaults to the bearer token in the `authorization` or `Authorization`\n * connection parameter. Pass the same function to `createSubscriptionContextFactory`.\n */\n extractToken?: ExtractSubscriptionToken\n requireAuth?: boolean\n jwtClaimsToLog?: string[]\n resolveSubscriptionOperationLogger?: (context: GraphQLContext) => TLogger\n /**\n * If true, deprecated schema elements a subscription uses are collected and logged when it is\n * established. Not collected per emitted payload: the usage is a property of the operation.\n */\n includeDeprecatedElements?: boolean\n}) {\n if (requireAuth && !verifyToken) throw new Error('verifyToken must be supplied when requireAuth is true')\n\n const wsServer = new WebSocketServer({\n server: httpServer,\n path,\n })\n\n return useServer(\n {\n schema,\n onError(_ctx, message, errors) {\n logger.error('GraphQL subscriptions server error', { message, errors })\n },\n onConnect: async (ctx) => {\n const connectionEstablished = 'Subscription connection established'\n if (!verifyToken) {\n logger.info(connectionEstablished)\n return true\n }\n\n const token = extractToken({ connectRequest: ctx.extra.request, connectionParams: ctx.connectionParams })\n if (!token) {\n if (requireAuth) {\n logger.error('No auth token was supplied with the websocket connection')\n return false\n }\n logger.info(connectionEstablished)\n return true\n }\n\n try {\n const claims = await verifyToken(getHost(ctx.extra.request), token)\n ctx.extra.claims = claims as unknown as undefined\n logger.info(connectionEstablished, {\n claims: pick(claims, jwtClaimsToLog),\n })\n return true\n } catch (error) {\n logger.error('Failed to verify subscription connection auth token', { error })\n return false\n }\n },\n onSubscribe: async (ctx) => {\n if (!verifyToken) return\n const token = extractToken({ connectRequest: ctx.extra.request, connectionParams: ctx.connectionParams })\n if (!token) {\n if (requireAuth) {\n logger.error('No auth token was supplied with the websocket connection')\n ctx.extra.socket.close(CloseCode.Forbidden, 'Forbidden')\n }\n return\n }\n try {\n await verifyToken(getHost(ctx.extra.request), token)\n } catch (error) {\n logger.warn('Subscription connection auth token is no longer valid', { claims: pick(ctx.extra.claims, jwtClaimsToLog), error })\n ctx.extra.socket.close(CloseCode.Forbidden, 'Forbidden')\n }\n },\n onDisconnect({ extra: { claims } }) {\n logger.info('Subscription connection disconnected', { claims: pick(claims, jwtClaimsToLog) })\n },\n context: async (ctx) => {\n return createSubscriptionContext({\n connectRequest: ctx.extra.request,\n connectionParams: ctx.connectionParams,\n claims: ctx.extra.claims as JwtPayload | undefined,\n })\n },\n onOperation(_ctx, id, _payload, args) {\n logSubscriptionOperation({\n id,\n args,\n logLevel: operationLogLevel,\n resolveLogger: resolveSubscriptionOperationLogger,\n includeDeprecatedElements,\n })\n },\n onNext(_ctx, id, _payload, args, { data, ...result }) {\n logSubscriptionOperation({ id, args, logLevel: operationLogLevel, result, resolveLogger: resolveSubscriptionOperationLogger })\n },\n },\n wsServer,\n )\n}\n"],"names":["defaultExtractSubscriptionToken","WebSocketServer","useServer","getHost","pick","CloseCode","logSubscriptionOperation"],"mappings":"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"}
@@ -3,9 +3,9 @@ import { CloseCode } from 'graphql-ws';
3
3
  import { useServer } from 'graphql-ws/use/ws';
4
4
  import { WebSocketServer } from 'ws';
5
5
  import { logSubscriptionOperation } from '../logging.mjs';
6
- import { extractTokenFromConnectionParams, getHost } from './utils.mjs';
6
+ import { defaultExtractSubscriptionToken, getHost } from './utils.mjs';
7
7
 
8
- function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel = 'info', path = '/graphql', verifyToken, requireAuth, jwtClaimsToLog = ['oid', 'iss'], resolveSubscriptionOperationLogger, includeDeprecatedElements, }) {
8
+ function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel = 'info', path = '/graphql', verifyToken, extractToken = defaultExtractSubscriptionToken, requireAuth, jwtClaimsToLog = ['oid', 'iss'], resolveSubscriptionOperationLogger, includeDeprecatedElements, }) {
9
9
  if (requireAuth && !verifyToken)
10
10
  throw new Error('verifyToken must be supplied when requireAuth is true');
11
11
  const wsServer = new WebSocketServer({
@@ -23,10 +23,10 @@ function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext,
23
23
  logger.info(connectionEstablished);
24
24
  return true;
25
25
  }
26
- const token = extractTokenFromConnectionParams(ctx.connectionParams);
26
+ const token = extractToken({ connectRequest: ctx.extra.request, connectionParams: ctx.connectionParams });
27
27
  if (!token) {
28
28
  if (requireAuth) {
29
- logger.error('No authorization parameter was supplied via websocket connection params');
29
+ logger.error('No auth token was supplied with the websocket connection');
30
30
  return false;
31
31
  }
32
32
  logger.info(connectionEstablished);
@@ -48,10 +48,10 @@ function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext,
48
48
  onSubscribe: async (ctx) => {
49
49
  if (!verifyToken)
50
50
  return;
51
- const token = extractTokenFromConnectionParams(ctx.connectionParams);
51
+ const token = extractToken({ connectRequest: ctx.extra.request, connectionParams: ctx.connectionParams });
52
52
  if (!token) {
53
53
  if (requireAuth) {
54
- logger.error('No authorization parameter was supplied via websocket connection params');
54
+ logger.error('No auth token was supplied with the websocket connection');
55
55
  ctx.extra.socket.close(CloseCode.Forbidden, 'Forbidden');
56
56
  }
57
57
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"server.mjs","sources":["../../src/subscriptions/server.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { GraphQLSchema } from 'graphql'\nimport { CloseCode } from 'graphql-ws'\nimport { useServer } from 'graphql-ws/use/ws'\nimport type { Server } from 'http'\nimport { WebSocketServer } from 'ws'\nimport type { GraphQLContext, JwtPayload } from '../context'\nimport { logSubscriptionOperation } from '../logging'\nimport type { CreateSubscriptionContext } from './context'\nimport { extractTokenFromConnectionParams, getHost } from './utils'\n\nexport function useSubscriptionsServer<TLogger extends Logger = Logger>({\n schema,\n httpServer,\n createSubscriptionContext,\n logger,\n operationLogLevel = 'info',\n path = '/graphql',\n verifyToken,\n requireAuth,\n jwtClaimsToLog = ['oid', 'iss'],\n resolveSubscriptionOperationLogger,\n includeDeprecatedElements,\n}: {\n schema: GraphQLSchema\n httpServer: Server\n createSubscriptionContext: CreateSubscriptionContext\n logger: TLogger\n operationLogLevel?: keyof TLogger\n path?: string\n verifyToken?: (host: string, token: string) => Promise<JwtPayload>\n requireAuth?: boolean\n jwtClaimsToLog?: string[]\n resolveSubscriptionOperationLogger?: (context: GraphQLContext) => TLogger\n /**\n * If true, deprecated schema elements a subscription uses are collected and logged when it is\n * established. Not collected per emitted payload: the usage is a property of the operation.\n */\n includeDeprecatedElements?: boolean\n}) {\n if (requireAuth && !verifyToken) throw new Error('verifyToken must be supplied when requireAuth is true')\n\n const wsServer = new WebSocketServer({\n server: httpServer,\n path,\n })\n\n return useServer(\n {\n schema,\n onError(_ctx, message, errors) {\n logger.error('GraphQL subscriptions server error', { message, errors })\n },\n onConnect: async (ctx) => {\n const connectionEstablished = 'Subscription connection established'\n if (!verifyToken) {\n logger.info(connectionEstablished)\n return true\n }\n\n const token = extractTokenFromConnectionParams(ctx.connectionParams)\n if (!token) {\n if (requireAuth) {\n logger.error('No authorization parameter was supplied via websocket connection params')\n return false\n }\n logger.info(connectionEstablished)\n return true\n }\n\n try {\n const claims = await verifyToken(getHost(ctx.extra.request), token)\n ctx.extra.claims = claims as unknown as undefined\n logger.info(connectionEstablished, {\n claims: pick(claims, jwtClaimsToLog),\n })\n return true\n } catch (error) {\n logger.error('Failed to verify subscription connection auth token', { error })\n return false\n }\n },\n onSubscribe: async (ctx) => {\n if (!verifyToken) return\n const token = extractTokenFromConnectionParams(ctx.connectionParams)\n if (!token) {\n if (requireAuth) {\n logger.error('No authorization parameter was supplied via websocket connection params')\n ctx.extra.socket.close(CloseCode.Forbidden, 'Forbidden')\n }\n return\n }\n try {\n await verifyToken(getHost(ctx.extra.request), token)\n } catch (error) {\n logger.warn('Subscription connection auth token is no longer valid', { claims: pick(ctx.extra.claims, jwtClaimsToLog), error })\n ctx.extra.socket.close(CloseCode.Forbidden, 'Forbidden')\n }\n },\n onDisconnect({ extra: { claims } }) {\n logger.info('Subscription connection disconnected', { claims: pick(claims, jwtClaimsToLog) })\n },\n context: async (ctx) => {\n return createSubscriptionContext({\n connectRequest: ctx.extra.request,\n connectionParams: ctx.connectionParams,\n claims: ctx.extra.claims as JwtPayload | undefined,\n })\n },\n onOperation(_ctx, id, _payload, args) {\n logSubscriptionOperation({\n id,\n args,\n logLevel: operationLogLevel,\n resolveLogger: resolveSubscriptionOperationLogger,\n includeDeprecatedElements,\n })\n },\n onNext(_ctx, id, _payload, args, { data, ...result }) {\n logSubscriptionOperation({ id, args, logLevel: operationLogLevel, result, resolveLogger: resolveSubscriptionOperationLogger })\n },\n },\n wsServer,\n )\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.mjs","sources":["../../src/subscriptions/server.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { GraphQLSchema } from 'graphql'\nimport { CloseCode } from 'graphql-ws'\nimport { useServer } from 'graphql-ws/use/ws'\nimport type { Server } from 'http'\nimport { WebSocketServer } from 'ws'\nimport type { GraphQLContext, JwtPayload } from '../context'\nimport { logSubscriptionOperation } from '../logging'\nimport type { CreateSubscriptionContext, ExtractSubscriptionToken } from './context'\nimport { defaultExtractSubscriptionToken, getHost } from './utils'\n\nexport function useSubscriptionsServer<TLogger extends Logger = Logger>({\n schema,\n httpServer,\n createSubscriptionContext,\n logger,\n operationLogLevel = 'info',\n path = '/graphql',\n verifyToken,\n extractToken = defaultExtractSubscriptionToken,\n requireAuth,\n jwtClaimsToLog = ['oid', 'iss'],\n resolveSubscriptionOperationLogger,\n includeDeprecatedElements,\n}: {\n schema: GraphQLSchema\n httpServer: Server\n createSubscriptionContext: CreateSubscriptionContext\n logger: TLogger\n operationLogLevel?: keyof TLogger\n path?: string\n verifyToken?: (host: string, token: string) => Promise<JwtPayload>\n /**\n * Returns the token to pass to `verifyToken`, e.g. from a JWT assertion header that a proxy adds\n * to the connect request. Defaults to the bearer token in the `authorization` or `Authorization`\n * connection parameter. Pass the same function to `createSubscriptionContextFactory`.\n */\n extractToken?: ExtractSubscriptionToken\n requireAuth?: boolean\n jwtClaimsToLog?: string[]\n resolveSubscriptionOperationLogger?: (context: GraphQLContext) => TLogger\n /**\n * If true, deprecated schema elements a subscription uses are collected and logged when it is\n * established. Not collected per emitted payload: the usage is a property of the operation.\n */\n includeDeprecatedElements?: boolean\n}) {\n if (requireAuth && !verifyToken) throw new Error('verifyToken must be supplied when requireAuth is true')\n\n const wsServer = new WebSocketServer({\n server: httpServer,\n path,\n })\n\n return useServer(\n {\n schema,\n onError(_ctx, message, errors) {\n logger.error('GraphQL subscriptions server error', { message, errors })\n },\n onConnect: async (ctx) => {\n const connectionEstablished = 'Subscription connection established'\n if (!verifyToken) {\n logger.info(connectionEstablished)\n return true\n }\n\n const token = extractToken({ connectRequest: ctx.extra.request, connectionParams: ctx.connectionParams })\n if (!token) {\n if (requireAuth) {\n logger.error('No auth token was supplied with the websocket connection')\n return false\n }\n logger.info(connectionEstablished)\n return true\n }\n\n try {\n const claims = await verifyToken(getHost(ctx.extra.request), token)\n ctx.extra.claims = claims as unknown as undefined\n logger.info(connectionEstablished, {\n claims: pick(claims, jwtClaimsToLog),\n })\n return true\n } catch (error) {\n logger.error('Failed to verify subscription connection auth token', { error })\n return false\n }\n },\n onSubscribe: async (ctx) => {\n if (!verifyToken) return\n const token = extractToken({ connectRequest: ctx.extra.request, connectionParams: ctx.connectionParams })\n if (!token) {\n if (requireAuth) {\n logger.error('No auth token was supplied with the websocket connection')\n ctx.extra.socket.close(CloseCode.Forbidden, 'Forbidden')\n }\n return\n }\n try {\n await verifyToken(getHost(ctx.extra.request), token)\n } catch (error) {\n logger.warn('Subscription connection auth token is no longer valid', { claims: pick(ctx.extra.claims, jwtClaimsToLog), error })\n ctx.extra.socket.close(CloseCode.Forbidden, 'Forbidden')\n }\n },\n onDisconnect({ extra: { claims } }) {\n logger.info('Subscription connection disconnected', { claims: pick(claims, jwtClaimsToLog) })\n },\n context: async (ctx) => {\n return createSubscriptionContext({\n connectRequest: ctx.extra.request,\n connectionParams: ctx.connectionParams,\n claims: ctx.extra.claims as JwtPayload | undefined,\n })\n },\n onOperation(_ctx, id, _payload, args) {\n logSubscriptionOperation({\n id,\n args,\n logLevel: operationLogLevel,\n resolveLogger: resolveSubscriptionOperationLogger,\n includeDeprecatedElements,\n })\n },\n onNext(_ctx, id, _payload, args, { data, ...result }) {\n logSubscriptionOperation({ id, args, logLevel: operationLogLevel, result, resolveLogger: resolveSubscriptionOperationLogger })\n },\n },\n wsServer,\n )\n}\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import type { IncomingMessage } from 'http';
2
+ import type { ExtractSubscriptionToken } from './context';
2
3
  /**
3
4
  * Extracts a token from a connection parameter named `authorization` or `Authorization`.
4
5
  * The expected format of the parameter value is: Bearer <token>, consistent with an HTTP Authorization header.
@@ -7,4 +8,5 @@ import type { IncomingMessage } from 'http';
7
8
  * @returns
8
9
  */
9
10
  export declare function extractTokenFromConnectionParams(connectionParams?: Readonly<Record<string, unknown>>): string | undefined;
11
+ export declare const defaultExtractSubscriptionToken: ExtractSubscriptionToken;
10
12
  export declare function getHost(request: IncomingMessage): string;
@@ -1,4 +1,5 @@
1
1
  import type { IncomingMessage } from 'http';
2
+ import type { ExtractSubscriptionToken } from './context.mjs';
2
3
  /**
3
4
  * Extracts a token from a connection parameter named `authorization` or `Authorization`.
4
5
  * The expected format of the parameter value is: Bearer <token>, consistent with an HTTP Authorization header.
@@ -7,4 +8,5 @@ import type { IncomingMessage } from 'http';
7
8
  * @returns
8
9
  */
9
10
  export declare function extractTokenFromConnectionParams(connectionParams?: Readonly<Record<string, unknown>>): string | undefined;
11
+ export declare const defaultExtractSubscriptionToken: ExtractSubscriptionToken;
10
12
  export declare function getHost(request: IncomingMessage): string;
@@ -1,4 +1,5 @@
1
1
  import type { IncomingMessage } from 'http';
2
+ import type { ExtractSubscriptionToken } from './context';
2
3
  /**
3
4
  * Extracts a token from a connection parameter named `authorization` or `Authorization`.
4
5
  * The expected format of the parameter value is: Bearer <token>, consistent with an HTTP Authorization header.
@@ -7,4 +8,5 @@ import type { IncomingMessage } from 'http';
7
8
  * @returns
8
9
  */
9
10
  export declare function extractTokenFromConnectionParams(connectionParams?: Readonly<Record<string, unknown>>): string | undefined;
11
+ export declare const defaultExtractSubscriptionToken: ExtractSubscriptionToken;
10
12
  export declare function getHost(request: IncomingMessage): string;
@@ -13,12 +13,14 @@ function extractTokenFromConnectionParams(connectionParams) {
13
13
  return undefined;
14
14
  return bearerTokenValue.substring(7);
15
15
  }
16
+ const defaultExtractSubscriptionToken = ({ connectionParams }) => extractTokenFromConnectionParams(connectionParams);
16
17
  function getHost(request) {
17
18
  const proxyHostHeader = request.headers['x-forwarded-host'];
18
19
  const host = Array.isArray(proxyHostHeader) ? (proxyHostHeader[0] ?? undefined) : (proxyHostHeader ?? request.headers.host ?? undefined);
19
20
  return host ?? 'subscriptions';
20
21
  }
21
22
 
23
+ exports.defaultExtractSubscriptionToken = defaultExtractSubscriptionToken;
22
24
  exports.extractTokenFromConnectionParams = extractTokenFromConnectionParams;
23
25
  exports.getHost = getHost;
24
26
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sources":["../../src/subscriptions/utils.ts"],"sourcesContent":["import type { IncomingMessage } from 'http'\n\n/**\n * Extracts a token from a connection parameter named `authorization` or `Authorization`.\n * The expected format of the parameter value is: Bearer <token>, consistent with an HTTP Authorization header.\n * Apollo Sandbox sets an `Authorization` connection parameter when you specify an HTTP Authorization header via the UI.\n * @param connectionParams\n * @returns\n */\nexport function extractTokenFromConnectionParams(connectionParams?: Readonly<Record<string, unknown>>) {\n const bearerTokenValue = (connectionParams?.authorization ?? connectionParams?.Authorization) as string | undefined\n if (!bearerTokenValue?.startsWith('Bearer ')) return undefined\n return bearerTokenValue.substring(7)\n}\n\nexport function getHost(request: IncomingMessage) {\n const proxyHostHeader = request.headers['x-forwarded-host']\n const host = Array.isArray(proxyHostHeader) ? (proxyHostHeader[0] ?? undefined) : (proxyHostHeader ?? request.headers.host ?? undefined)\n return host ?? 'subscriptions'\n}\n"],"names":[],"mappings":";;AAEA;;;;;;AAMG;AACG,SAAU,gCAAgC,CAAC,gBAAoD,EAAA;IACnG,MAAM,gBAAgB,IAAI,gBAAgB,EAAE,aAAa,IAAI,gBAAgB,EAAE,aAAa,CAAuB;AACnH,IAAA,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,SAAS,CAAC;AAAE,QAAA,OAAO,SAAS;AAC9D,IAAA,OAAO,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC;AACtC;AAEM,SAAU,OAAO,CAAC,OAAwB,EAAA;IAC9C,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC;AAC3D,IAAA,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,IAAI,SAAS,KAAK,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,SAAS,CAAC;IACxI,OAAO,IAAI,IAAI,eAAe;AAChC;;;;;"}
1
+ {"version":3,"file":"utils.js","sources":["../../src/subscriptions/utils.ts"],"sourcesContent":["import type { IncomingMessage } from 'http'\nimport type { ExtractSubscriptionToken } from './context'\n\n/**\n * Extracts a token from a connection parameter named `authorization` or `Authorization`.\n * The expected format of the parameter value is: Bearer <token>, consistent with an HTTP Authorization header.\n * Apollo Sandbox sets an `Authorization` connection parameter when you specify an HTTP Authorization header via the UI.\n * @param connectionParams\n * @returns\n */\nexport function extractTokenFromConnectionParams(connectionParams?: Readonly<Record<string, unknown>>) {\n const bearerTokenValue = (connectionParams?.authorization ?? connectionParams?.Authorization) as string | undefined\n if (!bearerTokenValue?.startsWith('Bearer ')) return undefined\n return bearerTokenValue.substring(7)\n}\n\nexport const defaultExtractSubscriptionToken: ExtractSubscriptionToken = ({ connectionParams }) =>\n extractTokenFromConnectionParams(connectionParams)\n\nexport function getHost(request: IncomingMessage) {\n const proxyHostHeader = request.headers['x-forwarded-host']\n const host = Array.isArray(proxyHostHeader) ? (proxyHostHeader[0] ?? undefined) : (proxyHostHeader ?? request.headers.host ?? undefined)\n return host ?? 'subscriptions'\n}\n"],"names":[],"mappings":";;AAGA;;;;;;AAMG;AACG,SAAU,gCAAgC,CAAC,gBAAoD,EAAA;IACnG,MAAM,gBAAgB,IAAI,gBAAgB,EAAE,aAAa,IAAI,gBAAgB,EAAE,aAAa,CAAuB;AACnH,IAAA,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,SAAS,CAAC;AAAE,QAAA,OAAO,SAAS;AAC9D,IAAA,OAAO,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC;AACtC;AAEO,MAAM,+BAA+B,GAA6B,CAAC,EAAE,gBAAgB,EAAE,KAC5F,gCAAgC,CAAC,gBAAgB;AAE7C,SAAU,OAAO,CAAC,OAAwB,EAAA;IAC9C,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC;AAC3D,IAAA,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,IAAI,SAAS,KAAK,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,SAAS,CAAC;IACxI,OAAO,IAAI,IAAI,eAAe;AAChC;;;;;;"}
@@ -11,11 +11,12 @@ function extractTokenFromConnectionParams(connectionParams) {
11
11
  return undefined;
12
12
  return bearerTokenValue.substring(7);
13
13
  }
14
+ const defaultExtractSubscriptionToken = ({ connectionParams }) => extractTokenFromConnectionParams(connectionParams);
14
15
  function getHost(request) {
15
16
  const proxyHostHeader = request.headers['x-forwarded-host'];
16
17
  const host = Array.isArray(proxyHostHeader) ? (proxyHostHeader[0] ?? undefined) : (proxyHostHeader ?? request.headers.host ?? undefined);
17
18
  return host ?? 'subscriptions';
18
19
  }
19
20
 
20
- export { extractTokenFromConnectionParams, getHost };
21
+ export { defaultExtractSubscriptionToken, extractTokenFromConnectionParams, getHost };
21
22
  //# sourceMappingURL=utils.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.mjs","sources":["../../src/subscriptions/utils.ts"],"sourcesContent":["import type { IncomingMessage } from 'http'\n\n/**\n * Extracts a token from a connection parameter named `authorization` or `Authorization`.\n * The expected format of the parameter value is: Bearer <token>, consistent with an HTTP Authorization header.\n * Apollo Sandbox sets an `Authorization` connection parameter when you specify an HTTP Authorization header via the UI.\n * @param connectionParams\n * @returns\n */\nexport function extractTokenFromConnectionParams(connectionParams?: Readonly<Record<string, unknown>>) {\n const bearerTokenValue = (connectionParams?.authorization ?? connectionParams?.Authorization) as string | undefined\n if (!bearerTokenValue?.startsWith('Bearer ')) return undefined\n return bearerTokenValue.substring(7)\n}\n\nexport function getHost(request: IncomingMessage) {\n const proxyHostHeader = request.headers['x-forwarded-host']\n const host = Array.isArray(proxyHostHeader) ? (proxyHostHeader[0] ?? undefined) : (proxyHostHeader ?? request.headers.host ?? undefined)\n return host ?? 'subscriptions'\n}\n"],"names":[],"mappings":"AAEA;;;;;;AAMG;AACG,SAAU,gCAAgC,CAAC,gBAAoD,EAAA;IACnG,MAAM,gBAAgB,IAAI,gBAAgB,EAAE,aAAa,IAAI,gBAAgB,EAAE,aAAa,CAAuB;AACnH,IAAA,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,SAAS,CAAC;AAAE,QAAA,OAAO,SAAS;AAC9D,IAAA,OAAO,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC;AACtC;AAEM,SAAU,OAAO,CAAC,OAAwB,EAAA;IAC9C,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC;AAC3D,IAAA,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,IAAI,SAAS,KAAK,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,SAAS,CAAC;IACxI,OAAO,IAAI,IAAI,eAAe;AAChC;;;;"}
1
+ {"version":3,"file":"utils.mjs","sources":["../../src/subscriptions/utils.ts"],"sourcesContent":["import type { IncomingMessage } from 'http'\nimport type { ExtractSubscriptionToken } from './context'\n\n/**\n * Extracts a token from a connection parameter named `authorization` or `Authorization`.\n * The expected format of the parameter value is: Bearer <token>, consistent with an HTTP Authorization header.\n * Apollo Sandbox sets an `Authorization` connection parameter when you specify an HTTP Authorization header via the UI.\n * @param connectionParams\n * @returns\n */\nexport function extractTokenFromConnectionParams(connectionParams?: Readonly<Record<string, unknown>>) {\n const bearerTokenValue = (connectionParams?.authorization ?? connectionParams?.Authorization) as string | undefined\n if (!bearerTokenValue?.startsWith('Bearer ')) return undefined\n return bearerTokenValue.substring(7)\n}\n\nexport const defaultExtractSubscriptionToken: ExtractSubscriptionToken = ({ connectionParams }) =>\n extractTokenFromConnectionParams(connectionParams)\n\nexport function getHost(request: IncomingMessage) {\n const proxyHostHeader = request.headers['x-forwarded-host']\n const host = Array.isArray(proxyHostHeader) ? (proxyHostHeader[0] ?? undefined) : (proxyHostHeader ?? request.headers.host ?? undefined)\n return host ?? 'subscriptions'\n}\n"],"names":[],"mappings":"AAGA;;;;;;AAMG;AACG,SAAU,gCAAgC,CAAC,gBAAoD,EAAA;IACnG,MAAM,gBAAgB,IAAI,gBAAgB,EAAE,aAAa,IAAI,gBAAgB,EAAE,aAAa,CAAuB;AACnH,IAAA,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,SAAS,CAAC;AAAE,QAAA,OAAO,SAAS;AAC9D,IAAA,OAAO,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC;AACtC;AAEO,MAAM,+BAA+B,GAA6B,CAAC,EAAE,gBAAgB,EAAE,KAC5F,gCAAgC,CAAC,gBAAgB;AAE7C,SAAU,OAAO,CAAC,OAAwB,EAAA;IAC9C,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC;AAC3D,IAAA,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,IAAI,SAAS,KAAK,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,SAAS,CAAC;IACxI,OAAO,IAAI,IAAI,eAAe;AAChC;;;;"}