@metaplay/metaplay-auth 1.0.1 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.1.1] - 2024-02-23
4
+
5
+ ### Changed
6
+
7
+ * Bumped version for package re-publishing.
8
+
9
+ ## [1.1.0] - 2024-02-22
10
+
11
+ ### Added
12
+
13
+ * Added support for inexplicit token refresh flow.
14
+
3
15
  ## [1.0.1] - 2024-02-08
4
16
 
5
17
  ### Changed
package/dist/auth.js CHANGED
@@ -2,18 +2,34 @@
2
2
  import express from 'express';
3
3
  import open from 'open';
4
4
  import * as crypto from 'crypto';
5
- import { createServer } from 'net';
5
+ import jwt from 'jsonwebtoken';
6
+ import jwkToPem from 'jwk-to-pem';
7
+ import { Configuration, WellknownApi } from '@ory/client';
6
8
  import { setSecret, getSecret, removeSecret } from './secret_store.js';
9
+ import { createServer } from 'net';
7
10
  import { logger } from './logging.js';
8
11
  // oauth2 client details (maybe move these to be discovered from some online location to make changes easier to manage?)
9
- const clientId = 'c16ea663-ced3-46c6-8f85-38c9681fe1f0';
10
- const authorizationEndpoint = 'https://auth.metaplay.dev/oauth2/auth';
11
- const tokenEndpoint = 'https://auth.metaplay.dev/oauth2/token';
12
+ const clientId = '1acc28dc-d1a4-447c-ba5d-7acbcfaae98d';
13
+ const baseURL = 'https://auth.metaplay.dev';
14
+ const authorizationEndpoint = `${baseURL}/oauth2/auth`;
15
+ const tokenEndpoint = `${baseURL}/oauth2/token`;
16
+ const wellknownApi = new WellknownApi(new Configuration({
17
+ basePath: baseURL,
18
+ }));
19
+ const tokenList = ['id_token', 'access_token', 'refresh_token']; // List of compulsory tokens
20
+ /**
21
+ * A helper function which generates a code verifier and challenge for exchaning code from Ory server.
22
+ * @returns
23
+ */
12
24
  function generateCodeVerifierAndChallenge() {
13
25
  const verifier = crypto.randomBytes(32).toString('hex');
14
26
  const challenge = crypto.createHash('sha256').update(verifier).digest('base64url');
15
27
  return { verifier, challenge };
16
28
  }
29
+ /**
30
+ * A helper function which finds an local available port to listen on.
31
+ * @returns A promise that resolves to an available port.
32
+ */
17
33
  async function findAvailablePort() {
18
34
  return await new Promise((resolve, reject) => {
19
35
  // Ports need to be in sync with oauth2 client callbacks.
@@ -43,6 +59,9 @@ async function findAvailablePort() {
43
59
  tryNextPort();
44
60
  });
45
61
  }
62
+ /**
63
+ * Log in and save tokens to the local secret store.
64
+ */
46
65
  export async function loginAndSaveTokens() {
47
66
  // Find an available port to listen on.
48
67
  const availablePort = await findAvailablePort();
@@ -56,64 +75,236 @@ export async function loginAndSaveTokens() {
56
75
  const error = req.query.error;
57
76
  const errorDescription = req.query.error_description;
58
77
  if (error) {
59
- console.log(`Error logging in. Received the following error:\n\n${error}: ${errorDescription}`);
78
+ console.error(`Error logging in. Received the following error:\n\n${error}: ${errorDescription}`);
60
79
  res.send(`Authentication failed: ${error}: ${errorDescription}`);
61
80
  server.close();
62
81
  process.exit(1);
63
82
  }
64
- const code = req.query.code;
65
- logger.debug(`Received callback request with code ${code}. Preparing to exchange for tokens...`);
66
- const tokens = await exchangeCodeForTokens(state, redirectUri, verifier, code);
67
- logger.debug(`Received tokens ${JSON.stringify(tokens)}. Storing them...`);
68
- await setSecret('id_token', tokens.id_token);
69
- await setSecret('access_token', tokens.access_token);
70
- console.log('Login tokens stored successfully.');
71
- // TODO: Could return a pre-generated HTML page instead of text?
72
- res.send('Authentication successful! You can close this window.');
73
- server.close();
74
- console.log('You are now logged in and can call the other commands.');
83
+ try {
84
+ const code = req.query.code;
85
+ logger.debug(`Received callback request with code ${code}. Preparing to exchange for tokens...`);
86
+ const tokens = await getTokensWithAuthorizationCode(state, redirectUri, verifier, code);
87
+ // TODO: Could return a pre-generated HTML page instead of text?
88
+ res.send('Authentication successful! You can close this window.');
89
+ await saveTokens(tokens);
90
+ console.log('You are now logged in and can call the other commands.');
91
+ }
92
+ catch (error) {
93
+ if (error instanceof Error) {
94
+ console.error(`Error: ${error.message}`);
95
+ }
96
+ }
97
+ finally {
98
+ server.close();
99
+ }
75
100
  process.exit(0);
76
101
  });
77
102
  // Start the server.
78
103
  const server = app.listen(availablePort, () => {
79
- const authorizationUrl = `${authorizationEndpoint}?response_type=code&client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&code_challenge=${challenge}&code_challenge_method=S256&scope=openid&state=${encodeURIComponent(state)}`;
104
+ const authorizationUrl = `${authorizationEndpoint}?response_type=code&client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&code_challenge=${challenge}&code_challenge_method=S256&scope=openid offline_access&state=${encodeURIComponent(state)}`;
80
105
  console.log(`Attempting to open a browser to log in. If a browser did not open up, you can copy-paste the following URL to authenticate:\n\n${authorizationUrl}\n`);
81
106
  void open(authorizationUrl);
82
107
  logger.debug(`Listening on port ${availablePort} and waiting for callback...`);
83
108
  });
84
109
  }
