@metaplay/metaplay-auth 1.9.0 → 1.9.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.
Binary file
package/package.json CHANGED
@@ -1,7 +1,7 @@
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.9.0",
4
+ "version": "1.9.1",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",
7
7
  "homepage": "https://metaplay.io",
@@ -17,28 +17,29 @@
17
17
  },
18
18
  "devDependencies": {
19
19
  "@metaplay/eslint-config": "workspace:*",
20
- "@types/dockerode": "3.3.32",
21
- "@types/express": "5.0.0",
20
+ "@types/dockerode": "3.3.39",
21
+ "@types/express": "5.0.2",
22
22
  "@types/js-yaml": "4.0.9",
23
- "@types/jsonwebtoken": "9.0.8",
23
+ "@types/jsonwebtoken": "9.0.9",
24
24
  "@types/jwk-to-pem": "2.0.3",
25
- "@types/node": "22.13.5",
26
- "@types/semver": "7.5.8",
27
- "esbuild": "0.25.0",
28
- "tsx": "4.19.3",
29
- "typescript": "5.6.3",
30
- "vitest": "3.0.6",
31
- "@aws-sdk/client-ecr": "3.750.0",
25
+ "@types/node": "22.15.29",
26
+ "@types/semver": "7.7.0",
27
+ "esbuild": "0.25.5",
28
+ "tsx": "4.19.4",
29
+ "typescript": "5.8.3",
30
+ "vitest": "3.1.3",
31
+ "@aws-sdk/client-ecr": "3.810.0",
32
32
  "@kubernetes/client-node": "1.0.0-rc7",
33
- "@ory/client": "1.16.7",
33
+ "@ory/client": "1.20.11",
34
34
  "commander": "12.1.0",
35
- "dockerode": "4.0.2",
36
- "h3": "1.15.0",
35
+ "dockerode": "4.0.6",
36
+ "h3": "1.15.3",
37
37
  "js-yaml": "4.1.0",
38
38
  "jsonwebtoken": "9.0.2",
39
39
  "jwk-to-pem": "2.0.7",
40
40
  "open": "8.4.2",
41
- "semver": "7.7.1",
42
- "tslog": "4.9.3"
41
+ "semver": "7.7.2",
42
+ "tslog": "4.9.3",
43
+ "eslint": "9.31.0"
43
44
  }
44
45
  }
package/src/auth.ts CHANGED
@@ -1,12 +1,12 @@
1
1
 
2
2
  import { toNodeListener, createApp, defineEventHandler, getQuery, sendError } from 'h3'
3
- import jwt from 'jsonwebtoken'
4
- import jwkToPem from 'jwk-to-pem'
3
+ import jwt, { JwtPayload } from 'jsonwebtoken'
4
+
5
5
  import { randomBytes, createHash } from 'node:crypto'
6
6
  import { createServer } from 'node:http'
7
7
  import open from 'open'
8
8
 
9
- import { Configuration, WellknownApi, OidcApi } from '@ory/client'
9
+ import { Configuration, OidcApi } from '@ory/client'
10
10
 
11
11
  import { portalBaseUrl } from './config.js'
12
12
  import { logger } from './logging.js'
@@ -23,11 +23,6 @@ const clientId = 'c16ea663-ced3-46c6-8f85-38c9681fe1f0'
23
23
  const baseURL = 'https://auth.metaplay.dev'
24
24
  const authorizationEndpoint = `${baseURL}/oauth2/auth`
25
25
  const tokenEndpoint = `${baseURL}/oauth2/token`
