Skip to content

Commit 2af8b45

Browse files
fix(auth): malformed SSO cache didn't prompt reauth (#6164)
## Problem: When we loaded sso cache from disk, we would only invalidate (leading to a reauth prompt) if the cache file was missing. But if the cache file was present, though its content was malformed, we would incorrectly treat it as recoverable by throwing instead of returning undefined. Users would get stuck in a state where all future api calls would fail, and they'd never get a prompt to reauth to fix their SSO session. ## Solution: If we detect a SyntaxError treat it as non-recoverable, meaning it will trigger a reauth. Also added some code to validate the content of the SSO cache we loaded from disk to ensure it is what we expected. Fixes #6140 --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --------- Signed-off-by: nkomonen-amazon <[email protected]>
1 parent 0a40071 commit 2af8b45

File tree

7 files changed

+34
-3
lines changed

7 files changed

+34
-3
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Bug Fix",
3+
"description": "Auth: SSO session was bad, but no reauth prompt given"
4+
}

packages/core/src/auth/sso/cache.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ import { getLogger } from '../../shared/logger/logger'
1010
import fs from '../../shared/fs/fs'
1111
import { createDiskCache, KeyedCache, mapCache } from '../../shared/utilities/cacheUtils'
1212
import { stripUndefined } from '../../shared/utilities/collectionUtils'
13-
import { hasProps, selectFrom } from '../../shared/utilities/tsUtils'
13+
import { getMissingProps, hasProps, selectFrom } from '../../shared/utilities/tsUtils'
1414
import { SsoToken, ClientRegistration } from './model'
1515
import { DevSettings } from '../../shared/settings'
1616
import { onceChanged } from '../../shared/utilities/functionUtils'
1717
import globals from '../../shared/extensionGlobals'
18+
import { ToolkitError } from '../../shared/errors'
1819

1920
interface RegistrationKey {
2021
readonly startUrl: string
@@ -92,6 +93,12 @@ export function getTokenCache(directory = getCacheDir()): KeyedCache<SsoAccess>
9293

9394
stripUndefined(token)
9495

96+
// Validate data is not missing.
97+
const missingProps = getMissingProps(token, 'accessToken', 'refreshToken')
98+
if (missingProps.length > 0) {
99+
throw new ToolkitError(`SSO cache data unexpectedly missing props: ${JSON.stringify(missingProps)}`)
100+
}
101+
95102
return {
96103
token,
97104
registration,

packages/core/src/shared/regions/regionProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { AwsContext } from '../awsContext'
1515
import { getIdeProperties, isAmazonQ, isCloud9 } from '../extensionUtilities'
1616
import { ResourceFetcher } from '../resourcefetcher/resourcefetcher'
1717
import { isSsoConnection } from '../../auth/connection'
18-
import { Auth } from '../../auth'
18+
import { Auth } from '../../auth/auth'
1919

2020
export const defaultRegion = 'us-east-1'
2121
export const defaultPartition = 'aws'

packages/core/src/shared/utilities/cacheUtils.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,23 @@ export function createDiskCache<V, K>(
116116
log('loaded', key)
117117
return result
118118
} catch (error) {
119+
// Non-recoverable errors mean there is no usable data.
120+
// Recoverable errors mean we can possibly use the data for something like
121+
// an SSO token refresh, or to just retry.
122+
// Returning undefined implies non-recoverable.
123+
124+
// -- Non-recoverable Errors --
119125
if (isFileNotFoundError(error)) {
120126
log('read failed (file not found)', key)
121127
return
122128
}
129+
if (error instanceof SyntaxError) {
130+
// file content was malformed or empty
131+
log(`read failed (invalid JSON)`, key)
132+
return
133+
}
134+
135+
// -- Recoverable Errors --
123136
log(`read failed ${error}`, key)
124137
throw createDiskCacheError(error, 'LOAD', target, key)
125138
}

packages/core/src/test/credentials/sso/cache.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import * as path from 'path'
88
import { makeTemporaryToolkitFolder, tryRemoveFolder } from '../../../shared/filesystemUtilities'
99
import { getRegistrationCache, getTokenCache } from '../../../auth/sso/cache'
1010
import { fs } from '../../../shared'
11+
import { SsoToken } from '../../../auth/sso/model'
1112

1213
describe('SSO Cache', function () {
1314
const region = 'dummyRegion'
@@ -26,7 +27,8 @@ describe('SSO Cache', function () {
2627
const validToken = {
2728
accessToken: 'longstringofrandomcharacters',
2829
expiresAt: new Date(Date.now() + hourInMs),
29-
}
30+
refreshToken: 'dummyRefreshToken',
31+
} as SsoToken
3032

3133
beforeEach(async function () {
3234
testDir = await makeTemporaryToolkitFolder()

packages/core/src/test/credentials/sso/ssoAccessTokenProvider.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ describe('SsoAccessTokenProvider', function () {
4545
return {
4646
accessToken: 'dummyAccessToken',
4747
expiresAt: new clock.Date(clock.Date.now() + timeDelta),
48+
refreshToken: 'dummyRefreshToken',
4849
...extras,
4950
}
5051
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Bug Fix",
3+
"description": "Auth: SSO session was bad, but no reauth prompt given"
4+
}

0 commit comments

Comments
 (0)