110
+ /**
111
+ * Refresh access and ID token with a refresh token.
112
+ */
113
+ export async function extendCurrentSession() {
114
+ try {
115
+ const tokens = await loadTokens();
116
+ logger.debug('Validating access token...');
117
+ if (await validateToken(tokens.access_token)) {
118
+ // Access token is not expired, return to the caller
119
+ logger.debug('Access token is valid, return to the caller.');
120
+ return;
121
+ }
122
+ logger.debug('Access token is no longer valid, trying to extend the current session with a refresh token.');
123
+ const refreshedTokens = await extendCurrentSessionWithRefreshToken(tokens.refresh_token);
124
+ await saveTokens(refreshedTokens);
125
+ }
126
+ catch (error) {
127
+ if (error instanceof Error) {
128
+ console.error(error.message);
129
+ }
130
+ process.exit(1);
131
+ }
132
+ }
133
+ /**
134
+ * Make HTTP request to the token endpoint for a new set of tokens using the refresh token.
135
+ * @param refreshToken: The refresh token to use to get a new set of tokens.
136
+ * @returns A promise that resolves to a new set of tokens.
137
+ */
138
+ async function extendCurrentSessionWithRefreshToken(refreshToken) {
139
+ const params = new URLSearchParams({
140
+ grant_type: 'refresh_token',
141
+ refresh_token: refreshToken,
142
+ scope: 'openid offline_access',
143
+ client_id: clientId
144
+ });
145
+ logger.debug('Refreshing tokens...');
146
+ // Send a POST request to refresh tokens
147
+ const response = await fetch(tokenEndpoint, {
148
+ method: 'POST',
149
+ headers: {
150
+ 'Content-Type': 'application/x-www-form-urlencoded',
151
+ },
152
+ body: params.toString(),
153
+ });
154
+ // Check if the response is OK
155
+ if (!response.ok) {
156
+ const responseJSON = await response.json();
157
+ logger.error('Failed to refresh tokens.');
158
+ logger.error(`Error Type: ${responseJSON.error}`);
159
+ logger.error(`Error Description: ${responseJSON.error_description}`);
160
+ logger.debug('Attempt to clear local state and remove any dangling tokens...');
161
+ await removeTokens();
162
+ logger.debug('Local state cleared and tokens removed.');
163
+ throw new Error('Failed extending current session, exiting. Please log in again.');
164
+ }
165
+ return await response.json();
166
+ }
167
+ /**
168
+ * Make HTTP request to the token endpoint for a new set of tokens (Authorization Code Flow).
169
+ * @param state
170
+ * @param redirectUri
171
+ * @param verifier
172
+ * @param code
173
+ * @returns
174
+ */
175
+ async function getTokensWithAuthorizationCode(state, redirectUri, verifier, code) {
176
+ try {
177
+ const response = await fetch(tokenEndpoint, {
178
+ method: 'POST',
179
+ headers: {
180
+ 'Content-Type': 'application/x-www-form-urlencoded'
181
+ },
182
+ body: `grant_type=authorization_code&code=${code}&redirect_uri=${encodeURIComponent(redirectUri)}&client_id=${clientId}&code_verifier=${verifier}&state=${encodeURIComponent(state)}`
183
+ });
184
+ return await response.json();
185
+ }
186
+ catch (error) {
187
+ if (error instanceof Error) {
188
+ logger.error(`Error exchanging code for tokens: ${error.message}`);
189
+ }
190
+ return {};
191
+ }
192
+ }
193
+ /**
194
+ * Load tokens from the local secret store.
195
+ */
85
196
  export async function loadTokens() {
86
- const idToken = await getSecret('id_token');
87
- const accessToken = await getSecret('access_token');
88
- if (idToken == null || accessToken == null) {
89
- throw new Error('Metaplay tokens not found. Are you logged in?');
197
+ try {
198
+ const idToken = await getSecret('id_token');
199
+ const accessToken = await getSecret('access_token');
200
+ const refreshToken = await getSecret('refresh_token');
201
+ if (idToken == null || accessToken == null || refreshToken == null) {
202
+ throw new Error('Metaplay tokens not found. Are you logged in?');
203
+ }
204
+ return {
205
+ id_token: idToken,
206
+ access_token: accessToken,
207
+ refresh_token: refreshToken
208
+ };
209
+ }
210
+ catch (error) {
211
+ if (error instanceof Error) {
212
+ throw new Error(`Error loading tokens: ${error.message}`);
213
+ }
214
+ throw error;
215
+ }
216
+ }
217
+ /**
218
+ * Save tokens to the local secret store.
219
+ * @param tokens The tokens to save.
220
+ */
221
+ export async function saveTokens(tokens) {
222
+ try {
223
+ logger.debug('Received new tokens, verifying...');
224
+ // Check if all tokens are present
225
+ for (const tokenName of tokenList) {
226
+ if (tokens[tokenName] === undefined) {
227
+ throw new Error(`Metaplay token ${tokenName} not found. Please log in again and make sure all checkboxes of permissions are selected before proceeding.`);
228
+ }
229
+ }
230
+ logger.debug('Token verification completed, storing tokens...');
231
+ await setSecret('id_token', tokens.id_token);
232
+ await setSecret('access_token', tokens.access_token);
233
+ await setSecret('refresh_token', tokens.refresh_token);
234
+ logger.debug('Tokens successfully stored.');
235
+ await showTokenInfo(tokens.access_token);
236
+ // await showTokenInfo(tokens.id_token)
237
+ }
238
+ catch (error) {
239
+ if (error instanceof Error) {
240
+ throw new Error(`Failed to save tokens: ${error.message}`);
241
+ }
242
+ throw error;
90
243
  }
91
- return {
92
- id_token: idToken,
93
- access_token: accessToken
94
- };
95
244
  }
245
+ /**
246
+ * Remove tokens from the local secret store.
247
+ */
96
248
  export async function removeTokens() {
97
249
  try {
98
250
  await removeSecret('id_token');
99
251
  logger.debug('Removed id_token.');
100
252
  await removeSecret('access_token');
101
253
  logger.debug('Removed access_token.');
254
+ await removeSecret('refresh_token');
255
+ logger.debug('Removed refresh_token.');
102
256
  }
103
257
  catch (error) {
104
258
  if (error instanceof Error) {
105
- throw new Error(`Could not remove tokens: ${error.message}`);
259
+ throw new Error(`Error removing tokens: ${error.message}`);
106
260
  }
261
+ throw error;
107
262
  }
108
263
  }
109
- async function exchangeCodeForTokens(state, redirectUri, verifier, code) {
110
- const response = await fetch(tokenEndpoint, {
111
- method: 'POST',
112
- headers: {
113
- 'Content-Type': 'application/x-www-form-urlencoded'
114
- },
115
- body: `grant_type=authorization_code&code=${code}&redirect_uri=${encodeURIComponent(redirectUri)}&client_id=${clientId}&code_verifier=${verifier}&state=${encodeURIComponent(state)}`
116
- });
117
- return await response.json();
264
+ /**
265
+ * This function validates a token by checking its signature, expiration and issuer.
266
+ * @param token The token being validated
267
+ * @returns return if token is valid, throw errors otherwise
268
+ */
269
+ async function validateToken(token) {
270
+ try {
271
+ // Decode the token
272
+ const completeTokenData = jwt.decode(token, { complete: true });
273
+ // Handle junk data.
274
+ if (!completeTokenData) {
275
+ throw new Error('Invalid token');
276
+ }
277
+ // Get the JSON web keys from the wellknown API.
278
+ const res = await wellknownApi.discoverJsonWebKeys();
279
+ const jwk = res.data.keys?.find((key) => key.kid === completeTokenData.header.kid);
280
+ if (!jwk) {
281
+ throw new Error('No public key found for token signature');
282
+ }
283
+ // Convert to PEM format.
284
+ const pem = jwkToPem(jwk);
285
+ // Verify the token signature, expiration and decode it.
286
+ jwt.verify(token, pem, { issuer: baseURL, ignoreExpiration: false });
287
+ return true;
288
+ }
289
+ catch (error) {
290
+ if (error instanceof Error) {
291
+ logger.info(`Token not valid: ${error.message}`);
292
+ }
293
+ return false;
294
+ }
295
+ }
296
+ /**
297
+ * A helper function which shows token info after being fully decoded.
298
+ * @param token The token to show info for.
299
+ */
300
+ async function showTokenInfo(token) {
301
+ logger.debug('Showing access token info...');
302
+ // Decode the token
303
+ const completeTokenData = jwt.decode(token, { complete: true });
304
+ // Handle junk data.
305
+ if (!completeTokenData) {
306
+ throw new Error('Token is corrupted');
307
+ }
308
+ logger.debug(JSON.stringify(completeTokenData));
118
309
  }
119
310
  //# sourceMappingURL=auth.js.map
package/dist/auth.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
- import { loginAndSaveTokens, loadTokens, removeTokens } from './auth.js';
3
+ import { loginAndSaveTokens, extendCurrentSession, loadTokens, removeTokens } from './auth.js';
4
4
  import { StackAPI } from './stackapi.js';
5
- import { validateTokens } from './utils.js';
6
5
  import { setLogLevel } from './logging.js';
7
6
  import { exit } from 'process';
8
7
  const program = new Command();
@@ -44,6 +43,9 @@ program.command('logout')
44
43
  });
