Skip to content

Commit 0645186

Browse files
feat(auth): read API key from CLI access token. fixes #562 (#563)
Eases use of CLI from GitHub actions and avoids some legacy hard-coding of API Keys
1 parent 8b93cae commit 0645186

File tree

5 files changed

+60
-17
lines changed

5 files changed

+60
-17
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
"@adobe/aio-lib-core-errors": "^3.0.1",
1111
"@adobe/aio-lib-core-logging": "^1.2.0",
1212
"@adobe/aio-lib-core-networking": "^2.0.0",
13-
"@adobe/aio-lib-env": "^1.1.0",
1413
"@adobe/aio-lib-ims": "^5.0.0",
1514
"@oclif/command": "^1.6.1",
1615
"@oclif/config": "^1.15.1",
@@ -20,6 +19,7 @@
2019
"figures": "^3.2.0",
2120
"halfred": "^2.0.0",
2221
"inquirer": "^8.1.0",
22+
"jsonwebtoken": "^8.5.1",
2323
"lodash": "^4.17.15",
2424
"moment": "^2.29.0"
2525
},
@@ -36,10 +36,10 @@
3636
"eslint-config-standard": "14.1.1",
3737
"eslint-plugin-import": "2.25.2",
3838
"eslint-plugin-jest": "23.20.0",
39+
"eslint-plugin-jsdoc": "25.4.3",
3940
"eslint-plugin-node": "10.0.0",
4041
"eslint-plugin-promise": "4.3.1",
4142
"eslint-plugin-standard": "4.1.0",
42-
"eslint-plugin-jsdoc": "25.4.3",
4343
"execa": "5.1.1",
4444
"fetch-mock": "9.11.0",
4545
"husky": "5.2.0",

