@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.
- package/CHANGELOG.md +6 -0
- package/README.md +2 -0
- package/dist/index.cjs +193 -134
- package/dist/sshcrypto-47ANNZZ5.node +0 -0
- package/package.json +17 -16
- package/src/auth.ts +44 -39
- package/src/deployment.ts +1 -1
- package/src/secret_store.ts +1 -1
- package/src/utils.ts +1 -1
- package/src/version.ts +1 -1
|
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.
|
|
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.
|
|
21
|
-
"@types/express": "5.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.
|
|
23
|
+
"@types/jsonwebtoken": "9.0.9",
|
|
24
24
|
"@types/jwk-to-pem": "2.0.3",
|
|
25
|
-
"@types/node": "22.
|
|
26
|
-
"@types/semver": "7.
|
|
27
|
-
"esbuild": "0.25.
|
|
28
|
-
"tsx": "4.19.
|
|
29
|
-
"typescript": "5.
|
|
30
|
-
"vitest": "3.
|
|
31
|
-
"@aws-sdk/client-ecr": "3.
|
|
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.
|
|
33
|
+
"@ory/client": "1.20.11",
|
|
34
34
|
"commander": "12.1.0",
|
|
35
|
-
"dockerode": "4.0.
|
|
36
|
-
"h3": "1.15.
|
|
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.
|
|
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
|
-
|
|
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,
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
414
|
+
|
|
417
415
|
throw error
|
|
418
416
|
}
|
|
419
417
|
}
|
|
420
418
|
|
|
421
419
|
/**
|
|
422
|
-
*
|
|
423
|
-
*
|
|
424
|
-
*
|
|
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
|
-
|
|
427
|
-
|
|
428
|
-
|
|
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
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
432
|
+
// Handle junk data.
|
|
433
|
+
if (!completeTokenData) {
|
|
434
|
+
throw new Error('Invalid token: unable to decode JWT')
|
|
435
|
+
}
|
|
435
436
|
|
|
436
|
-
|
|
437
|
-
|
|
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
|
-
|
|
446
|
-
|
|
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
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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-${
|
|
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}`)
|
package/src/secret_store.ts
CHANGED
|
@@ -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
|
-
|
|
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, "
|
|
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.
|
|
1
|
+
export const PACKAGE_VERSION = "1.9.1"
|