-
Notifications
You must be signed in to change notification settings - Fork 7
/
access-control-directives.ts
112 lines (104 loc) · 3.41 KB
/
access-control-directives.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { makeExecutableSchema } from '@graphql-tools/schema';
import { gql } from 'graphql-tag';
import type { MissingPermissionsResolverInfo } from '../lib/index.js';
import { applyDirectivesToSchema, hasPermissions, auth } from '../lib/index.js';
const yourTypeDefs = [
gql`
type Query {
authenticated: Boolean @auth
throwIfMissingPermissions: Int @hasPermissions(permissions: ["x", "y"])
handleMissingPermissions: [String!]
@hasPermissions(permissions: ["x", "y"], policy: RESOLVER)
}
type Mutation {
setAuthenticated(isAuthenticated: Boolean): Boolean!
setPermissions(permissions: [String!]): [String!]
}
`,
];
const state: {
grantedPermissions: string[] | null;
isAuthenticated: boolean | null;
} = {
grantedPermissions: null,
isAuthenticated: null,
};
const schema = applyDirectivesToSchema(
[hasPermissions, auth],
makeExecutableSchema({
resolvers: {
Mutation: {
setAuthenticated: (
_,
{ isAuthenticated }: { isAuthenticated: boolean | null },
): boolean | null => {
state.isAuthenticated = isAuthenticated;
return isAuthenticated;
},
setPermissions: (
_,
{ permissions }: { permissions: string[] | null },
): string[] | null => {
state.grantedPermissions = permissions;
return permissions;
},
},
Query: {
authenticated: (): boolean => state.isAuthenticated || false,
handleMissingPermissions: (
_,
__,
___,
{ missingPermissions }: MissingPermissionsResolverInfo,
): string[] | null => missingPermissions || null,
throwIfMissingPermissions: (): number => 123,
},
},
typeDefs: [
...yourTypeDefs,
...auth.getTypeDefs(),
...hasPermissions.getTypeDefs(),
],
}),
);
type Context = ReturnType<typeof auth.createDirectiveContext> &
ReturnType<typeof hasPermissions.createDirectiveContext>;
const server = new ApolloServer({
schema,
});
startStandaloneServer(server, {
context: async (expressContext): Promise<Context> => {
// This example allows for state to be passed in the headers:
// - authorization: any value results in authenticated
// - permissions: json-serialized array of strings or null
//
// However to make it easier to test using the built-in playground
// one can use the mutations to set state:
// - setAuthenticated(isAuthenticated: Boolean)
// - setPermissions(permissions: [String!])
if (state.isAuthenticated === null) {
const { authorization } = expressContext.req.headers;
state.isAuthenticated = !!authorization;
}
if (state.grantedPermissions === null) {
const { permissions } = expressContext.req.headers;
state.grantedPermissions = permissions
? JSON.parse(Array.isArray(permissions) ? permissions[0] : permissions)
: null;
}
return {
...auth.createDirectiveContext({
isAuthenticated: state.isAuthenticated,
}),
...hasPermissions.createDirectiveContext({
grantedPermissions: state.grantedPermissions || undefined,
}),
};
},
listen: { port: 4000 },
}).then(({ url }) => {
// eslint-disable-next-line no-console
console.log(`🚀 Server ready at ${url}`);
});