fix(auth): harden logout proxy traps from review feedback

- get() now returns undefined for logged-out providers instead of
  delegating to target.get() which could bypass the guard
- getCredential() in provider-auth returns undefined for logged-out
  providers instead of falling through to authStorage.get()
- getAll() skips logged-out providers at top of loop
- list() filters modelsJsonApiKeys against loggedOutProviders
- Added remove() trap in provider-auth for clearApiKey flow

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Timothy Laurent
2026-05-05 21:21:33 -07:00
parent dfb255249f
commit b4948a4ee0
2 changed files with 19 additions and 4 deletions

View File

@@ -157,7 +157,7 @@ export function mergeAuthStorageReads(
const getCredential = (providerId: string) => { const getCredential = (providerId: string) => {
if (loggedOutProviders.has(providerId)) { if (loggedOutProviders.has(providerId)) {
return authStorage.get(providerId) as StoredCredential | undefined; return undefined;
} }
return selectCredential(providerId, readAuthStorages); return selectCredential(providerId, readAuthStorages);
}; };
@@ -190,6 +190,13 @@ export function mergeAuthStorageReads(
}; };
} }
if (prop === "remove") {
return (provider: string) => {
loggedOutProviders.add(provider);
target.remove(provider);
};
}
if (prop === "set") { if (prop === "set") {
return (provider: string, credential: AuthCredential) => { return (provider: string, credential: AuthCredential) => {
loggedOutProviders.delete(provider); loggedOutProviders.delete(provider);

View File

@@ -202,7 +202,7 @@ export function createFusionAuthStorage(): AuthStorage {
if (prop === "get") { if (prop === "get") {
return (provider: string) => { return (provider: string) => {
if (loggedOutProviders.has(provider)) { if (loggedOutProviders.has(provider)) {
return target.get(provider); return undefined;
} }
return choosePreferredStoredCredential( return choosePreferredStoredCredential(
target.get(provider) as StoredCredential | undefined, target.get(provider) as StoredCredential | undefined,
@@ -239,9 +239,12 @@ export function createFusionAuthStorage(): AuthStorage {
]); ]);
const merged: Record<string, StoredCredential> = {}; const merged: Record<string, StoredCredential> = {};
for (const providerId of providerIds) { for (const providerId of providerIds) {
if (loggedOutProviders.has(providerId)) {
continue;
}
const credential = choosePreferredStoredCredential( const credential = choosePreferredStoredCredential(
(target.get(providerId) as StoredCredential | undefined), (target.get(providerId) as StoredCredential | undefined),
loggedOutProviders.has(providerId) ? undefined : supplementalCredentials[providerId], supplementalCredentials[providerId],
); );
if (credential) { if (credential) {
merged[providerId] = credential; merged[providerId] = credential;
@@ -253,7 +256,12 @@ export function createFusionAuthStorage(): AuthStorage {
if (prop === "list") { if (prop === "list") {
return () => { return () => {
const providers = new Set([...target.list(), ...modelsJsonApiKeys.keys()]); const providers = new Set([...target.list()]);
for (const p of modelsJsonApiKeys.keys()) {
if (!loggedOutProviders.has(p)) {
providers.add(p);
}
}
for (const p of Object.keys(supplementalCredentials)) { for (const p of Object.keys(supplementalCredentials)) {
if (!loggedOutProviders.has(p)) { if (!loggedOutProviders.has(p)) {
providers.add(p); providers.add(p);