@metaplay/metaplay-auth 1.4.2 → 1.6.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/.prettierignore +2 -0
- package/CHANGELOG.md +69 -28
- package/dist/index.cjs +292 -0
- package/dist/sshcrypto-OMBCGRSN.node +0 -0
- package/eslint.config.js +3 -0
- package/index.ts +763 -437
- package/package.json +22 -21
- package/prettier.config.js +3 -0
- package/src/auth.ts +121 -80
- package/src/buildCommand.ts +267 -0
- package/src/config.ts +12 -0
- package/src/deployment.ts +285 -52
- package/src/logging.ts +4 -4
- package/src/secret_store.ts +10 -7
- package/src/stackapi.ts +5 -381
- package/src/targetenvironment.ts +311 -0
- package/src/utils.ts +162 -31
- package/src/version.ts +1 -0
- package/dist/index.js +0 -644
- package/dist/index.js.map +0 -1
- package/dist/src/auth.js +0 -373
- package/dist/src/auth.js.map +0 -1
- package/dist/src/deployment.js +0 -250
- package/dist/src/deployment.js.map +0 -1
- package/dist/src/logging.js +0 -18
- package/dist/src/logging.js.map +0 -1
- package/dist/src/secret_store.js +0 -79
- package/dist/src/secret_store.js.map +0 -1
- package/dist/src/stackapi.js +0 -302
- package/dist/src/stackapi.js.map +0 -1
- package/dist/src/utils.js +0 -62
- package/dist/src/utils.js.map +0 -1
- package/dist/tests/utils.spec.js +0 -18
- package/dist/tests/utils.spec.js.map +0 -1
- package/tests/utils.spec.ts +0 -20
- package/vitest.config.ts +0 -7
package/package.json
CHANGED
|
@@ -1,46 +1,47 @@
|
|
|
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.6.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
7
7
|
"homepage": "https://metaplay.io",
|
|
8
8
|
"bin": {
|
|
9
|
-
"metaplay-auth": "dist/index.
|
|
9
|
+
"metaplay-auth": "dist/index.cjs"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"dev": "tsx index.ts",
|
|
13
|
-
"
|
|
13
|
+
"bake-version": "node -p \"'export const PACKAGE_VERSION = ' + JSON.stringify(require('./package.json').version)\" > src/version.ts"
|
|
14
14
|
},
|
|
15
15
|
"publishConfig": {
|
|
16
16
|
"access": "public"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@metaplay/eslint-config": "workspace:*",
|
|
20
|
-
"@metaplay/
|
|
21
|
-
"@
|
|
20
|
+
"@metaplay/prettier-config": "workspace:^",
|
|
21
|
+
"@prettier/plugin-pug": "workspace:^",
|
|
22
|
+
"@types/dockerode": "^3.3.31",
|
|
22
23
|
"@types/express": "^4.17.21",
|
|
23
24
|
"@types/js-yaml": "^4.0.9",
|
|
24
|
-
"@types/jsonwebtoken": "^9.0.
|
|
25
|
+
"@types/jsonwebtoken": "^9.0.6",
|
|
25
26
|
"@types/jwk-to-pem": "^2.0.3",
|
|
26
|
-
"@types/node": "^20.
|
|
27
|
-
"tsx": "^4.7.1",
|
|
28
|
-
"typescript": "^5.1.6",
|
|
29
|
-
"vitest": "^1.4.0"
|
|
30
|
-
},
|
|
31
|
-
"dependencies": {
|
|
32
|
-
"@aws-sdk/client-ecr": "^3.549.0",
|
|
33
|
-
"@kubernetes/client-node": "^1.0.0-rc4",
|
|
34
|
-
"@ory/client": "^1.9.0",
|
|
27
|
+
"@types/node": "^20.16.1",
|
|
35
28
|
"@types/semver": "^7.5.8",
|
|
36
|
-
"
|
|
29
|
+
"esbuild": "^0.23.1",
|
|
30
|
+
"tsx": "^4.19.0",
|
|
31
|
+
"typescript": "5.5.4",
|
|
32
|
+
"vitest": "^2.0.5",
|
|
33
|
+
"@aws-sdk/client-ecr": "^3.637.0",
|
|
34
|
+
"@kubernetes/client-node": "^1.0.0-rc6",
|
|
35
|
+
"@ory/client": "^1.14.5",
|
|
36
|
+
"commander": "^12.1.0",
|
|
37
37
|
"dockerode": "^4.0.2",
|
|
38
|
-
"h3": "^1.
|
|
38
|
+
"h3": "^1.12.0",
|
|
39
39
|
"js-yaml": "^4.1.0",
|
|
40
40
|
"jsonwebtoken": "^9.0.2",
|
|
41
|
-
"jwk-to-pem": "^2.0.
|
|
42
|
-
"open": "^
|
|
43
|
-
"semver": "^7.6.
|
|
44
|
-
"tslog": "^4.9.
|
|
41
|
+
"jwk-to-pem": "^2.0.6",
|
|
42
|
+
"open": "^8.4.2",
|
|
43
|
+
"semver": "^7.6.3",
|
|
44
|
+
"tslog": "^4.9.3",
|
|
45
|
+
"prettier": "3.3.3"
|
|
45
46
|
}
|
|
46
47
|
}
|
package/src/auth.ts
CHANGED
|
@@ -9,26 +9,36 @@ import jwt from 'jsonwebtoken'
|
|
|
9
9
|
import jwkToPem from 'jwk-to-pem'
|
|
10
10
|
import { Configuration, WellknownApi, OidcApi } from '@ory/client'
|
|
11
11
|
import { setSecret, getSecret, removeSecret } from './secret_store.js'
|
|
12
|
-
|
|
12
|
+
import { portalBaseUrl } from './config.js'
|
|
13
13
|
import { logger } from './logging.js'
|
|
14
14
|
|
|
15
|
+
export interface TokenSet {
|
|
16
|
+
id_token?: string
|
|
17
|
+
access_token: string
|
|
18
|
+
refresh_token?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
// oauth2 client details (maybe move these to be discovered from some online location to make changes easier to manage?)
|
|
16
22
|
const clientId = 'c16ea663-ced3-46c6-8f85-38c9681fe1f0'
|
|
17
23
|
const baseURL = 'https://auth.metaplay.dev'
|
|
18
24
|
const authorizationEndpoint = `${baseURL}/oauth2/auth`
|
|
19
25
|
const tokenEndpoint = `${baseURL}/oauth2/token`
|
|
20
|
-
const wellknownApi = new WellknownApi(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
const wellknownApi = new WellknownApi(
|
|
27
|
+
new Configuration({
|
|
28
|
+
basePath: baseURL,
|
|
29
|
+
})
|
|
30
|
+
)
|
|
31
|
+
const oidcApi = new OidcApi(
|
|
32
|
+
new Configuration({
|
|
33
|
+
basePath: baseURL,
|
|
34
|
+
})
|
|
35
|
+
)
|
|
26
36
|
|
|
27
37
|
/**
|
|
28
38
|
* A helper function which generates a code verifier and challenge for exchaning code from Ory server.
|
|
29
39
|
* @returns
|
|
30
40
|
*/
|
|
31
|
-
function generateCodeVerifierAndChallenge
|
|
41
|
+
function generateCodeVerifierAndChallenge(): { verifier: string; challenge: string } {
|
|
32
42
|
const verifier: string = randomBytes(32).toString('hex')
|
|
33
43
|
const challenge: string = createHash('sha256').update(verifier).digest('base64url')
|
|
34
44
|
return { verifier, challenge }
|
|
@@ -39,7 +49,7 @@ function generateCodeVerifierAndChallenge (): { verifier: string, challenge: str
|
|
|
39
49
|
* @param token The token to fetch the userinfo for.
|
|
40
50
|
* @returns An object containing the user's info.
|
|
41
51
|
*/
|
|
42
|
-
export async function getUserinfo
|
|
52
|
+
export async function getUserinfo(token: string): Promise<any> {
|
|
43
53
|
logger.debug('Trying to find OIDC well-known endpoints...')
|
|
44
54
|
const oidcRes = await oidcApi.discoverOidcConfiguration()
|
|
45
55
|
|
|
@@ -51,8 +61,8 @@ export async function getUserinfo (token: string): Promise<any> {
|
|
|
51
61
|
|
|
52
62
|
const userinfoRes = await fetch(userinfoEndpoint, {
|
|
53
63
|
headers: {
|
|
54
|
-
Authorization: `Bearer ${token}
|
|
55
|
-
}
|
|
64
|
+
Authorization: `Bearer ${token}`,
|
|
65
|
+
},
|
|
56
66
|
})
|
|
57
67
|
|
|
58
68
|
if (userinfoRes.status < 200 || userinfoRes.status >= 300) {
|
|
@@ -66,14 +76,14 @@ export async function getUserinfo (token: string): Promise<any> {
|
|
|
66
76
|
* A helper function which finds an local available port to listen on.
|
|
67
77
|
* @returns A promise that resolves to an available port.
|
|
68
78
|
*/
|
|
69
|
-
async function findAvailablePort
|
|
79
|
+
async function findAvailablePort(): Promise<number> {
|
|
70
80
|
return await new Promise((resolve, reject) => {
|
|
71
81
|
// Ports need to be in sync with oauth2 client callbacks.
|
|
72
82
|
const portsToCheck = [5000, 5001, 5002, 5003, 5004]
|
|
73
83
|
let index = 0
|
|
74
84
|
|
|
75
85
|
// Test ports by opening a server on them.
|
|
76
|
-
function tryNextPort
|
|
86
|
+
function tryNextPort(): void {
|
|
77
87
|
if (index >= portsToCheck.length) {
|
|
78
88
|
reject(new Error('Could not find an available port.'))
|
|
79
89
|
}
|
|
@@ -103,7 +113,7 @@ async function findAvailablePort (): Promise<number> {
|
|
|
103
113
|
/**
|
|
104
114
|
* Log in and save tokens to the local secret store.
|
|
105
115
|
*/
|
|
106
|
-
export async function loginAndSaveTokens
|
|
116
|
+
export async function loginAndSaveTokens(): Promise<void> {
|
|
107
117
|
// Find an available port to listen on.
|
|
108
118
|
const availablePort = await findAvailablePort()
|
|
109
119
|
|
|
@@ -113,52 +123,57 @@ export async function loginAndSaveTokens () {
|
|
|
113
123
|
const state = randomBytes(16).toString('hex')
|
|
114
124
|
|
|
115
125
|
// Create a /callback endpoint that exchanges the code for tokens.
|
|
116
|
-
app.use(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
error_description: errorDescription,
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
126
|
+
app.use(
|
|
127
|
+
'/callback',
|
|
128
|
+
defineEventHandler(async (event) => {
|
|
129
|
+
// Read the query parameters.
|
|
130
|
+
const { error, error_description: errorDescription, code } = getQuery(event)
|
|
131
|
+
|
|
132
|
+
// Raise an error if the query parameters contain an error message.
|
|
133
|
+
if (error) {
|
|
134
|
+
console.error(
|
|
135
|
+
`Error logging in. Received the following error:\n\n${String(error)}: ${String(errorDescription)}`
|
|
136
|
+
)
|
|
137
|
+
sendError(event, new Error(`Authentication failed: ${String(error)}: ${String(errorDescription)}`))
|
|
138
|
+
server.close()
|
|
139
|
+
process.exit(1)
|
|
140
|
+
}
|
|
131
141
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
142
|
+
// Raise an error if the query parameters do not contain a code.
|
|
143
|
+
if (typeof code !== 'string') {
|
|
144
|
+
console.error('Error logging in. No code received.')
|
|
145
|
+
sendError(event, new Error('Authentication failed: No code received.'))
|
|
146
|
+
server.close()
|
|
147
|
+
process.exit(1)
|
|
148
|
+
}
|
|
139
149
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
150
|
+
// Exchange the code for tokens.
|
|
151
|
+
try {
|
|
152
|
+
logger.debug(`Received callback request with code ${code}. Preparing to exchange for tokens...`)
|
|
153
|
+
const tokens = await getTokensWithAuthorizationCode(state, redirectUri, verifier, code)
|
|
144
154
|
|
|
145
|
-
|
|
146
|
-
|
|
155
|
+
// Only save access_token, id_token, and refresh_token
|
|
156
|
+
await saveTokens({
|
|
157
|
+
access_token: tokens.access_token,
|
|
158
|
+
id_token: tokens.id_token,
|
|
159
|
+
refresh_token: tokens.refresh_token,
|
|
160
|
+
})
|
|
147
161
|
|
|
148
|
-
|
|
162
|
+
console.log('You are now logged in and can call the other commands.')
|
|
149
163
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
164
|
+
// TODO: Could return a pre-generated HTML page instead of text?
|
|
165
|
+
return 'Authentication successful! You can close this window.'
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (error instanceof Error) {
|
|
168
|
+
console.error(`Error: ${error.message}`)
|
|
169
|
+
}
|
|
170
|
+
} finally {
|
|
171
|
+
server.close()
|
|
155
172
|
}
|
|
156
|
-
} finally {
|
|
157
|
-
server.close()
|
|
158
|
-
}
|
|
159
173
|
|
|
160
|
-
|
|
161
|
-
|
|
174
|
+
process.exit(0)
|
|
175
|
+
})
|
|
176
|
+
)
|
|
162
177
|
|
|
163
178
|
// Start the server.
|
|
164
179
|
// We use use H3 and adapt it to Node.js's http server.
|
|
@@ -167,12 +182,14 @@ export async function loginAndSaveTokens () {
|
|
|
167
182
|
logger.debug(`Listening on port ${availablePort} and waiting for callback...`)
|
|
168
183
|
|
|
169
184
|
// Open the browser to log in.
|
|
170
|
-
const authorizationUrl
|
|
171
|
-
console.log(
|
|
185
|
+
const authorizationUrl = `${authorizationEndpoint}?response_type=code&client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&code_challenge=${challenge}&code_challenge_method=S256&scope=${encodeURIComponent('openid offline_access')}&state=${encodeURIComponent(state)}`
|
|
186
|
+
console.log(
|
|
187
|
+
`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`
|
|
188
|
+
)
|
|
172
189
|
void open(authorizationUrl)
|
|
173
190
|
}
|
|
174
191
|
|
|
175
|
-
export async function machineLoginAndSaveTokens
|
|
192
|
+
export async function machineLoginAndSaveTokens(clientId: string, clientSecret: string): Promise<void> {
|
|
176
193
|
// Get a fresh access token from Metaplay Auth.
|
|
177
194
|
const params = new URLSearchParams()
|
|
178
195
|
params.set('grant_type', 'client_credentials')
|
|
@@ -189,27 +206,29 @@ export async function machineLoginAndSaveTokens (clientId: string, clientSecret:
|
|
|
189
206
|
})
|
|
190
207
|
|
|
191
208
|
// Return type checked manually by Teemu on 2024-3-7.
|
|
192
|
-
const tokens = await res.json() as { access_token: string
|
|
209
|
+
const tokens = (await res.json()) as { access_token: string; token_type: string; expires_in: number; scope: string }
|
|
193
210
|
|
|
194
211
|
logger.debug('Received machine authentication tokens, saving them for future use...')
|
|
195
212
|
|
|
196
213
|
await saveTokens({ access_token: tokens.access_token })
|
|
197
214
|
|
|
198
|
-
const userInfoResponse = await fetch(
|
|
215
|
+
const userInfoResponse = await fetch(`${portalBaseUrl}/api/external/userinfo`, {
|
|
199
216
|
headers: {
|
|
200
|
-
Authorization: `Bearer ${tokens.access_token}
|
|
217
|
+
Authorization: `Bearer ${tokens.access_token}`,
|
|
201
218
|
},
|
|
202
219
|
})
|
|
203
220
|
|
|
204
|
-
const userInfo = await userInfoResponse.json() as { given_name: string
|
|
221
|
+
const userInfo = (await userInfoResponse.json()) as { given_name: string; family_name: string }
|
|
205
222
|
|
|
206
|
-
console.log(
|
|
223
|
+
console.log(
|
|
224
|
+
`You are now logged in with machine user ${userInfo.given_name} ${userInfo.family_name} (clientId=${clientId}) and can execute the other commands.`
|
|
225
|
+
)
|
|
207
226
|
}
|
|
208
227
|
|
|
209
228
|
/**
|
|
210
229
|
* Refresh access and ID token with a refresh token.
|
|
211
230
|
*/
|
|
212
|
-
export async function extendCurrentSession
|
|
231
|
+
export async function extendCurrentSession(): Promise<void> {
|
|
213
232
|
try {
|
|
214
233
|
const tokens = await loadTokens()
|
|
215
234
|
|
|
@@ -222,13 +241,19 @@ export async function extendCurrentSession (): Promise<void> {
|
|
|
222
241
|
|
|
223
242
|
// Check that the refresh_token exists (machine users don't have it)
|
|
224
243
|
if (!tokens.refresh_token) {
|
|
225
|
-
throw new Error(
|
|
244
|
+
throw new Error(
|
|
245
|
+
'Cannot refresh an access_token without a refresh_token. With machine users, should just login again instead.'
|
|
246
|
+
)
|
|
226
247
|
}
|
|
227
248
|
|
|
228
249
|
logger.debug('Access token is no longer valid, trying to extend the current session with a refresh token.')
|
|
229
250
|
const refreshedTokens = await extendCurrentSessionWithRefreshToken(tokens.refresh_token)
|
|
230
251
|
|
|
231
|
-
await saveTokens({
|
|
252
|
+
await saveTokens({
|
|
253
|
+
access_token: refreshedTokens.access_token,
|
|
254
|
+
id_token: refreshedTokens.id_token,
|
|
255
|
+
refresh_token: refreshedTokens.refresh_token,
|
|
256
|
+
})
|
|
232
257
|
} catch (error) {
|
|
233
258
|
if (error instanceof Error) {
|
|
234
259
|
console.error(error.message)
|
|
@@ -242,13 +267,15 @@ export async function extendCurrentSession (): Promise<void> {
|
|
|
242
267
|
* @param refreshToken: The refresh token to use to get a new set of tokens.
|
|
243
268
|
* @returns A promise that resolves to a new set of tokens.
|
|
244
269
|
*/
|
|
245
|
-
async function extendCurrentSessionWithRefreshToken
|
|
270
|
+
async function extendCurrentSessionWithRefreshToken(
|
|
271
|
+
refreshToken: string
|
|
272
|
+
): Promise<{ id_token: string; access_token: string; refresh_token: string }> {
|
|
246
273
|
// TODO: similar to the todo task in getTokensWithAuthorizationCode, http request can be handled by ory/client.
|
|
247
274
|
const params = new URLSearchParams({
|
|
248
275
|
grant_type: 'refresh_token',
|
|
249
276
|
refresh_token: refreshToken,
|
|
250
277
|
scope: 'openid offline_access',
|
|
251
|
-
client_id: clientId
|
|
278
|
+
client_id: clientId,
|
|
252
279
|
})
|
|
253
280
|
|
|
254
281
|
logger.debug('Refreshing tokens...')
|
|
@@ -268,14 +295,16 @@ async function extendCurrentSessionWithRefreshToken (refreshToken: string): Prom
|
|
|
268
295
|
logger.error(`Failed to refresh tokens via endpoint ${tokenEndpoint}`)
|
|
269
296
|
logger.error('Fetch error details:', error)
|
|
270
297
|
if (error.cause?.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE') {
|
|
271
|
-
throw new Error(
|
|
298
|
+
throw new Error(
|
|
299
|
+
`Failed to refresh tokens: SSL certificate validation failed for ${tokenEndpoint}. Is someone trying to tamper with your internet connection?`
|
|
300
|
+
)
|
|
272
301
|
}
|
|
273
302
|
throw new Error(`Failed to refresh tokens via ${tokenEndpoint}: ${error}`)
|
|
274
303
|
}
|
|
275
304
|
|
|
276
305
|
// Check if the response is OK
|
|
277
306
|
if (!response.ok) {
|
|
278
|
-
const responseJSON = await response.json()
|
|
307
|
+
const responseJSON = (await response.json()) as { error: string; error_description: string }
|
|
279
308
|
|
|
280
309
|
logger.error('Failed to refresh tokens.')
|
|
281
310
|
logger.error(`Error Type: ${responseJSON.error}`)
|
|
@@ -288,7 +317,7 @@ async function extendCurrentSessionWithRefreshToken (refreshToken: string): Prom
|
|
|
288
317
|
throw new Error('Failed extending current session, exiting. Please log in again.')
|
|
289
318
|
}
|
|
290
319
|
|
|
291
|
-
return await response.json()
|
|
320
|
+
return (await response.json()) as { id_token: string; access_token: string; refresh_token: string }
|
|
292
321
|
}
|
|
293
322
|
|
|
294
323
|
/**
|
|
@@ -299,22 +328,29 @@ async function extendCurrentSessionWithRefreshToken (refreshToken: string): Prom
|
|
|
299
328
|
* @param code
|
|
300
329
|
* @returns
|
|
301
330
|
*/
|
|
302
|
-
|
|
331
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
332
|
+
async function getTokensWithAuthorizationCode(
|
|
333
|
+
state: string,
|
|
334
|
+
redirectUri: string,
|
|
335
|
+
verifier: string,
|
|
336
|
+
code: string
|
|
337
|
+
): Promise<{ id_token: string; access_token: string; refresh_token: string }> {
|
|
303
338
|
// TODO: the authorication code exchange flow might be better to be handled by ory/client, could check if there's any useful toosl there.
|
|
304
339
|
try {
|
|
305
340
|
const response = await fetch(tokenEndpoint, {
|
|
306
341
|
method: 'POST',
|
|
307
342
|
headers: {
|
|
308
|
-
'Content-Type': 'application/x-www-form-urlencoded'
|
|
343
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
309
344
|
},
|
|
310
|
-
body: `grant_type=authorization_code&code=${code}&redirect_uri=${encodeURIComponent(redirectUri)}&client_id=${clientId}&code_verifier=${verifier}&state=${encodeURIComponent(state)}
|
|
345
|
+
body: `grant_type=authorization_code&code=${code}&redirect_uri=${encodeURIComponent(redirectUri)}&client_id=${clientId}&code_verifier=${verifier}&state=${encodeURIComponent(state)}`,
|
|
311
346
|
})
|
|
312
347
|
|
|
313
|
-
return await response.json()
|
|
348
|
+
return (await response.json()) as { id_token: string; access_token: string; refresh_token: string }
|
|
314
349
|
} catch (error) {
|
|
315
350
|
if (error instanceof Error) {
|
|
316
351
|
logger.error(`Error exchanging code for tokens: ${error.message}`)
|
|
317
352
|
}
|
|
353
|
+
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
318
354
|
throw error
|
|
319
355
|
}
|
|
320
356
|
}
|
|
@@ -322,9 +358,9 @@ async function getTokensWithAuthorizationCode (state: string, redirectUri: strin
|
|
|
322
358
|
/**
|
|
323
359
|
* Load tokens from the local secret store.
|
|
324
360
|
*/
|
|
325
|
-
export async function loadTokens
|
|
361
|
+
export async function loadTokens(): Promise<TokenSet> {
|
|
326
362
|
try {
|
|
327
|
-
const tokens = await getSecret('tokens') as
|
|
363
|
+
const tokens = (await getSecret('tokens')) as TokenSet
|
|
328
364
|
|
|
329
365
|
if (!tokens) {
|
|
330
366
|
throw new Error('Unable to load tokens. You need to login first.')
|
|
@@ -335,6 +371,7 @@ export async function loadTokens (): Promise<{ id_token?: string, access_token:
|
|
|
335
371
|
if (error instanceof Error) {
|
|
336
372
|
throw new Error(`Error loading tokens: ${error.message}`)
|
|
337
373
|
}
|
|
374
|
+
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
338
375
|
throw error
|
|
339
376
|
}
|
|
340
377
|
}
|
|
@@ -343,13 +380,15 @@ export async function loadTokens (): Promise<{ id_token?: string, access_token:
|
|
|
343
380
|
* Save tokens to the local secret store.
|
|
344
381
|
* @param tokens The tokens to save.
|
|
345
382
|
*/
|
|
346
|
-
export async function saveTokens
|
|
383
|
+
export async function saveTokens(tokens: Record<string, string>): Promise<void> {
|
|
347
384
|
try {
|
|
348
385
|
logger.debug('Received new tokens, verifying...')
|
|
349
386
|
|
|
350
387
|
// All tokens must have an access_token (machine users only have it)
|
|
351
388
|
if (!tokens.access_token) {
|
|
352
|
-
throw new Error(
|
|
389
|
+
throw new Error(
|
|
390
|
+
'Metaplay token has no access_token. Please log in again and make sure all checkboxes of permissions are selected before proceeding.'
|
|
391
|
+
)
|
|
353
392
|
}
|
|
354
393
|
|
|
355
394
|
logger.debug('Token verification completed, storing tokens...')
|
|
@@ -363,6 +402,7 @@ export async function saveTokens (tokens: Record<string, string>): Promise<void>
|
|
|
363
402
|
if (error instanceof Error) {
|
|
364
403
|
throw new Error(`Failed to save tokens: ${error.message}`)
|
|
365
404
|
}
|
|
405
|
+
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
366
406
|
throw error
|
|
367
407
|
}
|
|
368
408
|
}
|
|
@@ -370,7 +410,7 @@ export async function saveTokens (tokens: Record<string, string>): Promise<void>
|
|
|
370
410
|
/**
|
|
371
411
|
* Remove tokens from the local secret store.
|
|
372
412
|
*/
|
|
373
|
-
export async function removeTokens
|
|
413
|
+
export async function removeTokens(): Promise<void> {
|
|
374
414
|
try {
|
|
375
415
|
await removeSecret('tokens')
|
|
376
416
|
logger.debug('Removed tokens.')
|
|
@@ -378,6 +418,7 @@ export async function removeTokens (): Promise<void> {
|
|
|
378
418
|
if (error instanceof Error) {
|
|
379
419
|
throw new Error(`Error removing tokens: ${error.message}`)
|
|
380
420
|
}
|
|
421
|
+
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
381
422
|
throw error
|
|
382
423
|
}
|
|
383
424
|
}
|
|
@@ -387,7 +428,7 @@ export async function removeTokens (): Promise<void> {
|
|
|
387
428
|
* @param token The token being validated
|
|
388
429
|
* @returns return if token is valid, throw errors otherwise
|
|
389
430
|
*/
|
|
390
|
-
async function validateToken
|
|
431
|
+
async function validateToken(token: string): Promise<boolean> {
|
|
391
432
|
try {
|
|
392
433
|
// Decode the token
|
|
393
434
|
const completeTokenData = jwt.decode(token, { complete: true })
|
|
@@ -422,7 +463,7 @@ async function validateToken (token: string): Promise<boolean> {
|
|
|
422
463
|
* A helper function which shows token info after being fully decoded.
|
|
423
464
|
* @param token The token to show info for.
|
|
424
465
|
*/
|
|
425
|
-
async function showTokenInfo
|
|
466
|
+
async function showTokenInfo(token: string): Promise<void> {
|
|
426
467
|
logger.debug('Showing access token info...')
|
|
427
468
|
// Decode the token
|
|
428
469
|
const completeTokenData = jwt.decode(token, { complete: true })
|