@metaplay/metaplay-auth 1.1.6 → 1.2.0
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 +17 -0
- package/README.md +37 -4
- package/dist/index.js +119 -40
- package/dist/index.js.map +1 -1
- package/dist/{auth.js → src/auth.js} +47 -32
- package/dist/src/auth.js.map +1 -0
- package/dist/{deployment.js → src/deployment.js} +16 -6
- package/dist/{deployment.js.map → src/deployment.js.map} +1 -1
- package/dist/src/logging.js.map +1 -0
- package/dist/src/secret_store.js.map +1 -0
- package/dist/{stackapi.js → src/stackapi.js} +8 -13
- package/dist/src/stackapi.js.map +1 -0
- package/dist/src/utils.js.map +1 -0
- package/dist/tests/utils.spec.js +18 -0
- package/dist/tests/utils.spec.js.map +1 -0
- package/{src/index.ts → index.ts} +127 -41
- package/package.json +7 -5
- package/src/auth.ts +59 -36
- package/src/deployment.ts +18 -6
- package/src/secret_store.ts +2 -2
- package/src/stackapi.ts +8 -13
- package/tests/utils.spec.ts +20 -0
- package/vitest.config.ts +7 -0
- package/dist/auth.js.map +0 -1
- package/dist/logging.js.map +0 -1
- package/dist/secret_store.js.map +0 -1
- package/dist/stackapi.js.map +0 -1
- package/dist/utils.js.map +0 -1
- /package/dist/{logging.js → src/logging.js} +0 -0
- /package/dist/{secret_store.js → src/secret_store.js} +0 -0
- /package/dist/{utils.js → src/utils.js} +0 -0
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
import { isValidFQDN, getGameserverAdminUrl } from './utils.js';
|
|
2
2
|
import { logger } from './logging.js';
|
|
3
3
|
export class StackAPI {
|
|
4
|
-
|
|
5
|
-
access_token;
|
|
4
|
+
accessToken;
|
|
6
5
|
_stack_api_base_uri;
|
|
7
|
-
constructor(
|
|
8
|
-
if (idToken == null) {
|
|
9
|
-
throw new Error('id_token is missing');
|
|
10
|
-
}
|
|
6
|
+
constructor(accessToken) {
|
|
11
7
|
if (accessToken == null) {
|
|
12
|
-
throw new Error('
|
|
8
|
+
throw new Error('accessToken must be provided');
|
|
13
9
|
}
|
|
14
|
-
this.
|
|
15
|
-
this.access_token = accessToken;
|
|
10
|
+
this.accessToken = accessToken;
|
|
16
11
|
this._stack_api_base_uri = 'https://infra.p1.metaplay.io/stackapi';
|
|
17
12
|
}
|
|
18
13
|
get stack_api_base_uri() {
|
|
@@ -43,12 +38,12 @@ export class StackAPI {
|
|
|
43
38
|
const response = await fetch(url, {
|
|
44
39
|
method: 'POST',
|
|
45
40
|
headers: {
|
|
46
|
-
Authorization: `Bearer ${this.
|
|
41
|
+
Authorization: `Bearer ${this.accessToken}`,
|
|
47
42
|
'Content-Type': 'application/json'
|
|
48
43
|
}
|
|
49
44
|
});
|
|
50
45
|
if (response.status !== 200) {
|
|
51
|
-
throw new Error(`Failed to fetch AWS
|
|
46
|
+
throw new Error(`Failed to fetch AWS credentials: ${response.statusText}, response code=${response.status}`);
|
|
52
47
|
}
|
|
53
48
|
return await response.json();
|
|
54
49
|
}
|
|
@@ -73,7 +68,7 @@ export class StackAPI {
|
|
|
73
68
|
const response = await fetch(url, {
|
|
74
69
|
method: 'POST',
|
|
75
70
|
headers: {
|
|
76
|
-
Authorization: `Bearer ${this.
|
|
71
|
+
Authorization: `Bearer ${this.accessToken}`,
|
|
77
72
|
'Content-Type': 'application/json'
|
|
78
73
|
}
|
|
79
74
|
});
|
|
@@ -103,7 +98,7 @@ export class StackAPI {
|
|
|
103
98
|
const response = await fetch(url, {
|
|
104
99
|
method: 'GET',
|
|
105
100
|
headers: {
|
|
106
|
-
Authorization: `Bearer ${this.
|
|
101
|
+
Authorization: `Bearer ${this.accessToken}`,
|
|
107
102
|
'Content-Type': 'application/json'
|
|
108
103
|
}
|
|
109
104
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stackapi.js","sourceRoot":"","sources":["../../src/stackapi.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { expect, test, describe } from 'vitest';
|
|
2
|
+
import { isValidFQDN } from '../src/utils.js';
|
|
3
|
+
describe('isValidFQDN', () => {
|
|
4
|
+
test('should return true for valid FQDNs', () => {
|
|
5
|
+
expect(isValidFQDN('www.example.com')).toBe(true);
|
|
6
|
+
expect(isValidFQDN('subdomain.example.com')).toBe(true);
|
|
7
|
+
expect(isValidFQDN('example.com')).toBe(true);
|
|
8
|
+
expect(isValidFQDN('example.co.uk')).toBe(true);
|
|
9
|
+
});
|
|
10
|
+
test('should return false for invalid FQDNs', () => {
|
|
11
|
+
expect(isValidFQDN('example')).toBe(false);
|
|
12
|
+
expect(isValidFQDN('www.example..com')).toBe(false);
|
|
13
|
+
expect(isValidFQDN('www.-example.com')).toBe(false);
|
|
14
|
+
expect(isValidFQDN('www.example-.com')).toBe(false);
|
|
15
|
+
expect(isValidFQDN('www.example.com-')).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
//# sourceMappingURL=utils.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.spec.js","sourceRoot":"","sources":["../../tests/utils.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAE/C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAE7C,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,IAAI,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC9C,MAAM,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACjD,MAAM,CAAC,WAAW,CAAC,uBAAuB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACvD,MAAM,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC7C,MAAM,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACjD,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,uCAAuC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAC1C,MAAM,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACnD,MAAM,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACnD,MAAM,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACnD,MAAM,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACrD,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander'
|
|
3
|
-
import { loginAndSaveTokens, extendCurrentSession, loadTokens,
|
|
4
|
-
import { StackAPI } from './stackapi.js'
|
|
5
|
-
import { checkGameServerDeployment } from './deployment.js'
|
|
6
|
-
import { setLogLevel } from './logging.js'
|
|
3
|
+
import { loginAndSaveTokens, machineLoginAndSaveTokens, extendCurrentSession, loadTokens, removeTokens } from './src/auth.js'
|
|
4
|
+
import { StackAPI } from './src/stackapi.js'
|
|
5
|
+
import { checkGameServerDeployment } from './src/deployment.js'
|
|
6
|
+
import { logger, setLogLevel } from './src/logging.js'
|
|
7
7
|
import { exit } from 'process'
|
|
8
|
+
import { ECRClient, GetAuthorizationTokenCommand } from '@aws-sdk/client-ecr'
|
|
8
9
|
|
|
9
10
|
const program = new Command()
|
|
10
11
|
|
|
11
12
|
program
|
|
12
13
|
.name('metaplay-auth')
|
|
13
14
|
.description('Authenticate with Metaplay and get AWS and Kubernetes credentials for game servers.')
|
|
14
|
-
.version('1.
|
|
15
|
+
.version('1.2.0')
|
|
15
16
|
.option('-d, --debug', 'enable debug output')
|
|
16
17
|
.hook('preAction', (thisCommand) => {
|
|
17
18
|
// Handle debug flag for all commands.
|
|
@@ -30,26 +31,31 @@ program.command('login')
|
|
|
30
31
|
})
|
|
31
32
|
|
|
32
33
|
program.command('machine-login')
|
|
33
|
-
.description('
|
|
34
|
-
.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
.description('login to the Metaplay cloud using a machine account (using credentials in environment variable METAPLAY_CREDENTIALS)')
|
|
35
|
+
.option('--dev-credentials', 'machine user credentials to use, only for dev purposes, use METAPLAY_CREDENTIALS env variable for better safety!')
|
|
36
|
+
.action(async (options) => {
|
|
37
|
+
// Get credentials from command line or from METAPLAY_CREDENTIALS environment variable
|
|
38
|
+
let credentials
|
|
39
|
+
if (options.devCredentials) {
|
|
40
|
+
credentials = options.devCredentials
|
|
41
|
+
} else {
|
|
42
|
+
credentials = process.env.METAPLAY_CREDENTIALS
|
|
43
|
+
if (!credentials || credentials === '') {
|
|
44
|
+
throw new Error('Unable to find the credentials, the environment variable METAPLAY_CREDENTIALS is not defined!')
|
|
45
|
+
}
|
|
38
46
|
}
|
|
39
47
|
|
|
40
|
-
//
|
|
41
|
-
// \
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
} catch (error) {
|
|
46
|
-
throw new Error('Unable to parse METAPLAY_CREDENTIALS: not valid JSON. Expecting a JSON blob with the access tokens, as outputted by `metaplay-auth show-tokens`.')
|
|
48
|
+
// Parse the clientId and clientSecret from the credentials (separate by a '+' character)
|
|
49
|
+
// \note We can't be certain that the secret does not contain pluses so split at the first occurrence
|
|
50
|
+
const splitOffset = credentials.indexOf('+')
|
|
51
|
+
if (splitOffset === -1) {
|
|
52
|
+
throw new Error('Invalid format for credentials, you should copy-paste the value from the developer portal verbatim')
|
|
47
53
|
}
|
|
54
|
+
const clientId = credentials.substring(0, splitOffset)
|
|
55
|
+
const clientSecret = credentials.substring(splitOffset + 1)
|
|
48
56
|
|
|
49
|
-
//
|
|
50
|
-
await
|
|
51
|
-
|
|
52
|
-
console.log('Successfully logged in to Metaplay cloud using machine account!')
|
|
57
|
+
// Login with machine user and save the tokens
|
|
58
|
+
await machineLoginAndSaveTokens(clientId, clientSecret)
|
|
53
59
|
})
|
|
54
60
|
|
|
55
61
|
program.command('logout')
|
|
@@ -71,23 +77,14 @@ program.command('logout')
|
|
|
71
77
|
|
|
72
78
|
program.command('show-tokens')
|
|
73
79
|
.description('show loaded tokens')
|
|
74
|
-
.option('-f, --format <format>', 'output format (json or pretty)', 'json')
|
|
75
80
|
.hook('preAction', async () => {
|
|
76
81
|
await extendCurrentSession()
|
|
77
82
|
})
|
|
78
83
|
.action(async (options) => {
|
|
79
84
|
try {
|
|
80
|
-
if (options.format !== 'json' && options.format !== 'pretty') {
|
|
81
|
-
throw new Error('Invalid format; must be one of json or pretty')
|
|
82
|
-
}
|
|
83
|
-
|
|
84
85
|
// TODO: Could detect if not logged in and fail more gracefully?
|
|
85
86
|
const tokens = await loadTokens()
|
|
86
|
-
|
|
87
|
-
console.log(JSON.stringify(tokens))
|
|
88
|
-
} else {
|
|
89
|
-
console.log(tokens)
|
|
90
|
-
}
|
|
87
|
+
console.log(tokens)
|
|
91
88
|
} catch (error) {
|
|
92
89
|
if (error instanceof Error) {
|
|
93
90
|
console.error(`Error showing tokens: ${error.message}`)
|
|
@@ -114,7 +111,7 @@ program.command('get-kubeconfig')
|
|
|
114
111
|
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')
|
|
115
112
|
}
|
|
116
113
|
|
|
117
|
-
const stackApi = new StackAPI(tokens.
|
|
114
|
+
const stackApi = new StackAPI(tokens.access_token)
|
|
118
115
|
if (options.stackApi) {
|
|
119
116
|
stackApi.stack_api_base_uri = options.stackApi
|
|
120
117
|
}
|
|
@@ -154,7 +151,7 @@ program.command('get-aws-credentials')
|
|
|
154
151
|
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')
|
|
155
152
|
}
|
|
156
153
|
|
|
157
|
-
const stackApi = new StackAPI(tokens.
|
|
154
|
+
const stackApi = new StackAPI(tokens.access_token)
|
|
158
155
|
if (options.stackApi) {
|
|
159
156
|
stackApi.stack_api_base_uri = options.stackApi
|
|
160
157
|
}
|
|
@@ -181,6 +178,89 @@ program.command('get-aws-credentials')
|
|
|
181
178
|
}
|
|
182
179
|
})
|
|
183
180
|
|
|
181
|
+
program.command('get-docker-login')
|
|
182
|
+
.description('get docker login credentials for pushing the server image')
|
|
183
|
+
.argument('[gameserver]', 'address of gameserver (e.g. idler-develop.p1.metaplay.io)')
|
|
184
|
+
.option('-o, --organization <organization>', 'organization name (e.g. metaplay)')
|
|
185
|
+
.option('-p, --project <project>', 'project name (e.g. idler)')
|
|
186
|
+
.option('-e, --environment <environment>', 'environment name (e.g. develop)')
|
|
187
|
+
.option('-f, --format <format>', 'output format (json or env)', 'json')
|
|
188
|
+
.hook('preAction', async () => {
|
|
189
|
+
await extendCurrentSession()
|
|
190
|
+
})
|
|
191
|
+
.action(async (gameserver, options) => {
|
|
192
|
+
try {
|
|
193
|
+
if (options.format !== 'json' && options.format !== 'env') {
|
|
194
|
+
throw new Error('Invalid format; must be one of json or env')
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const tokens = await loadTokens()
|
|
198
|
+
|
|
199
|
+
if (!gameserver && !(options.organization && options.project && options.environment)) {
|
|
200
|
+
throw new Error('Could not determine a game server deployment. You need to specify either a gameserver or an organization, project, and environment')
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const stackApi = new StackAPI(tokens.access_token)
|
|
204
|
+
if (options.stackApi) {
|
|
205
|
+
stackApi.stack_api_base_uri = options.stackApi
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Fetch AWS credentials from Metaplay cloud
|
|
209
|
+
logger.debug('Get AWS credentials from Metaplay')
|
|
210
|
+
const payload = gameserver ? { gameserver } : { organization: options.organization, project: options.project, environment: options.environment }
|
|
211
|
+
const credentials = await stackApi.getAwsCredentials(payload)
|
|
212
|
+
|
|
213
|
+
// Get environment info (region is needed for ECR)
|
|
214
|
+
logger.debug('Get environment info')
|
|
215
|
+
const environment = await stackApi.getEnvironmentDetails(payload)
|
|
216
|
+
const awsRegion = environment.deployment.aws_region
|
|
217
|
+
const dockerRepo = environment.deployment.ecr_repo
|
|
218
|
+
|
|
219
|
+
// Create ECR client with credentials
|
|
220
|
+
logger.debug('Create ECR client')
|
|
221
|
+
const client = new ECRClient({
|
|
222
|
+
credentials: {
|
|
223
|
+
accessKeyId: credentials.AccessKeyId,
|
|
224
|
+
secretAccessKey: credentials.SecretAccessKey,
|
|
225
|
+
sessionToken: credentials.SessionToken
|
|
226
|
+
},
|
|
227
|
+
region: awsRegion
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
// Fetch the ECR docker authentication token
|
|
231
|
+
logger.debug('Fetch ECR login credentials from AWS')
|
|
232
|
+
const command = new GetAuthorizationTokenCommand({})
|
|
233
|
+
const response = await client.send(command)
|
|
234
|
+
if (!response.authorizationData || response.authorizationData.length === 0 || !response.authorizationData[0].authorizationToken) {
|
|
235
|
+
throw new Error('Received an empty authorization token response for ECR repository')
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Parse username and password from the response (separated by a ':')
|
|
239
|
+
logger.debug('Parse ECR response')
|
|
240
|
+
const authorization64 = response.authorizationData[0].authorizationToken
|
|
241
|
+
const authorization = Buffer.from(authorization64, 'base64').toString()
|
|
242
|
+
const [username, password] = authorization.split(':')
|
|
243
|
+
|
|
244
|
+
// Output the docker repo & credentials
|
|
245
|
+
if (options.format === 'env') {
|
|
246
|
+
console.log(`export DOCKER_REPO=${dockerRepo}`)
|
|
247
|
+
console.log(`export DOCKER_USERNAME=${username}`)
|
|
248
|
+
console.log(`export DOCKER_PASSWORD=${password}`)
|
|
249
|
+
} else {
|
|
250
|
+
console.log(JSON.stringify({
|
|
251
|
+
dockerRepo,
|
|
252
|
+
username,
|
|
253
|
+
password
|
|
254
|
+
}))
|
|
255
|
+
}
|
|
256
|
+
} catch (error) {
|
|
257
|
+
if (error instanceof Error) {
|
|
258
|
+
console.error(`Error getting docker login credentials: ${error.message}`)
|
|
259
|
+
}
|
|
260
|
+
exit(1)
|
|
261
|
+
}
|
|
262
|
+
})
|
|
263
|
+
|
|
184
264
|
program.command('get-environment')
|
|
185
265
|
.description('get environment details for deployment')
|
|
186
266
|
.argument('[gameserver]', 'address of gameserver (e.g. idler-develop.p1.metaplay.io)')
|
|
@@ -199,7 +279,7 @@ program.command('get-environment')
|
|
|
199
279
|
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')
|
|
200
280
|
}
|
|
201
281
|
|
|
202
|
-
const stackApi = new StackAPI(tokens.
|
|
282
|
+
const stackApi = new StackAPI(tokens.access_token)
|
|
203
283
|
if (options.stackApi) {
|
|
204
284
|
stackApi.stack_api_base_uri = options.stackApi
|
|
205
285
|
}
|
|
@@ -219,15 +299,21 @@ program.command('get-environment')
|
|
|
219
299
|
program.command('check-deployment')
|
|
220
300
|
.description('[experimental] check that a game server was successfully deployed, or print out useful error messages in case of failure')
|
|
221
301
|
.argument('[namespace]', 'kubernetes namespace of the deployment')
|
|
222
|
-
.
|
|
223
|
-
await extendCurrentSession()
|
|
224
|
-
})
|
|
225
|
-
.action(async (namespace: string, options) => {
|
|
302
|
+
.action(async (namespace: string) => {
|
|
226
303
|
setLogLevel(0)
|
|
227
304
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
305
|
+
try {
|
|
306
|
+
if (!namespace) {
|
|
307
|
+
throw new Error('Must specify value for argument "namespace"')
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Run the checks and exit with success/failure exitCode depending on result
|
|
311
|
+
const exitCode = await checkGameServerDeployment(namespace)
|
|
312
|
+
exit(exitCode)
|
|
313
|
+
} catch (error) {
|
|
314
|
+
console.error(`Failed to check deployment status: ${error}`)
|
|
315
|
+
exit(1)
|
|
316
|
+
}
|
|
231
317
|
})
|
|
232
318
|
|
|
233
319
|
void program.parseAsync()
|
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.
|
|
4
|
+
"version": "1.2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
7
7
|
"homepage": "https://metaplay.io",
|
|
@@ -13,24 +13,26 @@
|
|
|
13
13
|
"@types/express": "^4.17.21",
|
|
14
14
|
"@types/jsonwebtoken": "^9.0.5",
|
|
15
15
|
"@types/jwk-to-pem": "^2.0.3",
|
|
16
|
-
"@types/node": "^20.11.
|
|
16
|
+
"@types/node": "^20.11.28",
|
|
17
17
|
"tsx": "^4.7.1",
|
|
18
18
|
"typescript": "^5.1.6",
|
|
19
|
+
"vitest": "^1.3.1",
|
|
19
20
|
"@metaplay/eslint-config": "1.0.0",
|
|
20
21
|
"@metaplay/typescript-config": "1.0.0"
|
|
21
22
|
},
|
|
22
23
|
"dependencies": {
|
|
24
|
+
"@aws-sdk/client-ecr": "^3.535.0",
|
|
25
|
+
"@kubernetes/client-node": "^0.20.0",
|
|
23
26
|
"@ory/client": "^1.6.2",
|
|
24
27
|
"commander": "^12.0.0",
|
|
25
28
|
"h3": "^1.10.2",
|
|
26
29
|
"jsonwebtoken": "^9.0.2",
|
|
27
30
|
"jwk-to-pem": "^2.0.5",
|
|
28
31
|
"open": "^10.0.2",
|
|
29
|
-
"tslog": "^4.9.2"
|
|
30
|
-
"@kubernetes/client-node": "^0.20.0"
|
|
32
|
+
"tslog": "^4.9.2"
|
|
31
33
|
},
|
|
32
34
|
"scripts": {
|
|
33
|
-
"dev": "tsx
|
|
35
|
+
"dev": "tsx index.ts",
|
|
34
36
|
"prepublish": "tsc"
|
|
35
37
|
}
|
|
36
38
|
}
|
package/src/auth.ts
CHANGED
|
@@ -20,7 +20,6 @@ const tokenEndpoint = `${baseURL}/oauth2/token`
|
|
|
20
20
|
const wellknownApi = new WellknownApi(new Configuration({
|
|
21
21
|
basePath: baseURL,
|
|
22
22
|
}))
|
|
23
|
-
const tokenList: string[] = ['id_token', 'access_token', 'refresh_token'] // List of compulsory tokens
|
|
24
23
|
|
|
25
24
|
/**
|
|
26
25
|
* A helper function which generates a code verifier and challenge for exchaning code from Ory server.
|
|
@@ -111,7 +110,9 @@ export async function loginAndSaveTokens () {
|
|
|
111
110
|
try {
|
|
112
111
|
logger.debug(`Received callback request with code ${code}. Preparing to exchange for tokens...`)
|
|
113
112
|
const tokens = await getTokensWithAuthorizationCode(state, redirectUri, verifier, code)
|
|
114
|
-
|
|
113
|
+
|
|
114
|
+
// Only save access_token, id_token, and refresh_token
|
|
115
|
+
await saveTokens({ access_token: tokens.access_token, id_token: tokens.id_token, refresh_token: tokens.refresh_token })
|
|
115
116
|
|
|
116
117
|
console.log('You are now logged in and can call the other commands.')
|
|
117
118
|
|
|
@@ -140,6 +141,40 @@ export async function loginAndSaveTokens () {
|
|
|
140
141
|
void open(authorizationUrl)
|
|
141
142
|
}
|
|
142
143
|
|
|
144
|
+
export async function machineLoginAndSaveTokens (clientId: string, clientSecret: string) {
|
|
145
|
+
// Get a fresh access token from Metaplay Auth.
|
|
146
|
+
const params = new URLSearchParams()
|
|
147
|
+
params.set('grant_type', 'client_credentials')
|
|
148
|
+
params.set('client_id', clientId)
|
|
149
|
+
params.set('client_secret', clientSecret)
|
|
150
|
+
params.set('scope', 'openid email profile offline_access')
|
|
151
|
+
|
|
152
|
+
const res = await fetch(tokenEndpoint, {
|
|
153
|
+
method: 'POST',
|
|
154
|
+
headers: {
|
|
155
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
156
|
+
},
|
|
157
|
+
body: params.toString(),
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
// Return type checked manually by Teemu on 2024-3-7.
|
|
161
|
+
const tokens = await res.json() as { access_token: string, token_type: string, expires_in: number, scope: string }
|
|
162
|
+
|
|
163
|
+
logger.debug('Received machine authentication tokens, saving them for future use...')
|
|
164
|
+
|
|
165
|
+
await saveTokens({ access_token: tokens.access_token })
|
|
166
|
+
|
|
167
|
+
const userInfoResponse = await fetch('https://portal.metaplay.dev/api/external/userinfo', {
|
|
168
|
+
headers: {
|
|
169
|
+
Authorization: `Bearer ${tokens.access_token}`
|
|
170
|
+
},
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
const userInfo = await userInfoResponse.json() as { given_name: string, family_name: string }
|
|
174
|
+
|
|
175
|
+
console.log(`You are now logged in with machine user ${userInfo.given_name} ${userInfo.family_name} (clientId=${clientId}) and can execute the other commands.`)
|
|
176
|
+
}
|
|
177
|
+
|
|
143
178
|
/**
|
|
144
179
|
* Refresh access and ID token with a refresh token.
|
|
145
180
|
*/
|
|
@@ -147,18 +182,22 @@ export async function extendCurrentSession (): Promise<void> {
|
|
|
147
182
|
try {
|
|
148
183
|
const tokens = await loadTokens()
|
|
149
184
|
|
|
150
|
-
logger.debug('
|
|
185
|
+
logger.debug('Check if access token still valid...')
|
|
151
186
|
if (await validateToken(tokens.access_token)) {
|
|
152
187
|
// Access token is not expired, return to the caller
|
|
153
|
-
logger.debug('Access token is valid,
|
|
188
|
+
logger.debug('Access token is valid, no need to refresh.')
|
|
154
189
|
return
|
|
155
190
|
}
|
|
156
191
|
|
|
157
|
-
|
|
192
|
+
// Check that the refresh_token exists (machine users don't have it)
|
|
193
|
+
if (!tokens.refresh_token) {
|
|
194
|
+
throw new Error('Cannot refresh an access_token without a refresh_token. With machine users, should just login again instead.')
|
|
195
|
+
}
|
|
158
196
|
|
|
197
|
+
logger.debug('Access token is no longer valid, trying to extend the current session with a refresh token.')
|
|
159
198
|
const refreshedTokens = await extendCurrentSessionWithRefreshToken(tokens.refresh_token)
|
|
160
199
|
|
|
161
|
-
await saveTokens(refreshedTokens)
|
|
200
|
+
await saveTokens({ access_token: refreshedTokens.access_token, id_token: refreshedTokens.id_token, refresh_token: refreshedTokens.refresh_token })
|
|
162
201
|
} catch (error) {
|
|
163
202
|
if (error instanceof Error) {
|
|
164
203
|
console.error(error.message)
|
|
@@ -173,7 +212,7 @@ export async function extendCurrentSession (): Promise<void> {
|
|
|
173
212
|
* @returns A promise that resolves to a new set of tokens.
|
|
174
213
|
*/
|
|
175
214
|
async function extendCurrentSessionWithRefreshToken (refreshToken: string): Promise<{ id_token: string, access_token: string, refresh_token: string }> {
|
|
176
|
-
// TODO:
|
|
215
|
+
// TODO: similar to the todo task in getTokensWithAuthorizationCode, http request can be handled by ory/client.
|
|
177
216
|
const params = new URLSearchParams({
|
|
178
217
|
grant_type: 'refresh_token',
|
|
179
218
|
refresh_token: refreshToken,
|
|
@@ -218,7 +257,7 @@ async function extendCurrentSessionWithRefreshToken (refreshToken: string): Prom
|
|
|
218
257
|
* @param code
|
|
219
258
|
* @returns
|
|
220
259
|
*/
|
|
221
|
-
async function getTokensWithAuthorizationCode (state: string, redirectUri: string, verifier: string, code: string): Promise<{ id_token: string, access_token: string, refresh_token: string }
|
|
260
|
+
async function getTokensWithAuthorizationCode (state: string, redirectUri: string, verifier: string, code: string): Promise<{ id_token: string, access_token: string, refresh_token: string }> {
|
|
222
261
|
// TODO: the authorication code exchange flow might be better to be handled by ory/client, could check if there's any useful toosl there.
|
|
223
262
|
try {
|
|
224
263
|
const response = await fetch(tokenEndpoint, {
|
|
@@ -234,29 +273,22 @@ async function getTokensWithAuthorizationCode (state: string, redirectUri: strin
|
|
|
234
273
|
if (error instanceof Error) {
|
|
235
274
|
logger.error(`Error exchanging code for tokens: ${error.message}`)
|
|
236
275
|
}
|
|
237
|
-
|
|
238
|
-
return {}
|
|
276
|
+
throw error
|
|
239
277
|
}
|
|
240
278
|
}
|
|
241
279
|
|
|
242
280
|
/**
|
|
243
281
|
* Load tokens from the local secret store.
|
|
244
282
|
*/
|
|
245
|
-
export async function loadTokens (): Promise<{ id_token
|
|
283
|
+
export async function loadTokens (): Promise<{ id_token?: string, access_token: string, refresh_token?: string }> {
|
|
246
284
|
try {
|
|
247
|
-
const
|
|
248
|
-
const accessToken = await getSecret('access_token')
|
|
249
|
-
const refreshToken = await getSecret('refresh_token')
|
|
285
|
+
const tokens = await getSecret('tokens') as { id_token?: string, access_token: string, refresh_token?: string }
|
|
250
286
|
|
|
251
|
-
if (
|
|
252
|
-
throw new Error('
|
|
287
|
+
if (!tokens) {
|
|
288
|
+
throw new Error('Unable to load tokens. You need to login first.')
|
|
253
289
|
}
|
|
254
290
|
|
|
255
|
-
return
|
|
256
|
-
id_token: idToken,
|
|
257
|
-
access_token: accessToken,
|
|
258
|
-
refresh_token: refreshToken
|
|
259
|
-
}
|
|
291
|
+
return tokens
|
|
260
292
|
} catch (error) {
|
|
261
293
|
if (error instanceof Error) {
|
|
262
294
|
throw new Error(`Error loading tokens: ${error.message}`)
|
|
@@ -273,23 +305,18 @@ export async function saveTokens (tokens: Record<string, string>): Promise<void>
|
|
|
273
305
|
try {
|
|
274
306
|
logger.debug('Received new tokens, verifying...')
|
|
275
307
|
|
|
276
|
-
//
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
throw new Error(`Metaplay token ${tokenName} not found. Please log in again and make sure all checkboxes of permissions are selected before proceeding.`)
|
|
280
|
-
}
|
|
308
|
+
// All tokens must have an access_token (machine users only have it)
|
|
309
|
+
if (!tokens.access_token) {
|
|
310
|
+
throw new Error('Metaplay token has no access_token. Please log in again and make sure all checkboxes of permissions are selected before proceeding.')
|
|
281
311
|
}
|
|
282
312
|
|
|
283
313
|
logger.debug('Token verification completed, storing tokens...')
|
|
284
314
|
|
|
285
|
-
await setSecret('
|
|
286
|
-
await setSecret('access_token', tokens.access_token)
|
|
287
|
-
await setSecret('refresh_token', tokens.refresh_token)
|
|
315
|
+
await setSecret('tokens', tokens)
|
|
288
316
|
|
|
289
317
|
logger.debug('Tokens successfully stored.')
|
|
290
318
|
|
|
291
319
|
await showTokenInfo(tokens.access_token)
|
|
292
|
-
// await showTokenInfo(tokens.id_token)
|
|
293
320
|
} catch (error) {
|
|
294
321
|
if (error instanceof Error) {
|
|
295
322
|
throw new Error(`Failed to save tokens: ${error.message}`)
|
|
@@ -303,12 +330,8 @@ export async function saveTokens (tokens: Record<string, string>): Promise<void>
|
|
|
303
330
|
*/
|
|
304
331
|
export async function removeTokens (): Promise<void> {
|
|
305
332
|
try {
|
|
306
|
-
await removeSecret('
|
|
307
|
-
logger.debug('Removed
|
|
308
|
-
await removeSecret('access_token')
|
|
309
|
-
logger.debug('Removed access_token.')
|
|
310
|
-
await removeSecret('refresh_token')
|
|
311
|
-
logger.debug('Removed refresh_token.')
|
|
333
|
+
await removeSecret('tokens')
|
|
334
|
+
logger.debug('Removed tokens.')
|
|
312
335
|
} catch (error) {
|
|
313
336
|
if (error instanceof Error) {
|
|
314
337
|
throw new Error(`Error removing tokens: ${error.message}`)
|
package/src/deployment.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { promises as fs, existsSync } from 'fs'
|
|
1
2
|
import { KubeConfig, CoreV1Api, V1Pod } from '@kubernetes/client-node'
|
|
2
3
|
import { logger } from './logging.js'
|
|
3
4
|
import { error } from 'console'
|
|
@@ -202,11 +203,22 @@ async function delay (ms: number): Promise<void> {
|
|
|
202
203
|
}
|
|
203
204
|
|
|
204
205
|
export async function checkGameServerDeployment (namespace: string): Promise<number> {
|
|
205
|
-
logger.info(`Validating deployment
|
|
206
|
+
logger.info(`Validating game server deployment in namespace ${namespace}`)
|
|
207
|
+
|
|
208
|
+
// Check that the KUBECONFIG environment variable exists
|
|
209
|
+
const kubeconfigPath = process.env.KUBECONFIG
|
|
210
|
+
if (!kubeconfigPath) {
|
|
211
|
+
throw new Error('The KUBECONFIG environment variable must be specified')
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Check that the kubeconfig file exists
|
|
215
|
+
if (!await existsSync(kubeconfigPath)) {
|
|
216
|
+
throw new Error(`The environment variable KUBECONFIG points to a file '${kubeconfigPath}' that doesn't exist`)
|
|
217
|
+
}
|
|
206
218
|
|
|
207
219
|
// Create Kubernetes API instance (with default kubeconfig)
|
|
208
220
|
const kc = new KubeConfig()
|
|
209
|
-
kc.
|
|
221
|
+
kc.loadFromFile(kubeconfigPath)
|
|
210
222
|
const k8sApi = kc.makeApiClient(CoreV1Api)
|
|
211
223
|
|
|
212
224
|
// Figure out when to stop
|
|
@@ -220,21 +232,21 @@ export async function checkGameServerDeployment (namespace: string): Promise<num
|
|
|
220
232
|
|
|
221
233
|
switch (podStatus.phase) {
|
|
222
234
|
case GameServerPodPhase.Ready:
|
|
223
|
-
|
|
235
|
+
console.log('Gameserver successfully started')
|
|
224
236
|
// \todo add further readiness checks -- ping endpoint, ping dashboard, other checks?
|
|
225
237
|
return 0
|
|
226
238
|
|
|
227
239
|
case GameServerPodPhase.Failed:
|
|
228
|
-
|
|
240
|
+
console.log('Gameserver failed to start')
|
|
229
241
|
return 1
|
|
230
242
|
|
|
231
243
|
case GameServerPodPhase.Pending:
|
|
232
|
-
|
|
244
|
+
console.log('Gameserver still starting')
|
|
233
245
|
break
|
|
234
246
|
|
|
235
247
|
case GameServerPodPhase.Unknown:
|
|
236
248
|
default:
|
|
237
|
-
|
|
249
|
+
console.log('Gameserver in unknown state')
|
|
238
250
|
break
|
|
239
251
|
}
|
|
240
252
|
|
package/src/secret_store.ts
CHANGED
|
@@ -65,7 +65,7 @@ function decrypt (text: string): string {
|
|
|
65
65
|
return decrypted.toString()
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
export async function setSecret (key: string, value:
|
|
68
|
+
export async function setSecret (key: string, value: any): Promise<void> {
|
|
69
69
|
logger.debug(`Setting secret ${key}...`)
|
|
70
70
|
|
|
71
71
|
const secrets = await loadSecrets()
|
|
@@ -75,7 +75,7 @@ export async function setSecret (key: string, value: string): Promise<void> {
|
|
|
75
75
|
await fs.writeFile(filePath, content)
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
export async function getSecret (key: string): Promise<
|
|
78
|
+
export async function getSecret (key: string): Promise<any | undefined> {
|
|
79
79
|
logger.debug(`Getting secret ${key}...`)
|
|
80
80
|
|
|
81
81
|
const secrets = await loadSecrets()
|