@graphql-mesh/plugin-jwt-auth
Version:
447 lines (434 loc) • 19.8 kB
TypeScript
import { Plugin, YogaInitialContext, Instrumentation as Instrumentation$2 } from 'graphql-yoga';
import { DisposableSymbols } from '@whatwg-node/disposablestack';
import { ExecutionRequest, ExecutionResult, Executor, MaybePromise as MaybePromise$1, Maybe } from '@graphql-tools/utils';
import { GraphQLSchema, OperationTypeNode, GraphQLFieldResolver, GraphQLResolveInfo, GraphQLOutputType, GraphQLError, SelectionSetNode, FragmentDefinitionNode, FieldNode, ExecutionResult as ExecutionResult$1, SelectionNode } from 'graphql';
import DataLoader from 'dataloader';
import { GraphQLResolveInfo as GraphQLResolveInfo$1, GraphQLOutputType as GraphQLOutputType$1 } from 'graphql/type';
import { MaybePromise } from '@whatwg-node/promise-helpers';
import { MeshFetch, KeyValueCache, MeshFetchRequestInit, Logger as Logger$1 } from '@graphql-mesh/types';
import { FetchInstrumentation } from '@graphql-mesh/utils';
import { JwtPluginOptions, JWTExtendContextFields } from '@graphql-yoga/plugin-jwt';
export { ExtractTokenFunction, GetSigningKeyFunction, JWTExtendContextFields, JwtPluginOptions, createInlineSigningKeyProvider, createRemoteJwksSigningKeyProvider, extractFromConnectionParams, extractFromCookie, extractFromHeader } from '@graphql-yoga/plugin-jwt';
type MaybeLazy<T> = T | (() => T);
type AttributeValue = any;
type Attributes = AttributeValue[] | {
[key: string | number]: AttributeValue;
};
interface LogWriter {
write(level: LogLevel, attrs: Attributes | null | undefined, msg: string | null | undefined): void | Promise<void>;
flush?(): void | Promise<void>;
}
type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error';
interface LoggerOptions {
/**
* The minimum log level to log.
*
* Providing `false` will disable all logging.
*
* Provided function will always be invoked to get the current log level.
*
* @default env.LOG_LEVEL || env.DEBUG ? 'debug' : 'info'
*/
level?: MaybeLazy<LogLevel | false>;
/** A prefix to include in every log's message. */
prefix?: string;
/**
* The attributes to include in all logs. Is mainly used to pass the parent
* attributes when creating {@link Logger.child child loggers}.
*/
attrs?: Attributes;
/**
* The log writers to use when writing logs.
*
* @default env.LOG_JSON ? [new JSONLogWriter()] : [new ConsoleLogWriter()]
*/
writers?: [LogWriter, ...LogWriter[]];
}
declare class Logger implements AsyncDisposable {
#private;
constructor(opts?: LoggerOptions);
/** The prefix that's prepended to each log message. */
get prefix(): string | undefined;
/**
* The attributes that are added to each log. If the log itself contains
* attributes with keys existing in {@link attrs}, the log's attributes will
* override.
*/
get attrs(): Attributes | undefined;
/** The current {@link LogLevel} of the logger. You can change the level using the {@link setLevel} method. */
get level(): false | LogLevel;
/**
* Sets the new {@link LogLevel} of the logger. All subsequent logs, and {@link child child loggers} whose
* level did not change, will respect the new level.
*/
setLevel(level: MaybeLazy<LogLevel | false>): void;
write(level: LogLevel, attrs: Attributes | null | undefined, msg: string | null | undefined): void;
flush(): Promise<void> | undefined;
[DisposableSymbols.asyncDispose](): Promise<void | undefined>;
child(prefix: string): Logger;
child(attrs: Attributes, prefix?: string): Logger;
log(level: LogLevel): void;
log(level: LogLevel, attrs: MaybeLazy<Attributes>): void;
log(level: LogLevel, msg: string, ...interpol: unknown[]): void;
log(level: LogLevel, attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
trace(): void;
trace(attrs: MaybeLazy<Attributes>): void;
trace(msg: string, ...interpol: unknown[]): void;
trace(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
debug(): void;
debug(attrs: MaybeLazy<Attributes>): void;
debug(msg: string, ...interpol: unknown[]): void;
debug(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
info(): void;
info(attrs: MaybeLazy<Attributes>): void;
info(msg: string, ...interpol: unknown[]): void;
info(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
warn(): void;
warn(attrs: MaybeLazy<Attributes>): void;
warn(msg: string, ...interpol: unknown[]): void;
warn(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
error(): void;
error(attrs: MaybeLazy<Attributes>): void;
error(msg: string, ...interpol: unknown[]): void;
error(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
}
type TopicDataMap = Record<string, any>;
interface HivePubSub<Data extends TopicDataMap = TopicDataMap> {
/** @deprecated Please use {@link subscribedTopics} if implemented instead. This method will be removed in next major release. */
getEventNames(): Iterable<keyof Data>;
/** @important This method will be required starting next major release. */
subscribedTopics?(): Iterable<keyof Data>;
publish<Topic extends keyof Data>(topic: Topic, data: Data[Topic]): void;
subscribe<Topic extends keyof Data>(topic: Topic, listener: PubSubListener<Data, Topic>): number;
unsubscribe(subId: number): void;
asyncIterator<Topic extends keyof Data>(topic: Topic): AsyncIterable<Data[Topic]>;
/** @important This method will be required starting next major release. */
dispose?(): void;
/** @important This method will be required starting next major release. */
[DisposableSymbols.dispose]?(): void;
}
type PubSubListener<Data extends TopicDataMap, Topic extends keyof Data> = (data: Data[Topic]) => void;
interface TransportEntry<Options extends Record<string, any> = Record<string, any>> {
kind: string;
subgraph: string;
location?: string;
headers?: [string, string][];
options?: Options;
}
type SchemaTransform<TContext = Record<any, string>> = (originalWrappingSchema: GraphQLSchema, subschemaConfig: SubschemaConfig<any, any, any, TContext>) => GraphQLSchema;
type RequestTransform<T = Record<string, any>, TContext = Record<any, string>> = (originalRequest: ExecutionRequest, delegationContext: DelegationContext<TContext>, transformationContext: T) => ExecutionRequest;
type ResultTransform<T = Record<string, any>, TContext = Record<any, string>> = (originalResult: ExecutionResult, delegationContext: DelegationContext<TContext>, transformationContext: T) => ExecutionResult;
interface Transform<T = any, TContext = Record<string, any>> {
transformSchema?: SchemaTransform<TContext>;
transformRequest?: RequestTransform<T, TContext>;
transformResult?: ResultTransform<T, TContext>;
}
interface DelegationContext<TContext = Record<string, any>> {
subschema: GraphQLSchema | SubschemaConfig<any, any, any, TContext>;
subschemaConfig?: SubschemaConfig<any, any, any, TContext>;
targetSchema: GraphQLSchema;
operation: OperationTypeNode;
fieldName: string;
args?: Record<string, any>;
context?: TContext;
info?: GraphQLResolveInfo;
returnType: GraphQLOutputType;
onLocatedError?: (originalError: GraphQLError) => GraphQLError;
rootValue?: any;
transforms: Array<Transform<any, TContext>>;
transformedSchema: GraphQLSchema;
skipTypeMerging: boolean;
}
type DelegationPlanBuilder = (schema: GraphQLSchema, sourceSubschema: Subschema<any, any, any, any>, variableValues: Record<string, any>, fragments: Record<string, FragmentDefinitionNode>, fieldNodes: FieldNode[], context?: any, info?: GraphQLResolveInfo) => Array<Map<Subschema, SelectionSetNode>>;
interface ICreateProxyingResolverOptions<TContext = Record<string, any>> {
subschemaConfig: SubschemaConfig<any, any, any, TContext>;
operation?: OperationTypeNode;
fieldName?: string;
}
type CreateProxyingResolverFn<TContext = Record<string, any>> = (options: ICreateProxyingResolverOptions<TContext>) => GraphQLFieldResolver<any, TContext>;
interface BatchingOptions<K = any, V = any, C = K> {
extensionsReducer?: (mergedExtensions: Record<string, any>, request: ExecutionRequest) => Record<string, any>;
dataLoaderOptions?: DataLoader.Options<K, V, C>;
}
interface SubschemaConfig<K = any, V = any, C = K, TContext = Record<string, any>> {
name?: string;
schema: GraphQLSchema;
createProxyingResolver?: CreateProxyingResolverFn<TContext>;
rootValue?: any;
transforms?: Array<Transform<any, TContext>>;
merge?: Record<string, MergedTypeConfig<any, any, TContext>>;
executor?: Executor<TContext>;
batch?: boolean;
batchingOptions?: BatchingOptions<K, V, C>;
}
interface MergedTypeConfig<K = any, V = any, TContext = Record<string, any>> extends MergedTypeEntryPoint<K, V, TContext> {
entryPoints?: Array<MergedTypeEntryPoint>;
fields?: Record<string, MergedFieldConfig>;
canonical?: boolean;
}
interface MergedTypeEntryPoint<K = any, V = any, TContext = Record<string, any>> extends MergedTypeResolverOptions<K, V> {
selectionSet?: string;
key?: (originalResult: any) => K | PromiseLike<K>;
resolve?: MergedTypeResolver<TContext>;
}
interface MergedTypeResolverOptions<K = any, V = any> {
fieldName?: string;
args?: (originalResult: any) => Record<string, any>;
argsFromKeys?: (keys: ReadonlyArray<K>) => Record<string, any>;
valuesFromResults?: (results: any, keys: ReadonlyArray<K>) => Array<V>;
dataLoaderOptions?: DataLoader.Options<K, V>;
}
interface MergedFieldConfig {
selectionSet?: string;
computed?: boolean;
canonical?: boolean;
provides?: SelectionSetNode;
}
type MergedTypeResolver<TContext = Record<string, any>> = (originalResult: any, context: TContext, info: GraphQLResolveInfo, subschema: Subschema<any, any, any, TContext>, selectionSet: SelectionSetNode, key: any | undefined, type: GraphQLOutputType) => any;
interface ISubschema<K = any, V = any, C = K, TContext = Record<string, any>> extends SubschemaConfig<K, V, C, TContext> {
transformedSchema: GraphQLSchema;
}
declare class Subschema<K = any, V = any, C = K, TContext = Record<string, any>> implements ISubschema<K, V, C, TContext> {
name?: string;
schema: GraphQLSchema;
executor?: Executor<TContext>;
batch?: boolean;
batchingOptions?: BatchingOptions<K, V, C>;
createProxyingResolver?: CreateProxyingResolverFn<TContext>;
transforms: Array<Transform<any, TContext>>;
private _transformedSchema;
merge?: Record<string, MergedTypeConfig<any, any, TContext>>;
constructor(config: SubschemaConfig<K, V, C, TContext>);
get transformedSchema(): GraphQLSchema;
set transformedSchema(value: GraphQLSchema);
}
type Instrumentation$1 = {
/**
* Wrap each subgraph execution request. This can happen multiple time for the same graphql operation.
*/
subgraphExecute?: (payload: {
executionRequest: ExecutionRequest;
subgraphName: string;
}, wrapped: () => MaybePromise<void>) => MaybePromise<void>;
/**
* Wrap each supergraph schema loading.
*
* Note: this span is only available when an Async compatible context manager is available
*/
schema?: (payload: null, wrapped: () => MaybePromise<void>) => MaybePromise<void>;
};
declare module 'graphql' {
interface GraphQLResolveInfo {
executionRequest?: ExecutionRequest;
}
}
interface UnifiedGraphPlugin<TContext> {
onSubgraphExecute?: OnSubgraphExecuteHook<TContext>;
onDelegationPlan?: OnDelegationPlanHook<TContext>;
onDelegationStageExecute?: OnDelegationStageExecuteHook<TContext>;
}
type OnSubgraphExecuteHook<TContext = any> = (payload: OnSubgraphExecutePayload<TContext>) => MaybePromise$1<Maybe<OnSubgraphExecuteDoneHook | void>>;
interface OnSubgraphExecutePayload<TContext> {
subgraph: GraphQLSchema;
subgraphName: string;
transportEntry?: TransportEntry;
executionRequest: ExecutionRequest<any, TContext>;
setExecutionRequest(executionRequest: ExecutionRequest): void;
executor: Executor;
setExecutor(executor: Executor): void;
log: Logger;
}
interface OnSubgraphExecuteDonePayload {
result: AsyncIterable<ExecutionResult$1> | ExecutionResult$1;
setResult(result: AsyncIterable<ExecutionResult$1> | ExecutionResult$1): void;
}
type OnSubgraphExecuteDoneHook = (payload: OnSubgraphExecuteDonePayload) => MaybePromise$1<Maybe<OnSubgraphExecuteDoneResult | void>>;
type OnSubgraphExecuteDoneResultOnNext = (payload: OnSubgraphExecuteDoneOnNextPayload) => MaybePromise$1<void>;
interface OnSubgraphExecuteDoneOnNextPayload {
result: ExecutionResult$1;
setResult(result: ExecutionResult$1): void;
}
type OnSubgraphExecuteDoneResultOnEnd = () => MaybePromise$1<void>;
type OnSubgraphExecuteDoneResult = {
onNext?: OnSubgraphExecuteDoneResultOnNext;
onEnd?: OnSubgraphExecuteDoneResultOnEnd;
};
type OnDelegationPlanHook<TContext> = (payload: OnDelegationPlanHookPayload<TContext>) => Maybe<OnDelegationPlanDoneHook | void>;
interface OnDelegationPlanHookPayload<TContext> {
supergraph: GraphQLSchema;
subgraph: string;
sourceSubschema: Subschema<any, any, any, TContext>;
typeName: string;
variables: Record<string, any>;
fragments: Record<string, FragmentDefinitionNode>;
fieldNodes: SelectionNode[];
context: TContext;
log: Logger;
info?: GraphQLResolveInfo$1;
delegationPlanBuilder: DelegationPlanBuilder;
setDelegationPlanBuilder(delegationPlanBuilder: DelegationPlanBuilder): void;
}
type OnDelegationPlanDoneHook = (payload: OnDelegationPlanDonePayload) => Maybe<void>;
interface OnDelegationPlanDonePayload {
delegationPlan: ReturnType<DelegationPlanBuilder>;
setDelegationPlan: (delegationPlan: ReturnType<DelegationPlanBuilder>) => void;
}
type OnDelegationStageExecuteHook<TContext> = (payload: OnDelegationStageExecutePayload<TContext>) => Maybe<OnDelegationStageExecuteDoneHook>;
interface OnDelegationStageExecutePayload<TContext> {
object: any;
context: TContext;
info: GraphQLResolveInfo$1;
subgraph: string;
subschema: Subschema<any, any, any, TContext>;
selectionSet: SelectionSetNode;
key?: any;
type: GraphQLOutputType$1;
resolver: MergedTypeResolver<TContext>;
setResolver: (resolver: MergedTypeResolver<TContext>) => void;
typeName: string;
log: Logger;
}
type OnDelegationStageExecuteDoneHook = (payload: OnDelegationStageExecuteDonePayload) => void;
interface OnDelegationStageExecuteDonePayload {
result: any;
setResult: (result: any) => void;
}
interface GatewayConfigContext {
/**
* WHATWG compatible Fetch implementation.
*/
fetch: MeshFetch;
/**
* The logger to use throught Hive and its plugins.
*/
log: Logger;
/**
* Current working directory.
*/
cwd: string;
/**
* Event bus for pub/sub.
*/
pubsub?: HivePubSub;
/**
* Cache Storage
*/
cache?: KeyValueCache;
}
interface GatewayContext extends GatewayConfigContext, YogaInitialContext {
/**
* Environment agnostic HTTP headers provided with the request.
*/
headers: Record<string, string>;
/**
* Runtime context available within WebSocket connections.
*/
connectionParams: Record<string, string>;
}
type GatewayPlugin<TPluginContext extends Record<string, any> = Record<string, any>, TContext extends Record<string, any> = Record<string, any>> = Plugin<Partial<TPluginContext> & GatewayContext & TContext> & UnifiedGraphPlugin<Partial<TPluginContext> & GatewayContext & TContext> & {
onFetch?: OnFetchHook<Partial<TPluginContext> & TContext>;
onCacheGet?: OnCacheGetHook;
onCacheSet?: OnCacheSetHook;
onCacheDelete?: OnCacheDeleteHook;
/**
* An Instrumentation instance that will wrap each phases of the request pipeline.
* This should be used primarily as an observability tool (for monitoring, tracing, etc...).
*
* Note: The wrapped functions in instrumentation should always be called. Use hooks to
* conditionally skip a phase.
*/
instrumentation?: Instrumentation<TPluginContext & TContext & GatewayContext>;
};
interface OnFetchHookPayload<TContext> {
url: string;
setURL(url: URL | string): void;
options: MeshFetchRequestInit;
setOptions(options: MeshFetchRequestInit): void;
/**
* The context is not available in cases where "fetch" is done in
* order to pull a supergraph or do some internal work.
*
* The logger will be available in all cases.
*/
context: (GatewayContext & TContext) | {
log: Logger;
};
/** @deprecated Please use `log` from the {@link context} instead. */
logger: Logger$1;
info: GraphQLResolveInfo$1;
fetchFn: MeshFetch;
setFetchFn: (fetchFn: MeshFetch) => void;
executionRequest?: ExecutionRequest;
endResponse: (response$: MaybePromise$1<Response>) => void;
}
interface OnFetchHookDonePayload {
response: Response;
setResponse: (response: Response) => void;
}
type OnFetchHookDone = (payload: OnFetchHookDonePayload) => MaybePromise$1<void>;
type OnFetchHook<TContext> = (payload: OnFetchHookPayload<TContext>) => MaybePromise$1<void | OnFetchHookDone>;
type OnCacheGetHook = (payload: OnCacheGetHookEventPayload) => MaybePromise$1<OnCacheGetHookResult | void>;
interface OnCacheGetHookEventPayload {
cache: KeyValueCache;
key: string;
ttl?: number;
}
interface OnCacheGetHookResult {
onCacheHit?: OnCacheHitHook;
onCacheMiss?: OnCacheMissHook;
onCacheGetError?: OnCacheErrorHook;
}
type OnCacheErrorHook = (payload: OnCacheErrorHookPayload) => void;
interface OnCacheErrorHookPayload {
error: Error;
}
type OnCacheHitHook = (payload: OnCacheHitHookEventPayload) => void;
interface OnCacheHitHookEventPayload {
value: any;
}
type OnCacheMissHook = () => void;
type OnCacheSetHook = (payload: OnCacheSetHookEventPayload) => MaybePromise$1<OnCacheSetHookResult | void>;
interface OnCacheSetHookResult {
onCacheSetDone?: () => void;
onCacheSetError?: OnCacheErrorHook;
}
interface OnCacheSetHookEventPayload {
cache: KeyValueCache;
key: string;
value: any;
ttl?: number;
}
type OnCacheDeleteHook = (payload: OnCacheDeleteHookEventPayload) => MaybePromise$1<OnCacheDeleteHookResult | void>;
interface OnCacheDeleteHookResult {
onCacheDeleteDone?: () => void;
onCacheDeleteError?: OnCacheErrorHook;
}
interface OnCacheDeleteHookEventPayload {
cache: KeyValueCache;
key: string;
}
type Instrumentation<TContext extends Record<string, any>> = Instrumentation$2<TContext> & Instrumentation$1 & FetchInstrumentation;
type JWTAuthPluginOptions = JwtPluginOptions & {
forward?: {
payload?: boolean | string;
token?: boolean | string;
extensionsFieldName?: string;
};
};
/**
* This Yoga plugin is used to extract the forwarded (from Mesh gateway) the JWT token and claims.
* Use this plugin in your Yoga server to extract the JWT token and claims from the forwarded extensions.
*/
declare function useForwardedJWT(config: {
extensionsFieldName?: string;
extendContextFieldName?: string;
}): Plugin<{
jwt?: JWTExtendContextFields;
}>;
/**
* This Mesh Gateway plugin is used to extract the JWT token and payload from the request and forward it to the subgraph.
*/
declare function useJWT(options: JWTAuthPluginOptions): GatewayPlugin<{
jwt?: JWTExtendContextFields;
}>;
export { type JWTAuthPluginOptions, useJWT as default, useForwardedJWT, useJWT };