Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fluffy-ducks-grin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@smithy/core": minor
---

Resolve auth schemes based on the preference list
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
HttpAuthSchemeParametersProvider,
HttpAuthSchemeProvider,
IdentityProviderConfig,
Provider,
SelectedHttpAuthScheme,
SerializeHandler,
SerializeHandlerArguments,
Expand All @@ -15,10 +16,13 @@ import {
} from "@smithy/types";
import { getSmithyContext } from "@smithy/util-middleware";

import { resolveAuthOptions } from "./resolveAuthOptions";

/**
* @internal
*/
export interface PreviouslyResolved<TParameters extends HttpAuthSchemeParameters> {
authSchemePreference?: Provider<string[]>;
httpAuthSchemes: HttpAuthScheme[];
httpAuthSchemeProvider: HttpAuthSchemeProvider<TParameters>;
}
Expand Down Expand Up @@ -84,10 +88,14 @@ export const httpAuthSchemeMiddleware =
const options = config.httpAuthSchemeProvider(
await mwOptions.httpAuthSchemeParametersProvider(config, context as TContext, args.input)
);

const authSchemePreference = config.authSchemePreference ? await config.authSchemePreference() : [];
const resolvedOptions = resolveAuthOptions(options, authSchemePreference);

const authSchemes = convertHttpAuthSchemesToMap(config.httpAuthSchemes);
const smithyContext: HttpAuthSchemeMiddlewareSmithyContext = getSmithyContext(context);
const failureReasons = [];
for (const option of options) {
for (const option of resolvedOptions) {
const scheme = authSchemes.get(option.schemeId);
if (!scheme) {
failureReasons.push(`HttpAuthScheme \`${option.schemeId}\` was not enabled for this service.`);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { HttpAuthOption } from "@smithy/types";
import { describe, expect, it } from "vitest";

import { resolveAuthOptions } from "./resolveAuthOptions";

describe("resolveAuthSchemes", () => {
const sigv4 = "sigv4";
const sigv4a = "sigv4a";

const mockSigV4AuthScheme = { schemeId: `aws.auth#${sigv4}` } as HttpAuthOption;
const mockSigV4aAuthScheme = { schemeId: `aws.auth#${sigv4a}` } as HttpAuthOption;

it("should return candidate auth schemes is preference list is not available", () => {
const candidateAuthSchemes = [mockSigV4AuthScheme, mockSigV4aAuthScheme];
expect(resolveAuthOptions(candidateAuthSchemes, [])).toEqual(candidateAuthSchemes);

// @ts-expect-error case where callee incorrectly passes undefined
expect(resolveAuthOptions(candidateAuthSchemes)).toEqual(candidateAuthSchemes);
});

it("should return auth scheme from preference if it's available", () => {
expect(resolveAuthOptions([mockSigV4AuthScheme, mockSigV4aAuthScheme], [sigv4a])).toEqual([
mockSigV4aAuthScheme,
mockSigV4AuthScheme,
]);

expect(resolveAuthOptions([mockSigV4AuthScheme, mockSigV4aAuthScheme], [sigv4a, sigv4])).toEqual([
mockSigV4aAuthScheme,
mockSigV4AuthScheme,
]);

expect(resolveAuthOptions([mockSigV4AuthScheme, mockSigV4aAuthScheme], [sigv4, sigv4a])).toEqual([
mockSigV4AuthScheme,
mockSigV4aAuthScheme,
]);
});

it("should ignore auth scheme from preference if it's not available", () => {
expect(resolveAuthOptions([mockSigV4AuthScheme], [sigv4a])).toEqual([mockSigV4AuthScheme]);
expect(resolveAuthOptions([mockSigV4AuthScheme], ["sigv3"])).toEqual([mockSigV4AuthScheme]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { HttpAuthOption } from "@smithy/types";

/**
* Resolves list of auth options based on the supported ones, vs the preference list.
*
* @param candidateAuthOptions list of supported auth options selected by the standard
* resolution process (model-based, endpoints 2.0, etc.)
* @param authSchemePreference list of auth schemes preferred by user.
* @returns
*/
export const resolveAuthOptions = (
candidateAuthOptions: HttpAuthOption[],
authSchemePreference: string[]
): HttpAuthOption[] => {
if (!authSchemePreference || authSchemePreference.length === 0) {
return candidateAuthOptions;
}

// reprioritize candidates based on user's preference
const preferredAuthOptions = [];

for (const preferredSchemeName of authSchemePreference) {
for (const candidateAuthOption of candidateAuthOptions) {
const candidateAuthSchemeName = candidateAuthOption.schemeId.split("#")[1];
if (candidateAuthSchemeName === preferredSchemeName) {
preferredAuthOptions.push(candidateAuthOption);
}
}
}

// add any remaining candidates that weren't in the preference list
for (const candidateAuthOption of candidateAuthOptions) {
if (!preferredAuthOptions.find(({ schemeId }) => schemeId === candidateAuthOption.schemeId)) {
preferredAuthOptions.push(candidateAuthOption);
}
}

return preferredAuthOptions;
};