src/ConfigurationErrors.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,5 @@ E('IMS_CONTEXT_MISSING_FIELDS', 'One or more of the required fields in %s were n
5757
E('IMS_CONTEXT_MISSING_METASCOPE', 'The configuration %s is missing the required metascope %s.')
5858
E('CLI_AUTH_EXPLICIT_NO_AUTH', 'cli context explicitly enabled, but not authenticated. You must run "aio auth:login" first.')
5959
E('CLI_AUTH_EXPLICIT_NO_ORG', 'cli context explicitly enabled but no org id specified. Configure using either "cloudmanager_orgid" or by running "aio cloudmanager:org:select"')
60+
E('CLI_AUTH_CONTEXT_CANNOT_DECODE', 'The access token configured for cli authentication cannot be decoded.')
61+
E('CLI_AUTH_CONTEXT_NO_CLIENT_ID', 'The decoded access token configured for cli authentication does not have a client_id.')

src/cloudmanager-helpers.js

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,17 @@ const Config = require('@adobe/aio-lib-core-config')
1414
const { init } = require('@adobe/aio-lib-cloudmanager')
1515
const { cli } = require('cli-ux')
1616
const { context, getToken, Ims } = require('@adobe/aio-lib-ims')
17-
const { getCliEnv, DEFAULT_ENV } = require('@adobe/aio-lib-env')
1817
const moment = require('moment')
1918
const _ = require('lodash')
2019
const { CLI } = require('@adobe/aio-lib-ims/src/context')
20+
const jwt = require('jsonwebtoken')
2121
const { defaultImsContextName: defaultContextName, exitCodes } = require('./constants')
2222
const { codes: validationCodes } = require('./ValidationErrors')
2323
const { codes: configurationCodes } = require('./ConfigurationErrors')
2424

2525
const SERVICE_ACCOUNT = 'service_account'
2626
const CLI_AUTH = 'cli_auth'
2727

28-
const CLI_API_KEYS = {
29-
prod: 'aio-cli-console-auth',
30-
stage: 'aio-cli-console-auth-stage',
31-
}
32-
3328
const SERVICE_CODES = [
3429
'dma_aem_cloud',
3530
'dma_aem_ams',
@@ -154,11 +149,21 @@ function setCliOrgId (orgId, local) {
154149
async function initSdk (contextName) {
155150
let apiKey
156151
let orgId
152+
let accessToken
157153

158154
if (isCliAuthEnabled()) {
159-
const imsEnv = getCliEnv() || DEFAULT_ENV
160-
apiKey = CLI_API_KEYS[imsEnv]
161155
contextName = CLI
156+
accessToken = await getToken(contextName)
157+
158+
// no need here to validate the token
159+
const decodedToken = jwt.decode(accessToken)
160+
if (!decodedToken) {
161+
throw new configurationCodes.CLI_AUTH_CONTEXT_CANNOT_DECODE()
162+
}
163+
apiKey = decodedToken.client_id
164+
if (!apiKey) {
165+
throw new configurationCodes.CLI_AUTH_CONTEXT_NO_CLIENT_ID()
166+
}
162167
orgId = getCliOrgId()
163168
} else {
164169
contextName = contextName || defaultContextName
@@ -168,10 +173,9 @@ async function initSdk (contextName) {
168173
}
169174
apiKey = contextData.data.client_id
170175
orgId = contextData.data.ims_org_id
176+
accessToken = await getToken(contextName)
171177
}
172178

173-
const accessToken = await getToken(contextName)
174-
175179
const baseUrl = getBaseUrl()
176180

177181
return await init(orgId, apiKey, accessToken, baseUrl)

test/__mocks__/jsonwebtoken.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
Copyright 2021 Adobe. All rights reserved.
3+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License. You may obtain a copy
5+
of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
7+
Unless required by applicable law or agreed to in writing, software distributed under
8+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
OF ANY KIND, either express or implied. See the License for the specific language
10+
governing permissions and limitations under the License.
11+
*/
12+
13+
const DEFAULT_DECODED_TOKEN = {
14+
client_id: 'fake-client-id',
15+
}
16+
let decodedToken = DEFAULT_DECODED_TOKEN
17+
18+
module.exports = {
19+
decode: () => decodedToken,
20+
setDecodedToken: newToken => {
21+
decodedToken = newToken
22+
},
23+
resetDecodedToken: () => {
24+
decodedToken = DEFAULT_DECODED_TOKEN
25+
},
26+
}

test/cloudmanager-helpers.test.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ const { setCurrentOrgId, context } = require('@adobe/aio-lib-ims')
1414
const { setStore } = require('@adobe/aio-lib-core-config')
1515
const { initSdk, getOutputFormat, columnWithArray, disableCliAuth, enableCliAuth, formatDuration } = require('../src/cloudmanager-helpers')
1616
const { init } = require('@adobe/aio-lib-cloudmanager')
17+
const { setDecodedToken, resetDecodedToken } = require('jsonwebtoken')
1718

1819
beforeEach(() => {
1920
disableCliAuth()
2021
setStore({})
22+
resetDecodedToken()
2123
})
2224

2325
test('initSdk - base url config -- no config', async () => {
@@ -93,18 +95,27 @@ test('initSdk - cli context', async () => {
9395
cloudmanager_orgid: 'something',
9496
})
9597
await initSdk()
96-
await expect(init).toHaveBeenCalledWith('something', 'aio-cli-console-auth', 'fake-token', 'https://cloudmanager.adobe.io')
98+
await expect(init).toHaveBeenCalledWith('something', 'fake-client-id', 'fake-token', 'https://cloudmanager.adobe.io')
9799
})
98100

99-
test('initSdk - cli context stage env', async () => {
101+
test('initSdk - cli cannot decode token', async () => {
100102
enableCliAuth()
103+
setDecodedToken(null)
101104

102105
setStore({
103106
cloudmanager_orgid: 'something',
104-
'cli.env': 'stage',
105107
})
106-
await initSdk()
107-
await expect(init).toHaveBeenCalledWith('something', 'aio-cli-console-auth-stage', 'fake-token', 'https://cloudmanager.adobe.io')
108+
await expect(initSdk).rejects.toThrow('[CloudManagerCLI:CLI_AUTH_CONTEXT_CANNOT_DECODE] The access token configured for cli authentication cannot be decoded.')
109+
})
110+
111+
test('initSdk - decoded token does not have client_id', async () => {
112+
enableCliAuth()
113+
setDecodedToken({})
114+
115+
setStore({
116+
cloudmanager_orgid: 'something',
117+
})
118+
await expect(initSdk).rejects.toThrow('[CloudManagerCLI:CLI_AUTH_CONTEXT_NO_CLIENT_ID] The decoded access token configured for cli authentication does not have a client_id.')
108119
})
109120

110121
test('formatDuration -- empty', async () => {

0 commit comments

Comments
 (0)