26
- const wellknownApi = new WellknownApi(
27
- new Configuration({
28
- basePath: baseURL,
29
- })
30
- )
31
26
  const oidcApi = new OidcApi(
32
27
  new Configuration({
33
28
  basePath: baseURL,
@@ -233,8 +228,11 @@ export async function extendCurrentSession(): Promise<void> {
233
228
  try {
234
229
  const tokens = await loadTokens()
235
230
 
231
+ // Check whether the token has expired and needs to be refreshed.
232
+ // Note: We don't properly validate the token here as it's the servers that need to
233
+ // perform these checks; there is no benefit in doing it locally.
236
234
  logger.debug('Check if access token still valid...')
237
- if (await validateToken(tokens.access_token)) {
235
+ if (isJwtStillValid(tokens.access_token)) {
238
236
  // Access token is not expired, return to the caller
239
237
  logger.debug('Access token is valid, no need to refresh.')
240
238
  return
@@ -347,7 +345,7 @@ async function getTokensWithAuthorizationCode(
347
345
  if (error instanceof Error) {
348
346
  logger.error(`Error exchanging code for tokens: ${error.message}`)
349
347
  }
350
- // eslint-disable-next-line @typescript-eslint/only-throw-error
348
+
351
349
  throw error
352
350
  }
353
351
  }
@@ -368,7 +366,7 @@ export async function loadTokens(): Promise<TokenSet> {
368
366
  if (error instanceof Error) {
369
367
  throw new Error(`Error loading tokens: ${error.message}`)
370
368
  }
371
- // eslint-disable-next-line @typescript-eslint/only-throw-error
369
+
372
370
  throw error
373
371
  }
374
372
  }
@@ -397,7 +395,7 @@ export async function saveTokens(tokens: Record<string, string>): Promise<void>
397
395
  if (error instanceof Error) {
398
396
  throw new Error(`Failed to save tokens: ${error.message}`)
399
397
  }
400
- // eslint-disable-next-line @typescript-eslint/only-throw-error
398
+
401
399
  throw error
402
400
  }
403
401
  }
@@ -413,45 +411,52 @@ export async function removeTokens(): Promise<void> {
413
411
  if (error instanceof Error) {
414
412
  throw new Error(`Error removing tokens: ${error.message}`)
415
413
  }
416
- // eslint-disable-next-line @typescript-eslint/only-throw-error
414
+
417
415
  throw error
418
416
  }
419
417
  }
420
418
 
421
419
  /**
422
- * This function validates a token by checking its signature, expiration and issuer.
423
- * @param token The token being validated
424
- * @returns return if token is valid, throw errors otherwise
420
+ * Check whether a given JWT is still valid, i.e., hsa not expired. Does a few sanity
421
+ * checks as well and throws on malformed tokens, but does not do a full validation
422
+ * of the token (there's no point to do this on the client).
423
+ *
424
+ * @param token The JWT to check for expiration.
425
+ * @returns True if token is still valid (not expired), false otherwise.
426
+ * @throws Error if the token is malformed.
425
427
  */
426
- async function validateToken(token: string): Promise<boolean> {
427
- try {
428
- // Decode the token
429
- const completeTokenData = jwt.decode(token, { complete: true })
428
+ function isJwtStillValid(token: string): boolean {
429
+ // Decode the token
430
+ const completeTokenData = jwt.decode(token, { complete: true })
430
431
 
431
- // Handle junk data.
432
- if (!completeTokenData) {
433
- throw new Error('Invalid token')
434
- }
432
+ // Handle junk data.
433
+ if (!completeTokenData) {
434
+ throw new Error('Invalid token: unable to decode JWT')
435
+ }
435
436
 
436
- // Get the JSON web keys from the wellknown API.
437
- const res = await wellknownApi.discoverJsonWebKeys()
438
- const jwk = res.data.keys?.find((key) => key.kid === completeTokenData.header.kid)
439
- if (!jwk) {
440
- throw new Error('No public key found for token signature')
441
- }
442
- // Convert to PEM format.
443
- const pem = jwkToPem(jwk as jwkToPem.JWK)
437
+ // Perform simple local validation without network requests
438
+ const payload = completeTokenData.payload as JwtPayload
444
439
 
445
- // Verify the token signature, expiration and decode it.
446
- jwt.verify(token, pem, { issuer: baseURL, ignoreExpiration: false })
440
+ // Check if token has required fields
441
+ if (!payload.exp) {
442
+ throw new Error('Invalid token: missing expiration time (exp)')
443
+ }
447
444
 
448
- return true
449
- } catch (error) {
450
- if (error instanceof Error) {
451
- logger.info(`Token not valid: ${error.message}`)
452
- }
445
+ if (!payload.iss) {
446
+ throw new Error('Invalid token: missing issuer (iss)')
447
+ }
448
+
449
+ // Check if token is expired
450
+ const now = Math.floor(Date.now() / 1000)
451
+ if (payload.exp < now) {
453
452
  return false
454
453
  }
454
+
455
+ // Ignore nbf (not before). No additional value in checking it and
456
+ // the check is vulnerable to local clock skew.
457
+
458
+ // Token is still valid
459
+ return true
455
460
  }
456
461
 
457
462
  /**
package/src/deployment.ts CHANGED
@@ -596,7 +596,7 @@ export async function debugGameServer(targetEnv: TargetEnvironment, targetPodNam
596
596
  }
597
597
 
598
598
  // Execute 'kubectl debug ..'
599
- const tmpKubeconfigPath = path.join(os.tmpdir(), `temp-kubeconfig-${+Date.now()}`)
599
+ const tmpKubeconfigPath = path.join(os.tmpdir(), `temp-kubeconfig-${Date.now()}`)
600
600
  try {
601
601
  // Write kubeconfig to a file
602
602
  logger.debug(`Write temporary kubeconfig to ${tmpKubeconfigPath}`)
@@ -38,7 +38,7 @@ async function loadSecrets(): Promise<Secrets> {
38
38
  if (password.length === 0) {
39
39
  throw new Error('The file is encrypted. Please set the METAPLAY_AUTH_PASSWORD environment variable.')
40
40
  }
41
- // eslint-disable-next-line @typescript-eslint/only-throw-error
41
+
42
42
  throw error
43
43
  }
44
44
  }
package/src/utils.ts CHANGED
@@ -262,7 +262,7 @@ export async function executeHelmCommand(kubeconfigPayload: string, args: string
262
262
  */
263
263
  export interface HelmRelease {
264
264
  name: string // Eg, "gameserver"
265
- namespace: string // Eg, "idler-develop"
265
+ namespace: string // Eg, "lovely-wombats-build-nimbly"
266
266
  revision: string // Eg, "334"
267
267
  updated: string // Eg, "2024-11-21 10:38:06.105016472 +0000 UTC"
268
268
  status: string // Eg, "deployed"
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const PACKAGE_VERSION = "1.9.0"
1
+ export const PACKAGE_VERSION = "1.9.1"