@metaplay/metaplay-auth 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ # Changelog
2
+
3
+ ## [1.0.0] - 2023-01-17
4
+
5
+ ### Added
6
+
7
+ * Initial `metaplay-auth` version.
package/LICENSE ADDED
@@ -0,0 +1 @@
1
+ This module and all files within are distributed under the Metaplay SDK Software License.
package/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # metaplay-auth
2
+
3
+ ## Description
4
+
5
+ `metaplay-auth` is a command-line tool for authenticating to Metaplay infrastructure using your Metaplay account and interacting with your game servers.
6
+
7
+ ## Installation
8
+
9
+ You can install `metaplay-auth` via NPM:
10
+
11
+ ```
12
+ npm install -g @metaplay/metaplay-auth
13
+ ```
14
+
15
+ Alternatively, you can quickly locally run the latest version using `npx`:
16
+
17
+ ```
18
+ npx @metaplay/metaplay-auth@latest login
19
+ npx @metaplay/metaplay-auth@latest show-tokens
20
+ npx @metaplay/metaplay-auth@latest logout
21
+ ```
22
+
23
+ ## License
24
+
25
+ See the LICENSE file.
package/dist/auth.js ADDED
@@ -0,0 +1,110 @@
1
+ /* eslint-disable @typescript-eslint/no-misused-promises */
2
+ import express from 'express';
3
+ import open from 'open';
4
+ import * as crypto from 'crypto';
5
+ import { createServer } from 'net';
6
+ import { setSecret, getSecret, removeSecret } from './secret_store.js';
7
+ import { logger } from './logging.js';
8
+ // 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
+ function generateCodeVerifierAndChallenge() {
13
+ const verifier = crypto.randomBytes(32).toString('hex');
14
+ const challenge = crypto.createHash('sha256').update(verifier).digest('base64url');
15
+ return { verifier, challenge };
16
+ }
17
+ async function findAvailablePort() {
18
+ return await new Promise((resolve, reject) => {
19
+ // Ports need to be in sync with oauth2 client callbacks.
20
+ const portsToCheck = [5000, 5001, 5002, 5003, 5004];
21
+ let index = 0;
22
+ // Test ports by opening a server on them.
23
+ function tryNextPort() {
24
+ if (index >= portsToCheck.length) {
25
+ reject(new Error('Could not find an available port.'));
26
+ }
27
+ const port = portsToCheck[index];
28
+ const server = createServer();
29
+ logger.debug(`Trying port ${port}...`);
30
+ server.listen(port, () => {
31
+ server.once('close', () => {
32
+ resolve(port);
33
+ });
34
+ server.close();
35
+ logger.debug(`Port ${port} is available.`);
36
+ });
37
+ server.on('error', () => {
38
+ logger.debug(`Port ${port} is not available.`);
39
+ index++;
40
+ tryNextPort();
41
+ });
42
+ }
43
+ tryNextPort();
44
+ });
45
+ }
46
+ export async function loginAndSaveTokens() {
47
+ // Find an available port to listen on.
48
+ const availablePort = await findAvailablePort();
49
+ const app = express();
50
+ const redirectUri = `http://localhost:${availablePort}/callback`;
51
+ const { verifier, challenge } = generateCodeVerifierAndChallenge();
52
+ const state = crypto.randomBytes(16).toString('hex');
53
+ // Create a /callback endpoint that exchanges the code for tokens.
54
+ app.get('/callback', async (req, res) => {
55
+ const code = req.query.code;
56
+ logger.debug(`Received callback request with code ${code}. Preparing to exchange for tokens...`);
57
+ const tokens = await exchangeCodeForTokens(state, redirectUri, verifier, code);
58
+ logger.debug(`Received tokens ${JSON.stringify(tokens)}. Storing them...`);
59
+ await setSecret('id_token', tokens.id_token);
60
+ await setSecret('access_token', tokens.access_token);
61
+ console.log('Login tokens stored successfully.');
62
+ // TODO: Could return a pre-generated HTML page instead of text?
63
+ res.send('Authentication successful! You can close this window.');
64
+ server.close();
65
+ console.log('You are now logged in and can call the other commands.');
66
+ process.exit(0);
67
+ });
68
+ // Start the server.
69
+ const server = app.listen(availablePort, () => {
70
+ 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)}`;
71
+ 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`);
72
+ void open(authorizationUrl);
73
+ logger.debug(`Listening on port ${availablePort} and waiting for callback...`);
74
+ });
75
+ }
76
+ export async function loadTokens() {
77
+ const idToken = await getSecret('id_token');
78
+ const accessToken = await getSecret('access_token');
79
+ if (idToken == null || accessToken == null) {
80
+ throw new Error('Metaplay tokens not found. Are you logged in?');
81
+ }
82
+ return {
83
+ id_token: idToken,
84
+ access_token: accessToken
85
+ };
86
+ }
87
+ export async function removeTokens() {
88
+ try {
89
+ await removeSecret('id_token');
90
+ logger.debug('Removed id_token.');
91
+ await removeSecret('access_token');
92
+ logger.debug('Removed access_token.');
93
+ }
94
+ catch (error) {
95
+ if (error instanceof Error) {
96
+ throw new Error(`Could not remove tokens: ${error.message}`);
97
+ }
98
+ }
99
+ }
100
+ async function exchangeCodeForTokens(state, redirectUri, verifier, code) {
101
+ const response = await fetch(tokenEndpoint, {
102
+ method: 'POST',
103
+ headers: {
104
+ 'Content-Type': 'application/x-www-form-urlencoded'
105
+ },
106
+ body: `grant_type=authorization_code&code=${code}&redirect_uri=${encodeURIComponent(redirectUri)}&client_id=${clientId}&code_verifier=${verifier}&state=${encodeURIComponent(state)}`
107
+ });
108
+ return await response.json();
109
+ }
110
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { loginAndSaveTokens, loadTokens, removeTokens } from './auth.js';
4
+ import { StackAPI } from './stackapi.js';
5
+ import { validateTokens } from './utils.js';
6
+ import { setLogLevel } from './logging.js';
7
+ import { exit } from 'process';
8
+ const program = new Command();
9
+ program
10
+ .name('metaplay-auth')
11
+ .description('Authenticate with Metaplay and get AWS and Kubernetes credentials for game servers.')
12
+ .version('1.0.0')
13
+ .option('-d, --debug', 'enable debug output')
14
+ .hook('preAction', (thisCommand) => {
15
+ // Handle debug flag for all commands.
16
+ const opts = thisCommand.opts();
17
+ if (opts.debug) {
18
+ setLogLevel(0);
19
+ }
20
+ else {
21
+ setLogLevel(10);
22
+ }
23
+ });
24
+ program.command('login')
25
+ .description('login to your Metaplay account')
26
+ .action(async () => {
27
+ await loginAndSaveTokens();
28
+ });
29
+ program.command('logout')
30
+ .description('log out of your Metaplay account')
31
+ .action(async () => {
32
+ console.log('Logging out by removing locally stored tokens...');
33
+ try {
34
+ // TODO: Could check if the tokens existed in the first place and print something nicer if doing an unnecessary operation?
35
+ await removeTokens();
36
+ console.log('Done! You are now logged out.');
37
+ }
38
+ catch (error) {
39
+ if (error instanceof Error) {
40
+ console.error(`Error logging out: ${error.message}`);
41
+ }
42
+ exit(1);
43
+ }
44
+ });
45
+ program.command('show-tokens')
46
+ .description('show loaded tokens')
47
+ .action(async () => {
48
+ try {
49
+ // TODO: Could detect if not logged in and fail more gracefully?
50
+ const tokens = await loadTokens();
51
+ console.log(tokens);
52
+ }
53
+ catch (error) {
54
+ if (error instanceof Error) {
55
+ console.error(`Error showing tokens: ${error.message}`);
56
+ }
57
+ exit(1);
58
+ }
59
+ });
60
+ program.command('get-kubeconfig')
61
+ .description('get kubeconfig for deployment')
62
+ .argument('[gameserver]', 'address of gameserver (e.g. idler-develop.p1.metaplay.io)')
63
+ .option('-s, --stack-api <stack-api-base-path>', 'explicit stack api (e.g. https://infra.p1.metaplay.io/stackapi/)')
64
+ .option('-o, --organization <organization>', 'organization name (e.g. metaplay)')
65
+ .option('-p, --project <project>', 'project name (e.g. idler)')
66
+ .option('-e, --environment <environment>', 'environment name (e.g. develop)')
67
+ .action(async (gameserver, options) => {
68
+ try {
69
+ const tokens = await loadTokens();
70
+ if (!validateTokens(tokens)) {
71
+ throw new Error('Invalid tokens; try to refresh?');
72
+ }
73
+ if (!gameserver && !(options.organization && options.project && options.environment)) {
74
+ 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
+ }
76
+ const stackApi = new StackAPI(tokens.id_token, tokens.access_token);
77
+ if (options.stackApi) {
78
+ stackApi.stack_api_base_uri = options.stackApi;
79
+ }
80
+ const payload = gameserver ? { gameserver } : { organization: options.organization, project: options.project, environment: options.environment };
81
+ const credentials = await stackApi.getKubeConfig(payload);
82
+ console.log(credentials);
83
+ }
84
+ catch (error) {
85
+ if (error instanceof Error) {
86
+ console.error(`Error getting KubeConfig: ${error.message}`);
87
+ }
88
+ exit(1);
89
+ }
90
+ });
91
+ program.command('get-aws-credentials')
92
+ .description('get AWS credentials for deployment')
93
+ .argument('[gameserver]', 'address of gameserver (e.g. idler-develop.p1.metaplay.io)')
94
+ .option('-s, --stack-api <stack-api-base-path>', 'explicit stack api (e.g. https://infra.p1.metaplay.io/stackapi/)')
95
+ .option('-o, --organization <organization>', 'organization name (e.g. metaplay)')
96
+ .option('-p, --project <project>', 'project name (e.g. idler)')
97
+ .option('-e, --environment <environment>', 'environment name (e.g. develop)')
98
+ .option('-f, --format <format>', 'output format (json or env)', 'json')
99
+ .action(async (gameserver, options) => {
100
+ try {
101
+ if (options.format !== 'json' && options.format !== 'env') {
102
+ throw new Error('Invalid format; must be one of json or env');
103
+ }
104
+ const tokens = await loadTokens();
105
+ if (!validateTokens(tokens)) {
106
+ throw new Error('Invalid tokens; try to refresh?');
107
+ }
108
+ if (!gameserver && !(options.organization && options.project && options.environment)) {
109
+ 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
+ }
111
+ const stackApi = new StackAPI(tokens.id_token, tokens.access_token);
112
+ if (options.stackApi) {
113
+ stackApi.stack_api_base_uri = options.stackApi;
114
+ }
115
+ const payload = gameserver ? { gameserver } : { organization: options.organization, project: options.project, environment: options.environment };
116
+ const credentials = await stackApi.getAwsCredentials(payload);
117
+ if (options.format === 'env') {
118
+ console.log(`export AWS_ACCESS_KEY_ID=${credentials.AccessKeyId}`);
119
+ console.log(`export AWS_SECRET_ACCESS_KEY=${credentials.SecretAccessKey}`);
120
+ console.log(`export AWS_SESSION_TOKEN=${credentials.SessionToken}`);
121
+ }
122
+ else {
123
+ console.log(JSON.stringify({
124
+ ...credentials,
125
+ Version: 1 // this is needed to comply with `aws` format for external credential providers
126
+ }));
127
+ }
128
+ }
129
+ catch (error) {
130
+ if (error instanceof Error) {
131
+ console.error(`Error getting AWS credentials: ${error.message}`);
132
+ }
133
+ exit(1);
134
+ }
135
+ });
136
+ program.command('get-environment')
137
+ .description('get environment details for deployment')
138
+ .argument('[gameserver]', 'address of gameserver (e.g. idler-develop.p1.metaplay.io)')
139
+ .option('-s, --stack-api <stack-api-base-path>', 'explicit stack api (e.g. https://infra.p1.metaplay.io/stackapi/)')
140
+ .option('-o, --organization <organization>', 'organization name (e.g. metaplay)')
141
+ .option('-p, --project <project>', 'project name (e.g. idler)')
142
+ .option('-e, --environment <environment>', 'environment name (e.g. develop)')
143
+ .action(async (gameserver, options) => {
144
+ try {
145
+ const tokens = await loadTokens();
146
+ if (!validateTokens(tokens)) {
147
+ throw new Error('Invalid tokens; try to refresh?');
148
+ }
149
+ if (!gameserver && !(options.organization && options.project && options.environment)) {
150
+ 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
+ }
152
+ const stackApi = new StackAPI(tokens.id_token, tokens.access_token);
153
+ if (options.stackApi) {
154
+ stackApi.stack_api_base_uri = options.stackApi;
155
+ }
156
+ const payload = gameserver ? { gameserver } : { organization: options.organization, project: options.project, environment: options.environment };
157
+ const environment = await stackApi.getEnvironmentDetails(payload);
158
+ console.log(JSON.stringify(environment));
159
+ }
160
+ catch (error) {
161
+ if (error instanceof Error) {
162
+ console.error(`Error getting environment details: ${error.message}`);
163
+ }
164
+ exit(1);
165
+ }
166
+ });
167
+ void program.parseAsync();
168
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAE9B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;AAE7B,OAAO;KACJ,IAAI,CAAC,eAAe,CAAC;KACrB,WAAW,CAAC,qFAAqF,CAAC;KAClG,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CAAC,aAAa,EAAE,qBAAqB,CAAC;KAC5C,IAAI,CAAC,WAAW,EAAE,CAAC,WAAW,EAAE,EAAE;IACjC,sCAAsC;IACtC,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,EAAE,CAAA;IAC/B,IAAI,IAAI,CAAC,KAAK,EAAE;QACd,WAAW,CAAC,CAAC,CAAC,CAAA;KACf;SAAM;QACL,WAAW,CAAC,EAAE,CAAC,CAAA;KAChB;AACH,CAAC,CAAC,CAAA;AAEJ,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;KACrB,WAAW,CAAC,gCAAgC,CAAC;KAC7C,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,kBAAkB,EAAE,CAAA;AAC5B,CAAC,CAAC,CAAA;AAEJ,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC;KACtB,WAAW,CAAC,kCAAkC,CAAC;KAC/C,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAA;IAE/D,IAAI;QACF,0HAA0H;QAC1H,MAAM,YAAY,EAAE,CAAA;QACpB,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAA;KAC7C;IAAC,OAAO,KAAK,EAAE;QACd,IAAI,KAAK,YAAY,KAAK,EAAE;YAC1B,OAAO,CAAC,KAAK,CAAC,sBAAsB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;SACrD;QACD,IAAI,CAAC,CAAC,CAAC,CAAA;KACR;AACH,CAAC,CAAC,CAAA;AAEJ,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;KAC3B,WAAW,CAAC,oBAAoB,CAAC;KACjC,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,IAAI;QACF,gEAAgE;QAChE,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAA;QACjC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;KACpB;IAAC,OAAO,KAAK,EAAE;QACd,IAAI,KAAK,YAAY,KAAK,EAAE;YAC1B,OAAO,CAAC,KAAK,CAAC,yBAAyB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;SACxD;QACD,IAAI,CAAC,CAAC,CAAC,CAAA;KACR;AACH,CAAC,CAAC,CAAA;AAEJ,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC;KAC9B,WAAW,CAAC,+BAA+B,CAAC;KAC5C,QAAQ,CAAC,cAAc,EAAE,2DAA2D,CAAC;KACrF,MAAM,CAAC,uCAAuC,EAAE,kEAAkE,CAAC;KACnH,MAAM,CAAC,mCAAmC,EAAE,mCAAmC,CAAC;KAChF,MAAM,CAAC,yBAAyB,EAAE,2BAA2B,CAAC;KAC9D,MAAM,CAAC,iCAAiC,EAAE,iCAAiC,CAAC;KAC5E,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE;IACpC,IAAI;QACF,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAA;QAEjC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE;YAC3B,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;SACnD;QAED,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,WAAW,CAAC,EAAE;YACpF,MAAM,IAAI,KAAK,CAAC,sJAAsJ,CAAC,CAAA;SACxK;QAED,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,YAAY,CAAC,CAAA;QACnE,IAAI,OAAO,CAAC,QAAQ,EAAE;YACpB,QAAQ,CAAC,kBAAkB,GAAG,OAAO,CAAC,QAAQ,CAAA;SAC/C;QAED,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAA;QAEhJ,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;QACzD,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;KACzB;IAAC,OAAO,KAAK,EAAE;QACd,IAAI,KAAK,YAAY,KAAK,EAAE;YAC1B,OAAO,CAAC,KAAK,CAAC,6BAA6B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;SAC5D;QACD,IAAI,CAAC,CAAC,CAAC,CAAA;KACR;AACH,CAAC,CAAC,CAAA;AAEJ,OAAO,CAAC,OAAO,CAAC,qBAAqB,CAAC;KACnC,WAAW,CAAC,oCAAoC,CAAC;KACjD,QAAQ,CAAC,cAAc,EAAE,2DAA2D,CAAC;KACrF,MAAM,CAAC,uCAAuC,EAAE,kEAAkE,CAAC;KACnH,MAAM,CAAC,mCAAmC,EAAE,mCAAmC,CAAC;KAChF,MAAM,CAAC,yBAAyB,EAAE,2BAA2B,CAAC;KAC9D,MAAM,CAAC,iCAAiC,EAAE,iCAAiC,CAAC;KAC5E,MAAM,CAAC,uBAAuB,EAAE,6BAA6B,EAAE,MAAM,CAAC;KACtE,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE;IACpC,IAAI;QACF,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE;YACzD,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;SAC9D;QAED,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAA;QAEjC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE;YAC3B,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;SACnD;QAED,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,WAAW,CAAC,EAAE;YACpF,MAAM,IAAI,KAAK,CAAC,0JAA0J,CAAC,CAAA;SAC5K;QAED,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,YAAY,CAAC,CAAA;QACnE,IAAI,OAAO,CAAC,QAAQ,EAAE;YACpB,QAAQ,CAAC,kBAAkB,GAAG,OAAO,CAAC,QAAQ,CAAA;SAC/C;QAED,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAA;QAEhJ,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAA;QAE7D,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE;YAC5B,OAAO,CAAC,GAAG,CAAC,4BAA4B,WAAW,CAAC,WAAW,EAAE,CAAC,CAAA;YAClE,OAAO,CAAC,GAAG,CAAC,gCAAgC,WAAW,CAAC,eAAe,EAAE,CAAC,CAAA;YAC1E,OAAO,CAAC,GAAG,CAAC,4BAA4B,WAAW,CAAC,YAAY,EAAE,CAAC,CAAA;SACpE;aAAM;YACL,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;gBACzB,GAAG,WAAW;gBACd,OAAO,EAAE,CAAC,CAAC,+EAA+E;aAC3F,CAAC,CAAC,CAAA;SACJ;KACF;IAAC,OAAO,KAAK,EAAE;QACd,IAAI,KAAK,YAAY,KAAK,EAAE;YAC1B,OAAO,CAAC,KAAK,CAAC,kCAAkC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;SACjE;QACD,IAAI,CAAC,CAAC,CAAC,CAAA;KACR;AACH,CAAC,CAAC,CAAA;AAEJ,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC;KAC/B,WAAW,CAAC,wCAAwC,CAAC;KACrD,QAAQ,CAAC,cAAc,EAAE,2DAA2D,CAAC;KACrF,MAAM,CAAC,uCAAuC,EAAE,kEAAkE,CAAC;KACnH,MAAM,CAAC,mCAAmC,EAAE,mCAAmC,CAAC;KAChF,MAAM,CAAC,yBAAyB,EAAE,2BAA2B,CAAC;KAC9D,MAAM,CAAC,iCAAiC,EAAE,iCAAiC,CAAC;KAC5E,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE;IACpC,IAAI;QACF,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAA;QAEjC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE;YAC3B,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;SACnD;QAED,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,WAAW,CAAC,EAAE;YACpF,MAAM,IAAI,KAAK,CAAC,0JAA0J,CAAC,CAAA;SAC5K;QAED,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,YAAY,CAAC,CAAA;QACnE,IAAI,OAAO,CAAC,QAAQ,EAAE;YACpB,QAAQ,CAAC,kBAAkB,GAAG,OAAO,CAAC,QAAQ,CAAA;SAC/C;QAED,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAA;QAEhJ,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAA;QACjE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAA;KACzC;IAAC,OAAO,KAAK,EAAE;QACd,IAAI,KAAK,YAAY,KAAK,EAAE;YAC1B,OAAO,CAAC,KAAK,CAAC,sCAAsC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;SACrE;QACD,IAAI,CAAC,CAAC,CAAC,CAAA;KACR;AACH,CAAC,CAAC,CAAA;AAEJ,KAAK,OAAO,CAAC,UAAU,EAAE,CAAA"}
@@ -0,0 +1,18 @@
1
+ import { Logger } from 'tslog';
2
+ /**
3
+ * The logger instance. Use this to log messages.
4
+ */
5
+ export const logger = new Logger({
6
+ overwrite: {
7
+ transportFormatted(logMetaMarkup, logArgs, logErrors, settings) {
8
+ console.error(`${logMetaMarkup} ${logArgs.join('')} ${logErrors.join('')}`);
9
+ }
10
+ }
11
+ });
12
+ /**
13
+ * Set the log level. We use 0 and 10.
14
+ */
15
+ export function setLogLevel(level) {
16
+ logger.settings.minLevel = level;
17
+ }
18
+ //# sourceMappingURL=logging.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging.js","sourceRoot":"","sources":["../src/logging.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAE9B;;GAEG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC;IAC/B,SAAS,EAAE;QACT,kBAAkB,CAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ;YAC7D,OAAO,CAAC,KAAK,CAAC,GAAG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;QAC7E,CAAC;KACF;CACF,CAAC,CAAA;AAEF;;GAEG;AACH,MAAM,UAAU,WAAW,CAAE,KAAa;IACxC,MAAM,CAAC,QAAQ,CAAC,QAAQ,GAAG,KAAK,CAAA;AAClC,CAAC"}
@@ -0,0 +1,79 @@
1
+ import { promises as fs } from 'fs';
2
+ import { join } from 'path';
3
+ import { randomBytes, createCipheriv, createDecipheriv, scryptSync } from 'crypto';
4
+ import { homedir } from 'os';
5
+ import { logger } from './logging.js';
6
+ const filePath = process.env.METAPLAY_AUTH_FILE ?? join(homedir(), '.metaplay_auth.json');
7
+ const algorithm = 'aes-256-cbc';
8
+ const password = process.env.METAPLAY_AUTH_PASSWORD ?? '';
9
+ const salt = process.env.METAPLAY_AUTH_SALT ?? 'saltysalt';
10
+ async function loadSecrets() {
11
+ try {
12
+ logger.debug(`Loading secrets from ${filePath}...`);
13
+ await fs.access(filePath);
14
+ const data = await fs.readFile(filePath, 'utf8');
15
+ // if password is not provided, treat as unencrypted
16
+ if (password.length === 0) {
17
+ logger.debug('No password provided. Treating as unencrypted.');
18
+ return JSON.parse(data);
19
+ }
20
+ logger.debug('Decrypting data...');
21
+ const decryptedData = decrypt(data);
22
+ return JSON.parse(decryptedData);
23
+ }
24
+ catch (error) {
25
+ const nodeError = error;
26
+ if (nodeError.code === 'ENOENT') {
27
+ // file does not exist, just revert to an empty object
28
+ return {};
29
+ }
30
+ if (password.length === 0) {
31
+ throw new Error('The file is encrypted. Please set the METAPLAY_AUTH_PASSWORD environment variable.');
32
+ }
33
+ throw error;
34
+ }
35
+ }
36
+ function encrypt(text) {
37
+ const key = scryptSync(password, salt, 32);
38
+ const iv = randomBytes(16); // Generate a 16-byte IV
39
+ const cipher = createCipheriv(algorithm, key, iv);
40
+ const encrypted = Buffer.concat([cipher.update(text), cipher.final()]);
41
+ return iv.toString('hex') + ':' + encrypted.toString('hex'); // Store IV with the encrypted data
42
+ }
43
+ function decrypt(text) {
44
+ const textParts = text.split(':');
45
+ if (textParts.length !== 2) {
46
+ throw new Error('Invalid encrypted data format.');
47
+ }
48
+ const iv = Buffer.from(textParts[0], 'hex');
49
+ if (iv.length !== 16) { // Ensure the IV is 16 bytes
50
+ throw new Error('Invalid IV length.');
51
+ }
52
+ const encryptedText = Buffer.from(textParts[1], 'hex');
53
+ const key = scryptSync(password, salt, 32);
54
+ const decipher = createDecipheriv(algorithm, key, iv);
55
+ const decrypted = Buffer.concat([decipher.update(encryptedText), decipher.final()]);
56
+ return decrypted.toString();
57
+ }
58
+ export async function setSecret(key, value) {
59
+ logger.debug(`Setting secret ${key}...`);
60
+ const secrets = await loadSecrets();
61
+ secrets[key] = value;
62
+ const secretJson = JSON.stringify(secrets);
63
+ const content = password.length !== 0 ? encrypt(secretJson) : secretJson;
64
+ await fs.writeFile(filePath, content);
65
+ }
66
+ export async function getSecret(key) {
67
+ logger.debug(`Getting secret ${key}...`);
68
+ const secrets = await loadSecrets();
69
+ return secrets[key];
70
+ }
71
+ export async function removeSecret(key) {
72
+ logger.debug(`Removing secret ${key}...`);
73
+ const currentSecrets = await loadSecrets();
74
+ const { [key]: _, ...secrets } = currentSecrets; // remove key from secrets
75
+ const secretJson = JSON.stringify(secrets);
76
+ const content = password.length !== 0 ? encrypt(secretJson) : secretJson;
77
+ await fs.writeFile(filePath, content);
78
+ }
79
+ //# sourceMappingURL=secret_store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret_store.js","sourceRoot":"","sources":["../src/secret_store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAA;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAClF,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAA;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAErC,MAAM,QAAQ,GAAW,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,qBAAqB,CAAC,CAAA;AACjG,MAAM,SAAS,GAAG,aAAa,CAAA;AAC/B,MAAM,QAAQ,GAAW,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,EAAE,CAAA;AACjE,MAAM,IAAI,GAAW,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,WAAW,CAAA;AAIlE,KAAK,UAAU,WAAW;IACxB,IAAI;QACF,MAAM,CAAC,KAAK,CAAC,wBAAwB,QAAQ,KAAK,CAAC,CAAA;QAEnD,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;QACzB,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QAEhD,oDAAoD;QACpD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;YACzB,MAAM,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAA;YAC9D,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;SACxB;QAED,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAA;QAClC,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;QAEnC,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAA;KACjC;IAAC,OAAO,KAAK,EAAE;QACd,MAAM,SAAS,GAAG,KAA8B,CAAA;QAChD,IAAI,SAAS,CAAC,IAAI,KAAK,QAAQ,EAAE;YAC/B,sDAAsD;YACtD,OAAO,EAAE,CAAA;SACV;QACD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;YACzB,MAAM,IAAI,KAAK,CAAC,oFAAoF,CAAC,CAAA;SACtG;QACD,MAAM,KAAK,CAAA;KACZ;AACH,CAAC;AAED,SAAS,OAAO,CAAE,IAAY;IAC5B,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,CAAA;IAC1C,MAAM,EAAE,GAAG,WAAW,CAAC,EAAE,CAAC,CAAA,CAAC,wBAAwB;IACnD,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC,CAAA;IACjD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;IACtE,OAAO,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA,CAAC,mCAAmC;AACjG,CAAC;AAED,SAAS,OAAO,CAAE,IAAY;IAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IACjC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE;QAC1B,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAA;KAClD;IACD,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;IAC3C,IAAI,EAAE,CAAC,MAAM,KAAK,EAAE,EAAE,EAAE,4BAA4B;QAClD,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAA;KACtC;IACD,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;IACtD,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,CAAA;IAC1C,MAAM,QAAQ,GAAG,gBAAgB,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC,CAAA;IACrD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;IACnF,OAAO,SAAS,CAAC,QAAQ,EAAE,CAAA;AAC7B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAE,GAAW,EAAE,KAAa;IACzD,MAAM,CAAC,KAAK,CAAC,kBAAkB,GAAG,KAAK,CAAC,CAAA;IAExC,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAA;IACnC,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;IACpB,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;IAC1C,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAA;IACxE,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;AACvC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAE,GAAW;IAC1C,MAAM,CAAC,KAAK,CAAC,kBAAkB,GAAG,KAAK,CAAC,CAAA;IAExC,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAA;IACnC,OAAO,OAAO,CAAC,GAAG,CAAC,CAAA;AACrB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAE,GAAW;IAC7C,MAAM,CAAC,KAAK,CAAC,mBAAmB,GAAG,KAAK,CAAC,CAAA;IAEzC,MAAM,cAAc,GAAG,MAAM,WAAW,EAAE,CAAA;IAC1C,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,cAAc,CAAA,CAAC,0BAA0B;IAE1E,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;IAC1C,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAA;IACxE,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;AACvC,CAAC"}
@@ -0,0 +1,116 @@
1
+ import { isValidFQDN, getGameserverAdminUrl } from './utils.js';
2
+ import { logger } from './logging.js';
3
+ export class StackAPI {
4
+ id_token;
5
+ access_token;
6
+ _stack_api_base_uri;
7
+ constructor(idToken, accessToken) {
8
+ if (idToken == null) {
9
+ throw new Error('id_token is missing');
10
+ }
11
+ if (accessToken == null) {
12
+ throw new Error('access_token is missing');
13
+ }
14
+ this.id_token = idToken;
15
+ this.access_token = accessToken;
16
+ this._stack_api_base_uri = 'https://infra.p1.metaplay.io/stackapi';
17
+ }
18
+ get stack_api_base_uri() {
19
+ return this._stack_api_base_uri.replace(/\/$/, '');
20
+ }
21
+ set stack_api_base_uri(uri) {
22
+ uri = uri.replace(/\/$/, ''); // Remove trailing slash
23
+ this._stack_api_base_uri = uri;
24
+ }
25
+ async getAwsCredentials(gs) {
26
+ let url = '';
27
+ if (gs.gameserver != null) {
28
+ if (isValidFQDN(gs.gameserver)) {
29
+ const adminUrl = getGameserverAdminUrl(gs.gameserver);
30
+ url = `https://${adminUrl}/.infra/credentials/aws`;
31
+ }
32
+ else {
33
+ url = `${this.stack_api_base_uri}/v0/credentials/${gs.gameserver}/aws`;
34
+ }
35
+ }
36
+ else if (gs.organization != null && gs.project != null && gs.environment != null) {
37
+ url = `${this.stack_api_base_uri}/v1/servers/${gs.organization}/${gs.project}/${gs.environment}/credentials/aws`;
38
+ }
39
+ else {
40
+ throw new Error('Invalid arguments for getAwsCredentials');
41
+ }
42
+ logger.debug(`Getting AWS credentials from ${url}...`);
43
+ const response = await fetch(url, {
44
+ method: 'POST',
45
+ headers: {
46
+ Authorization: `Bearer ${this.id_token}`,
47
+ 'Content-Type': 'application/json'
48
+ }
49
+ });
50
+ if (response.status !== 200) {
51
+ throw new Error(`Failed to fetch AWS credetials: ${response.statusText}`);
52
+ }
53
+ return await response.json();
54
+ }
55
+ async getKubeConfig(gs) {
56
+ let url = '';
57
+ if (gs.gameserver != null) {
58
+ if (isValidFQDN(gs.gameserver)) {
59
+ const adminUrl = getGameserverAdminUrl(gs.gameserver);
60
+ url = `https://${adminUrl}/.infra/credentials/k8s`;
61
+ }
62
+ else {
63
+ url = `${this.stack_api_base_uri}/v0/credentials/${gs.gameserver}/k8s`;
64
+ }
65
+ }
66
+ else if (gs.organization != null && gs.project != null && gs.environment != null) {
67
+ url = `${this.stack_api_base_uri}/v1/servers/${gs.organization}/${gs.project}/${gs.environment}/credentials/k8s`;
68
+ }
69
+ else {
70
+ throw new Error('Invalid arguments for getKubeConfig');
71
+ }
72
+ logger.debug(`Getting KubeConfig from ${url}...`);
73
+ const response = await fetch(url, {
74
+ method: 'POST',
75
+ headers: {
76
+ Authorization: `Bearer ${this.id_token}`,
77
+ 'Content-Type': 'application/json'
78
+ }
79
+ });
80
+ if (response.status !== 200) {
81
+ throw new Error(`Failed to fetch KubeConfigs: ${response.statusText}`);
82
+ }
83
+ return await response.text();
84
+ }
85
+ async getEnvironmentDetails(gs) {
86
+ let url = '';
87
+ if (gs.gameserver != null) {
88
+ if (isValidFQDN(gs.gameserver)) {
89
+ const adminUrl = getGameserverAdminUrl(gs.gameserver);
90
+ url = `https://${adminUrl}/.infra/environment`;
91
+ }
92
+ else {
93
+ url = `${this.stack_api_base_uri}/v0/deployments/${gs.gameserver}`;
94
+ }
95
+ }
96
+ else if (gs.organization != null && gs.project != null && gs.environment != null) {
97
+ url = `${this.stack_api_base_uri}/v1/servers/${gs.organization}/${gs.project}/${gs.environment}`;
98
+ }
99
+ else {
100
+ throw new Error('Invalid arguments for environment details');
101
+ }
102
+ logger.debug(`Getting environment details from ${url}...`);
103
+ const response = await fetch(url, {
104
+ method: 'GET',
105
+ headers: {
106
+ Authorization: `Bearer ${this.id_token}`,
107
+ 'Content-Type': 'application/json'
108
+ }
109
+ });
110
+ if (response.status !== 200) {
111
+ throw new Error(`Failed to fetch environment details: ${response.statusText}`);
112
+ }
113
+ return await response.json();
114
+ }
115
+ }
116
+ //# sourceMappingURL=stackapi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stackapi.js","sourceRoot":"","sources":["../src/stackapi.ts"],"names":[],"mappings":"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"}
package/dist/utils.js ADDED
@@ -0,0 +1,60 @@
1
+ export function stackApiBaseFromOptions(arg, options) {
2
+ return '';
3
+ }
4
+ /**
5
+ * Checks if the given string is a fully qualified domain name (FQDN).
6
+ *
7
+ * @param domain The domain name to check.
8
+ * @returns true if the domain is a valid FQDN, false otherwise.
9
+ */
10
+ export function isValidFQDN(domain) {
11
+ const fqdnRegex = /^(?=.{1,253}$)(([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,})$/;
12
+ return fqdnRegex.test(domain);
13
+ }
14
+ /**
15
+ * Splits an HTTP URL into its components: scheme, hostname, port, and subpaths.
16
+ *
17
+ * @param urlString The HTTP URL string to be split.
18
+ * @returns An object containing the scheme, hostname, port, and subpaths.
19
+ */
20
+ export function splitUrlComponents(urlString) {
21
+ try {
22
+ const url = new URL(urlString);
23
+ // Extract the scheme (protocol without the ":"), hostname, and port
24
+ const scheme = url.protocol.slice(0, -1); // Removes the trailing ":"
25
+ const hostname = url.hostname;
26
+ const port = url.port ?? null;
27
+ // Extract subpaths (pathname) and remove the leading "/"
28
+ const subpaths = url.pathname.slice(1) ?? null;
29
+ return { scheme, hostname, port, subpaths };
30
+ }
31
+ catch (error) {
32
+ throw new Error('Invalid URL');
33
+ }
34
+ }
35
+ export function getGameserverAdminUrl(uri) {
36
+ const parts = uri.split('.');
37
+ const hostname = parts[0];
38
+ const domain = uri.substring(hostname.length + 1);
39
+ return `${hostname}-admin.${domain}`;
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
+ //# sourceMappingURL=utils.js.map
@@ -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;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"}