-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathauth.js
More file actions
82 lines (78 loc) · 2.78 KB
/
Copy pathauth.js
File metadata and controls
82 lines (78 loc) · 2.78 KB
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
let authToken = '';
function decodeJWT(token) {
try {
const parts = token.split('.');
if (parts.length !== 3) return null;
const payload = Buffer.from(parts[1], 'base64').toString('utf8');
return JSON.parse(payload);
} catch (error) {
console.error('Failed to decode JWT:', error);
return null;
}
}
/**
* Performs a simple server-to-server authentication using the x-token header. This request is how you can
* convert a project API key into a valid "admin" auth token used for MCP requests. It will also cache the
* auth token until it is close to expiring, and ensure that the x-token header is valid before any auth can
* occur.
*
* @param {*} req
* @param {*} res
* @param {*} next
* @returns
*/
export async function authenticate(req, res, next) {
// They must ALWAYS provide an x-token header.
if (!req.headers['x-token'] && !req.headers['x-admin-key']) {
res.sendStatus(401);
return;
}
// The x-token header must match our project key.
if (
(req.headers['x-token'] && req.headers['x-token'] !== process.env.PROJECT_KEY) ||
(req.headers['x-admin-key'] && req.headers['x-admin-key'] !== process.env.ADMIN_KEY)
) {
res.sendStatus(401);
return;
}
// If we have a cached auth token, ensure it is still valid and not close to expiring.
if (authToken) {
const decoded = decodeJWT(authToken);
if (!decoded || !decoded.exp || (Date.now() / 1000) >= (decoded.exp - 60)) {
authToken = '';
}
}
// Check if we have a cached auth token.
if (authToken) {
req.authToken = authToken;
return next();
}
// Fetch a new auth token from the UAG server.
const auth = {
grant_type: 'client_credentials',
};
const projectName = process.env.PROJECT ? process.env.PROJECT.split('/').at(-1) : null;
if (process.env.PROJECT_KEY && projectName) {
auth.client_id = `${projectName}-x-token`;
auth.client_secret = process.env.PROJECT_KEY;
}
else if (process.env.ADMIN_KEY) {
// OSS deployments have a fixed project name of "formio-oss", and the UAG
// token endpoint requires the client_id to be prefixed with the project name.
auth.client_id = 'formio-oss-x-admin-key';
auth.client_secret = process.env.ADMIN_KEY;
}
let fetchUrl = process.env.UAG_SERVER || process.env.BASE_URL;
if (projectName) {
fetchUrl += `/${projectName}`;
}
fetchUrl += '/auth/token';
const resp = await fetch(fetchUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(auth),
});
const data = await resp.json();
authToken = req.authToken = data.access_token;
next();
}