-
Notifications
You must be signed in to change notification settings - Fork 11
/
jwt.js
118 lines (113 loc) · 3.19 KB
/
jwt.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
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
113
114
115
116
117
118
import { setNoCacheHeader } from './noCache';
import {
getProviderJWTSecret,
publicKey as verifySecret,
defaultAudience,
getToken,
issuer,
} from '../util/jwt';
import {
oAuthClientCollection as oAuthClientDbRef,
} from '../util/firebase';
const expressjwt = require('express-jwt');
const jwt = require('jsonwebtoken');
async function fetchProviderClientSecret(clientId) {
const spClient = await oAuthClientDbRef.doc(clientId).get();
if (!spClient.exists) throw new Error('INVALID_AZP');
const {
secret,
} = spClient.data();
return secret;
}
function checkPermissions(inputScopes, target) {
let scopes = inputScopes;
if (!scopes) return false;
if (!Array.isArray(scopes)) scopes = scopes.split(' ');
let targets = target.split(':');
if (targets.length > 1) {
const permission = targets[0];
const subScopes = targets[1].split('.');
let lastScope = `${permission}:${subScopes[0]}`;
const list = [permission, lastScope];
for (let i = 1; i < subScopes.length; i += 1) {
const currentScope = `${lastScope}.${subScopes[i]}`;
list.push(currentScope);
lastScope = currentScope;
}
targets = list;
}
if (scopes.find(scope => targets.includes(scope))) return true;
return false;
}
export const jwtAuth = (
permission = 'read',
secret = verifySecret,
{ audience = defaultAudience } = {},
) => async (req, res, next) => {
setNoCacheHeader(res);
try {
const token = getToken(req);
const decoded = jwt.decode(token);
if (decoded.azp) {
const clientSecret = await fetchProviderClientSecret(decoded.azp);
secret = getProviderJWTSecret(clientSecret); // eslint-disable-line no-param-reassign
}
} catch (err) {
// no op
}
expressjwt({
secret,
getToken,
audience,
issuer,
})(req, res, (e) => {
if (e instanceof expressjwt.UnauthorizedError) {
res.status(401).send('LOGIN_NEEDED');
return;
}
if (!req.user
|| (permission && !req.user.permissions && !req.user.scope)
|| ((permission && !checkPermissions(req.user.permissions, permission))
&& (permission && !checkPermissions(req.user.scope, permission)))) {
res.status(401).send('INVALID_GRANT');
return;
}
next(e);
});
};
export const jwtOptionalAuth = (
permission = 'read',
secret = verifySecret,
{ audience = defaultAudience } = {},
) => async (req, res, next) => {
setNoCacheHeader(res);
try {
const token = getToken(req);
const decoded = jwt.decode(token);
if (decoded.azp) {
const clientSecret = await fetchProviderClientSecret(decoded.azp);
secret = getProviderJWTSecret(clientSecret); // eslint-disable-line no-param-reassign
}
} catch (err) {
// no op
}
expressjwt({
credentialsRequired: false,
secret,
getToken,
audience,
issuer,
})(req, res, (e) => {
if (e instanceof expressjwt.UnauthorizedError) {
next();
return;
}
if (!req.user
|| (permission && !req.user.permissions && !req.user.scope)
|| ((permission && !checkPermissions(req.user.permissions, permission))
&& (permission && !checkPermissions(req.user.scope, permission)))) {
req.user = undefined;
}
next(e);
});
};