-
Notifications
You must be signed in to change notification settings - Fork 2
/
refreshTokenPlugin.js
82 lines (73 loc) · 2.77 KB
/
refreshTokenPlugin.js
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
import graphileUtilsPkg from 'graphile-utils';
const { makeExtendSchemaPlugin, gql } = graphileUtilsPkg;
import jwtPkg from 'jsonwebtoken';
const { sign } = jwtPkg;
const { ACCESS_TOKEN_SECRET, REFRESH_TOKEN_SECRET } = process.env
const RefreshTokenPlugin = makeExtendSchemaPlugin((_, {pgJwtSignOptions}) => ({
typeDefs: gql`
input AuthenticateInput {
email: String!,
password: String!
},
extend type Mutation {
authenticate(input: AuthenticateInput!): String
}
`,
resolvers: {
Mutation: {
authenticate: async (_, args, context) => {
const { email, password } = args.input;
try {
// Because this is auth, we use rootPgPool, which uses PostGraphile's role
// We don't use pgClient, b/c that's 'too late' - transaction & role is already
// set based on the incoming JWT.
// Note: this means we must manually pass rootPgPool in via resolver context
const { rows: [tokenPlaintext] } = await context.rootPgPool.query(
` SELECT users.*
FROM demo_private.generate_token_plaintext($1, $2) users
WHERE NOT (users is null)
LIMIT 1
`,
[email, password]
);
if (!tokenPlaintext) { // unable to auth/invalid creds
throw new Error("not authenticated");
}
console.log('>>', tokenPlaintext)
const { sub } = tokenPlaintext;
const accessToken = signToken(sub, pgJwtSignOptions, ACCESS_TOKEN_SECRET);
const refreshToken = signToken(sub, {...pgJwtSignOptions, expiresIn: '7 days'}, REFRESH_TOKEN_SECRET);
sendRefreshToken(context.res, refreshToken);
return accessToken;
} catch (e) {
console.error(e);
throw e;
}
}
}
}
}));
export default RefreshTokenPlugin;
export const signToken = (sub, pgJwtSignOptions, secret) => {
const token = {
sub, // the sub, aka 'subscriber id', comes from account.person_id
role: 'demo_authenticated' // _must_ match the role in SQL as defined by generate_token_plaintext() function
}
return sign(token, secret,
Object.assign({}, pgJwtSignOptions,
token.aud || (pgJwtSignOptions && pgJwtSignOptions.audience)
? null : { audience: "postgraphile" },
token.iss || (pgJwtSignOptions && pgJwtSignOptions.issuer)
? null : { issuer: "postgraphile" },
token.exp || (pgJwtSignOptions && pgJwtSignOptions.expiresIn)
? null : { expiresIn: "15 mins" }
)
);
}
export const sendRefreshToken = (res, token) => {
res.cookie('qid', token, {
httpOnly: true,
sameSite: true, // if you're on a single origin, this may help prevent CSRF attacks
path: "/access_token"
});
}