45
44
  program.command('show-tokens')
46
45
  .description('show loaded tokens')
46
+ .hook('preAction', async () => {
47
+ await extendCurrentSession();
48
+ })
47
49
  .action(async () => {
48
50
  try {
49
51
  // TODO: Could detect if not logged in and fail more gracefully?
@@ -64,12 +66,12 @@ program.command('get-kubeconfig')
64
66
  .option('-o, --organization <organization>', 'organization name (e.g. metaplay)')
65
67
  .option('-p, --project <project>', 'project name (e.g. idler)')
66
68
  .option('-e, --environment <environment>', 'environment name (e.g. develop)')
69
+ .hook('preAction', async () => {
70
+ await extendCurrentSession();
71
+ })
67
72
  .action(async (gameserver, options) => {
68
73
  try {
69
74
  const tokens = await loadTokens();
70
- if (!validateTokens(tokens)) {
71
- throw new Error('Invalid tokens; try to refresh?');
72
- }
73
75
  if (!gameserver && !(options.organization && options.project && options.environment)) {
74
76
  throw new Error('Could not determine a deployment to fetch the KubeConfigs from. You need to specify either a gameserver or an organization, project, and environment');
75
77
  }
@@ -96,15 +98,15 @@ program.command('get-aws-credentials')
96
98
  .option('-p, --project <project>', 'project name (e.g. idler)')
97
99
  .option('-e, --environment <environment>', 'environment name (e.g. develop)')
98
100
  .option('-f, --format <format>', 'output format (json or env)', 'json')
101
+ .hook('preAction', async () => {
102
+ await extendCurrentSession();
103
+ })
99
104
  .action(async (gameserver, options) => {
100
105
  try {
101
106
  if (options.format !== 'json' && options.format !== 'env') {
102
107
  throw new Error('Invalid format; must be one of json or env');
103
108
  }
104
109
  const tokens = await loadTokens();
105
- if (!validateTokens(tokens)) {
106
- throw new Error('Invalid tokens; try to refresh?');
107
- }
108
110
  if (!gameserver && !(options.organization && options.project && options.environment)) {
109
111
  throw new Error('Could not determine a deployment to fetch the AWS credentials from. You need to specify either a gameserver or an organization, project, and environment');
110
112
  }
@@ -140,12 +142,12 @@ program.command('get-environment')
140
142
  .option('-o, --organization <organization>', 'organization name (e.g. metaplay)')
141
143
  .option('-p, --project <project>', 'project name (e.g. idler)')
142
144
  .option('-e, --environment <environment>', 'environment name (e.g. develop)')
145
+ .hook('preAction', async () => {
146
+ await extendCurrentSession();
147
+ })
143
148
  .action(async (gameserver, options) => {
144
149
  try {
145
150
  const tokens = await loadTokens();
146
- if (!validateTokens(tokens)) {
147
- throw new Error('Invalid tokens; try to refresh?');
148
- }
149
151
  if (!gameserver && !(options.organization && options.project && options.environment)) {
150
152
  throw new Error('Could not determine a deployment to fetch environment details from. You need to specify either a gameserver or an organization, project, and environment');
151
153
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/utils.js CHANGED
@@ -38,23 +38,4 @@ export function getGameserverAdminUrl(uri) {
38
38
  const domain = uri.substring(hostname.length + 1);
39
39
  return `${hostname}-admin.${domain}`;
40
40
  }
41
- export function validateTokens(tokens) {
42
- if (tokens.id_token == null) {
43
- throw new Error('id_token is missing');
44
- }
45
- if (tokens.access_token == null) {
46
- throw new Error('access_token is missing');
47
- }
48
- if (!isJwtTokenExpired(tokens.id_token)) {
49
- throw new Error('id_token is expired');
50
- }
51
- return true;
52
- }
53
- function isJwtTokenExpired(token) {
54
- const base64Url = token.split('.')[1];
55
- const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
56
- const payload = JSON.parse(Buffer.from(base64, 'base64').toString());
57
- const now = Math.floor(Date.now() / 1000);
58
- return payload.exp > now;
59
- }
60
41
  //# sourceMappingURL=utils.js.map
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,uBAAuB,CAAE,GAAW,EAAE,OAA4B;IAChF,OAAO,EAAE,CAAA;AACX,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAE,MAAc;IACzC,MAAM,SAAS,GAAG,+EAA+E,CAAA;IAEjG,OAAO,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;AAC/B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAE,SAAiB;IACnD,IAAI;QACF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAA;QAE9B,oEAAoE;QACpE,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA,CAAC,2BAA2B;QACpE,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAA;QAC7B,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,IAAI,CAAA;QAE7B,yDAAyD;QACzD,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;QAE9C,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAA;KAC5C;IAAC,OAAO,KAAK,EAAE;QACd,MAAM,IAAI,KAAK,CAAC,aAAa,CAAC,CAAA;KAC/B;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAE,GAAW;IAChD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAE5B,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;IACzB,MAAM,MAAM,GAAG,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IAEjD,OAAO,GAAG,QAAQ,UAAU,MAAM,EAAE,CAAA;AACtC,CAAC;AAED,MAAM,UAAU,cAAc,CAAE,MAAqC;IACnE,IAAI,MAAM,CAAC,QAAQ,IAAI,IAAI,EAAE;QAC3B,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAA;KACvC;IACD,IAAI,MAAM,CAAC,YAAY,IAAI,IAAI,EAAE;QAC/B,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAA;KAC3C;IAED,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE;QACvC,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAA;KACvC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,iBAAiB,CAAE,KAAa;IACvC,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;IACrC,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IAC9D,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAA;IAEpE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;IACzC,OAAO,OAAO,CAAC,GAAG,GAAG,GAAG,CAAA;AAC1B,CAAC"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,uBAAuB,CAAE,GAAW,EAAE,OAA4B;IAChF,OAAO,EAAE,CAAA;AACX,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAE,MAAc;IACzC,MAAM,SAAS,GAAG,+EAA+E,CAAA;IAEjG,OAAO,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;AAC/B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAE,SAAiB;IACnD,IAAI;QACF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAA;QAE9B,oEAAoE;QACpE,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA,CAAC,2BAA2B;QACpE,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAA;QAC7B,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,IAAI,CAAA;QAE7B,yDAAyD;QACzD,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;QAE9C,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAA;KAC5C;IAAC,OAAO,KAAK,EAAE;QACd,MAAM,IAAI,KAAK,CAAC,aAAa,CAAC,CAAA;KAC/B;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAE,GAAW;IAChD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAE5B,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;IACzB,MAAM,MAAM,GAAG,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IAEjD,OAAO,GAAG,QAAQ,UAAU,MAAM,EAAE,CAAA;AACtC,CAAC"}
package/package.json CHANGED
@@ -1,21 +1,30 @@
1
1
  {
2
2
  "name": "@metaplay/metaplay-auth",
3
3
  "description": "Utility CLI for authenticating with the Metaplay Auth and making authenticated calls to infrastructure endpoints.",
4
- "version": "1.0.1",
4
+ "version": "1.1.1",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",
7
7
  "homepage": "https://metaplay.io",
8
8
  "bin": "dist/index.js",
9
+ "scripts": {
10
+ "dev": "tsx src/index.ts",
11
+ "prepublish": "tsc"
12
+ },
9
13
  "publishConfig": {
10
14
  "access": "public"
11
15
  },
12
16
  "devDependencies": {
17
+ "@metaplay/eslint-config": "workspace:*",
18
+ "@metaplay/typescript-config": "workspace:*",
19
+ "@ory/client": "^1.4.5",
13
20
  "@types/express": "^4.17.21",
14
- "@types/node": "^20.11.10",
21
+ "@types/jsonwebtoken": "^9.0.5",
22
+ "@types/jwk-to-pem": "^2.0.3",
23
+ "@types/node": "^20.11.19",
24
+ "jsonwebtoken": "^9.0.2",
25
+ "jwk-to-pem": "^2.0.5",
15
26
  "typescript": "^5.1.6",
16
- "tsx": "^4.7.0",
17
- "@metaplay/typescript-config": "1.0.0",
18
- "@metaplay/eslint-config": "1.0.0"
27
+ "tsx": "^4.7.0"
19
28
  },
20
29
  "dependencies": {
21
30
  "commander": "^11.1.0",
@@ -25,9 +34,5 @@
25
34
  "open": "^10.0.2",
26
35
  "process": "^0.11.10",
27
36
  "tslog": "^4.9.2"
28
- },
29
- "scripts": {
30
- "dev": "tsx src/index.ts",
31
- "prepublish": "tsc"
32
37
  }
33
- }
38
+ }
package/src/auth.ts CHANGED
@@ -2,21 +2,37 @@
2
2
  import express from 'express'
3
3
  import open from 'open'
4
4
  import * as crypto from 'crypto'
5
- import { createServer } from 'net'
5
+ import jwt from 'jsonwebtoken'
6
+ import jwkToPem from 'jwk-to-pem'
7
+ import { Configuration, WellknownApi } from '@ory/client'
6
8
  import { setSecret, getSecret, removeSecret } from './secret_store.js'
9
+ import { createServer } from 'net'
7
10
  import { logger } from './logging.js'
8
11
 
9
12
  // oauth2 client details (maybe move these to be discovered from some online location to make changes easier to manage?)
10
13
  const clientId = 'c16ea663-ced3-46c6-8f85-38c9681fe1f0'
11
- const authorizationEndpoint = 'https://auth.metaplay.dev/oauth2/auth'
12
- const tokenEndpoint = 'https://auth.metaplay.dev/oauth2/token'
14
+ const baseURL = 'https://auth.metaplay.dev'
15
+ const authorizationEndpoint = `${baseURL}/oauth2/auth`
16
+ const tokenEndpoint = `${baseURL}/oauth2/token`
17
+ const wellknownApi = new WellknownApi(new Configuration({
18
+ basePath: baseURL,
19
+ }))
20
+ const tokenList: string[] = ['id_token', 'access_token', 'refresh_token'] // List of compulsory tokens
13
21
 
22
+ /**
23
+ * A helper function which generates a code verifier and challenge for exchaning code from Ory server.
24
+ * @returns
25
+ */
14
26
  function generateCodeVerifierAndChallenge (): { verifier: string, challenge: string } {
15
27
  const verifier: string = crypto.randomBytes(32).toString('hex')
16
28
  const challenge: string = crypto.createHash('sha256').update(verifier).digest('base64url')
17
29
  return { verifier, challenge }
18
30
  }
19
31
 
32
+ /**
33
+ * A helper function which finds an local available port to listen on.
34
+ * @returns A promise that resolves to an available port.
35
+ */
20
36
  async function findAvailablePort (): Promise<number> {
21
37
  return await new Promise((resolve, reject) => {
22
38
  // Ports need to be in sync with oauth2 client callbacks.
@@ -51,6 +67,9 @@ async function findAvailablePort (): Promise<number> {
51
67
  })
52
68
  }
53
69
 
70
+ /**
71
+ * Log in and save tokens to the local secret store.
72
+ */
54
73
  export async function loginAndSaveTokens () {
55
74
  // Find an available port to listen on.
56
75
  const availablePort = await findAvailablePort()
@@ -67,32 +86,37 @@ export async function loginAndSaveTokens () {
67
86
  const errorDescription: string | undefined = req.query.error_description as string
68
87
 
69
88
  if (error) {
70
- console.log(`Error logging in. Received the following error:\n\n${error}: ${errorDescription}`)
89
+ console.error(`Error logging in. Received the following error:\n\n${error}: ${errorDescription}`)
71
90
  res.send(`Authentication failed: ${error}: ${errorDescription}`)
72
91
  server.close()
73
92
  process.exit(1)
74
93
  }
75
94
 
76
- const code: string | undefined = req.query.code as string
77
- logger.debug(`Received callback request with code ${code}. Preparing to exchange for tokens...`)
95
+ try {
96
+ const code: string | undefined = req.query.code as string
97
+ logger.debug(`Received callback request with code ${code}. Preparing to exchange for tokens...`)
78
98
 
79
- const tokens = await exchangeCodeForTokens(state, redirectUri, verifier, code)
80
- logger.debug(`Received tokens ${JSON.stringify(tokens)}. Storing them...`)
99
+ const tokens = await getTokensWithAuthorizationCode(state, redirectUri, verifier, code)
100
+ // TODO: Could return a pre-generated HTML page instead of text?
101
+ res.send('Authentication successful! You can close this window.')
81
102
 
82
- await setSecret('id_token', tokens.id_token)
83
- await setSecret('access_token', tokens.access_token)
103
+ await saveTokens(tokens)
104
+
105
+ console.log('You are now logged in and can call the other commands.')
106
+ } catch (error) {
107
+ if (error instanceof Error) {
108
+ console.error(`Error: ${error.message}`)
109
+ }
110
+ } finally {
111
+ server.close()
112
+ }
84
113
 
85
- console.log('Login tokens stored successfully.')
86
- // TODO: Could return a pre-generated HTML page instead of text?
87
- res.send('Authentication successful! You can close this window.')
88
- server.close()
89
- console.log('You are now logged in and can call the other commands.')
90
114
  process.exit(0)
91
115
  })
92
116
 
93
117
  // Start the server.
94
118
  const server = app.listen(availablePort, () => {
95
- const authorizationUrl: string = `${authorizationEndpoint}?response_type=code&client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&code_challenge=${challenge}&code_challenge_method=S256&scope=openid&state=${encodeURIComponent(state)}`
119
+ const authorizationUrl: string = `${authorizationEndpoint}?response_type=code&client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&code_challenge=${challenge}&code_challenge_method=S256&scope=openid offline_access&state=${encodeURIComponent(state)}`
96
120
  console.log(`Attempting to open a browser to log in. If a browser did not open up, you can copy-paste the following URL to authenticate:\n\n${authorizationUrl}\n`)
97
121
  void open(authorizationUrl)
98
122
 
@@ -100,40 +124,232 @@ export async function loginAndSaveTokens () {
100
124
  })
101
125
  }
102
126
 
103
- export async function loadTokens (): Promise<{ id_token: string | null, access_token: string | null }> {
104
- const idToken = await getSecret('id_token')
105
- const accessToken = await getSecret('access_token')
127
+ /**
128
+ * Refresh access and ID token with a refresh token.
129
+ */
130
+ export async function extendCurrentSession (): Promise<void> {
131
+ try {
132
+ const tokens = await loadTokens()
133
+
134
+ logger.debug('Validating access token...')
135
+ if (await validateToken(tokens.access_token)) {
136
+ // Access token is not expired, return to the caller
137
+ logger.debug('Access token is valid, return to the caller.')
138
+ return
139
+ }
140
+
141
+ logger.debug('Access token is no longer valid, trying to extend the current session with a refresh token.')
106
142
 
107
- if (idToken == null || accessToken == null) {
108
- throw new Error('Metaplay tokens not found. Are you logged in?')
143
+ const refreshedTokens = await extendCurrentSessionWithRefreshToken(tokens.refresh_token)
144
+
145
+ await saveTokens(refreshedTokens)
146
+ } catch (error) {
147
+ if (error instanceof Error) {
148
+ console.error(error.message)
149
+ }
150
+ process.exit(1)
109
151
  }
152
+ }
110
153
 
111
- return {
112
- id_token: idToken,
113
- access_token: accessToken
154
+ /**
155
+ * Make HTTP request to the token endpoint for a new set of tokens using the refresh token.
156
+ * @param refreshToken: The refresh token to use to get a new set of tokens.
157
+ * @returns A promise that resolves to a new set of tokens.
158
+ */
159
+ async function extendCurrentSessionWithRefreshToken (refreshToken: string): Promise<{ id_token: string, access_token: string, refresh_token: string }> {
160
+ // TODO: similiar to the todo task in getTokensWithAuthorizationCode, http request can be handled by ory/client.
161
+ const params = new URLSearchParams({
162
+ grant_type: 'refresh_token',
163
+ refresh_token: refreshToken,
164
+ scope: 'openid offline_access',
165
+ client_id: clientId
166
+ })
167
+
168
+ logger.debug('Refreshing tokens...')
169
+
170
+ // Send a POST request to refresh tokens
171
+ const response = await fetch(tokenEndpoint, {
172
+ method: 'POST',
173
+ headers: {
174
+ 'Content-Type': 'application/x-www-form-urlencoded',
175
+ },
176
+ body: params.toString(),
177
+ })
178
+
179
+ // Check if the response is OK
180
+ if (!response.ok) {
181
+ const responseJSON = await response.json()
182
+
183
+ logger.error('Failed to refresh tokens.')
184
+ logger.error(`Error Type: ${responseJSON.error}`)
185
+ logger.error(`Error Description: ${responseJSON.error_description}`)
186
+
187
+ logger.debug('Attempt to clear local state and remove any dangling tokens...')
188
+ await removeTokens()
189
+ logger.debug('Local state cleared and tokens removed.')
190
+
191
+ throw new Error('Failed extending current session, exiting. Please log in again.')
192
+ }
193
+
194
+ return await response.json()
195
+ }
196
+
197
+ /**
198
+ * Make HTTP request to the token endpoint for a new set of tokens (Authorization Code Flow).
199
+ * @param state
200
+ * @param redirectUri
201
+ * @param verifier
202
+ * @param code
203
+ * @returns
204
+ */
205
+ async function getTokensWithAuthorizationCode (state: string, redirectUri: string, verifier: string, code: string): Promise<{ id_token: string, access_token: string, refresh_token: string } | {}> {
206
+ // TODO: the authorication code exchange flow might be better to be handled by ory/client, could check if there's any useful toosl there.
207
+ try {
208
+ const response = await fetch(tokenEndpoint, {
209
+ method: 'POST',
210
+ headers: {
211
+ 'Content-Type': 'application/x-www-form-urlencoded'
212
+ },
213
+ body: `grant_type=authorization_code&code=${code}&redirect_uri=${encodeURIComponent(redirectUri)}&client_id=${clientId}&code_verifier=${verifier}&state=${encodeURIComponent(state)}`
214
+ })
215
+
216
+ return await response.json()
217
+ } catch (error) {
218
+ if (error instanceof Error) {
219
+ logger.error(`Error exchanging code for tokens: ${error.message}`)
220
+ }
221
+
222
+ return {}
114
223
  }
115
224
  }
116
225
 
226
+ /**
227
+ * Load tokens from the local secret store.
228
+ */
229
+ export async function loadTokens (): Promise<{ id_token: string, access_token: string, refresh_token: string }> {
230
+ try {
231
+ const idToken = await getSecret('id_token')
232
+ const accessToken = await getSecret('access_token')
233
+ const refreshToken = await getSecret('refresh_token')
234
+
235
+ if (idToken == null || accessToken == null || refreshToken == null) {
236
+ throw new Error('Metaplay tokens not found. Are you logged in?')
237
+ }
238
+
239
+ return {
240
+ id_token: idToken,
241
+ access_token: accessToken,
242
+ refresh_token: refreshToken
243
+ }
244
+ } catch (error) {
245
+ if (error instanceof Error) {
246
+ throw new Error(`Error loading tokens: ${error.message}`)
247
+ }
248
+ throw error
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Save tokens to the local secret store.
254
+ * @param tokens The tokens to save.
255
+ */
256
+ export async function saveTokens (tokens: Record<string, string>): Promise<void> {
257
+ try {
258
+ logger.debug('Received new tokens, verifying...')
259
+
260
+ // Check if all tokens are present
261
+ for (const tokenName of tokenList) {
262
+ if (tokens[tokenName] === undefined) {
263
+ throw new Error(`Metaplay token ${tokenName} not found. Please log in again and make sure all checkboxes of permissions are selected before proceeding.`)
264
+ }
265
+ }
266
+
267
+ logger.debug('Token verification completed, storing tokens...')
268
+
269
+ await setSecret('id_token', tokens.id_token)
270
+ await setSecret('access_token', tokens.access_token)
271
+ await setSecret('refresh_token', tokens.refresh_token)
272
+
273
+ logger.debug('Tokens successfully stored.')
274
+
275
+ await showTokenInfo(tokens.access_token)
276
+ // await showTokenInfo(tokens.id_token)
277
+ } catch (error) {
278
+ if (error instanceof Error) {
279
+ throw new Error(`Failed to save tokens: ${error.message}`)
280
+ }
281
+ throw error
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Remove tokens from the local secret store.
287
+ */
117
288
  export async function removeTokens (): Promise<void> {
118
289
  try {
119
290
  await removeSecret('id_token')
120
291
  logger.debug('Removed id_token.')
121
292
  await removeSecret('access_token')
122
293
  logger.debug('Removed access_token.')
294
+ await removeSecret('refresh_token')
295
+ logger.debug('Removed refresh_token.')
123
296
  } catch (error) {
124
297
  if (error instanceof Error) {
125
- throw new Error(`Could not remove tokens: ${error.message}`)
298
+ throw new Error(`Error removing tokens: ${error.message}`)
126
299
  }
300
+ throw error
127
301
  }
128
302
  }
129
303
 
130
- async function exchangeCodeForTokens (state: string, redirectUri: string, verifier: string, code: string): Promise<{ id_token: string, access_token: string }> {
131
- const response = await fetch(tokenEndpoint, {
132
- method: 'POST',
133
- headers: {
134
- 'Content-Type': 'application/x-www-form-urlencoded'
135
- },
136
- body: `grant_type=authorization_code&code=${code}&redirect_uri=${encodeURIComponent(redirectUri)}&client_id=${clientId}&code_verifier=${verifier}&state=${encodeURIComponent(state)}`
137
- })
138
- return await response.json()
304
+ /**
305
+ * This function validates a token by checking its signature, expiration and issuer.
306
+ * @param token The token being validated
307
+ * @returns return if token is valid, throw errors otherwise
308
+ */
309
+ async function validateToken (token: string): Promise<boolean> {
310
+ try {
311
+ // Decode the token
312
+ const completeTokenData = jwt.decode(token, { complete: true })
313
+
314
+ // Handle junk data.
315
+ if (!completeTokenData) {
316
+ throw new Error('Invalid token')
317
+ }
318
+
319
+ // Get the JSON web keys from the wellknown API.
320
+ const res = await wellknownApi.discoverJsonWebKeys()
321
+ const jwk = res.data.keys?.find((key) => key.kid === completeTokenData.header.kid)
322
+ if (!jwk) {
323
+ throw new Error('No public key found for token signature')
324
+ }
325
+ // Convert to PEM format.
326
+ const pem = jwkToPem(jwk as jwkToPem.JWK)
327
+
328
+ // Verify the token signature, expiration and decode it.
329
+ jwt.verify(token, pem, { issuer: baseURL, ignoreExpiration: false })
330
+
331
+ return true
332
+ } catch (error) {
333
+ if (error instanceof Error) {
334
+ logger.info(`Token not valid: ${error.message}`)
335
+ }
336
+ return false
337
+ }
338
+ }
339
+
340
+ /**
341
+ * A helper function which shows token info after being fully decoded.
342
+ * @param token The token to show info for.
343
+ */
344
+ async function showTokenInfo (token: string): Promise<void> {
345
+ logger.debug('Showing access token info...')
346
+ // Decode the token
347
+ const completeTokenData = jwt.decode(token, { complete: true })
348
+
349
+ // Handle junk data.
350
+ if (!completeTokenData) {
351
+ throw new Error('Token is corrupted')
352
+ }
353
+
354
+ logger.debug(JSON.stringify(completeTokenData))
139
355
  }
package/src/index.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander'
3
- import { loginAndSaveTokens, loadTokens, removeTokens } from './auth.js'
3
+ import { loginAndSaveTokens, extendCurrentSession, loadTokens, removeTokens } from './auth.js'
4
4
  import { StackAPI } from './stackapi.js'
5
- import { validateTokens } from './utils.js'
6
5
  import { setLogLevel } from './logging.js'
7
6
  import { exit } from 'process'
8
7
 
@@ -11,7 +10,7 @@ const program = new Command()
11
10
  program
12
11
  .name('metaplay-auth')
13
12
  .description('Authenticate with Metaplay and get AWS and Kubernetes credentials for game servers.')
14
- .version('1.0.1')
13
+ .version('1.1.1')
15
14
  .option('-d, --debug', 'enable debug output')
16
15
  .hook('preAction', (thisCommand) => {
17
16
  // Handle debug flag for all commands.
@@ -48,6 +47,9 @@ program.command('logout')
48
47
 
49
48
  program.command('show-tokens')
50
49
  .description('show loaded tokens')
50
+ .hook('preAction', async () => {
51
+ await extendCurrentSession()
52
+ })
51
53
  .action(async () => {
52
54
  try {
53
55
  // TODO: Could detect if not logged in and fail more gracefully?
@@ -68,14 +70,13 @@ program.command('get-kubeconfig')
68
70
  .option('-o, --organization <organization>', 'organization name (e.g. metaplay)')
69
71
  .option('-p, --project <project>', 'project name (e.g. idler)')
70
72
  .option('-e, --environment <environment>', 'environment name (e.g. develop)')
73
+ .hook('preAction', async () => {
74
+ await extendCurrentSession()
75
+ })
71
76
  .action(async (gameserver, options) => {
72
77
  try {
73
78
  const tokens = await loadTokens()
74
79
 
75
- if (!validateTokens(tokens)) {
76
- throw new Error('Invalid tokens; try to refresh?')
77
- }
78
-
79
80
  if (!gameserver && !(options.organization && options.project && options.environment)) {
80
81
  throw new Error('Could not determine a deployment to fetch the KubeConfigs from. You need to specify either a gameserver or an organization, project, and environment')
81
82
  }
@@ -105,6 +106,9 @@ program.command('get-aws-credentials')
105
106
  .option('-p, --project <project>', 'project name (e.g. idler)')
106
107
  .option('-e, --environment <environment>', 'environment name (e.g. develop)')
107
108
  .option('-f, --format <format>', 'output format (json or env)', 'json')
109
+ .hook('preAction', async () => {
110
+ await extendCurrentSession()
111
+ })
108
112
  .action(async (gameserver, options) => {
109
113
  try {
110
114
  if (options.format !== 'json' && options.format !== 'env') {
@@ -113,10 +117,6 @@ program.command('get-aws-credentials')
113
117
 
114
118
  const tokens = await loadTokens()
115
119
 
116
- if (!validateTokens(tokens)) {
117
- throw new Error('Invalid tokens; try to refresh?')
118
- }
119
-
120
120
  if (!gameserver && !(options.organization && options.project && options.environment)) {
121
121
  throw new Error('Could not determine a deployment to fetch the AWS credentials from. You need to specify either a gameserver or an organization, project, and environment')
122
122
  }
@@ -155,14 +155,13 @@ program.command('get-environment')
155
155
  .option('-o, --organization <organization>', 'organization name (e.g. metaplay)')
156
156
  .option('-p, --project <project>', 'project name (e.g. idler)')
157
157
  .option('-e, --environment <environment>', 'environment name (e.g. develop)')
158
+ .hook('preAction', async () => {
159
+ await extendCurrentSession()
160
+ })
158
161
  .action(async (gameserver, options) => {
159
162
  try {
160
163
  const tokens = await loadTokens()
161
164
 
162
- if (!validateTokens(tokens)) {
163
- throw new Error('Invalid tokens; try to refresh?')
164
- }
165
-
166
165
  if (!gameserver && !(options.organization && options.project && options.environment)) {
167
166
  throw new Error('Could not determine a deployment to fetch environment details from. You need to specify either a gameserver or an organization, project, and environment')
168
167
  }
package/src/utils.ts CHANGED
@@ -46,27 +46,3 @@ export function getGameserverAdminUrl (uri: string): string {
46
46
 
47
47
  return `${hostname}-admin.${domain}`
48
48
  }
49
-
50
- export function validateTokens (tokens: Record<string, string | null>): boolean {
51
- if (tokens.id_token == null) {
52
- throw new Error('id_token is missing')
53
- }
54
- if (tokens.access_token == null) {
55
- throw new Error('access_token is missing')
56
- }
57
-
58
- if (!isJwtTokenExpired(tokens.id_token)) {
59
- throw new Error('id_token is expired')
60
- }
61
-
62
- return true
63
- }
64
-
65
- function isJwtTokenExpired (token: string): boolean {
66
- const base64Url = token.split('.')[1]
67
- const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')
68
- const payload = JSON.parse(Buffer.from(base64, 'base64').toString())
69
-
70
- const now = Math.floor(Date.now() / 1000)
71
- return payload.exp > now
72
- }