@adobe/aio-commerce-lib-auth 0.1.0 → 0.2.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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +16 -0
- package/README.md +74 -13
- package/dist/cjs/index.cjs +179 -86
- package/dist/cjs/index.d.cts +79 -30
- package/dist/es/index.d.ts +79 -30
- package/dist/es/index.js +176 -86
- package/package.json +5 -2
- package/source/index.ts +23 -2
- package/source/lib/ims-auth/provider.ts +160 -0
- package/source/lib/ims-auth/schema.ts +87 -0
- package/source/lib/integration-auth/provider.ts +145 -0
- package/source/lib/integration-auth/schema.ts +87 -0
- package/test/ims-auth.test.ts +119 -41
- package/test/integration-auth.test.ts +97 -34
- package/source/lib/ims-auth.ts +0 -95
- package/source/lib/integration-auth.ts +0 -95
- package/source/lib/params.ts +0 -34
package/dist/es/index.d.ts
CHANGED
|
@@ -1,39 +1,88 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ErrorType, Result } from "@adobe/aio-commerce-lib-core/result";
|
|
2
|
+
import * as valibot0 from "valibot";
|
|
3
|
+
import { InferInput, InferIssue, InferOutput } from "valibot";
|
|
4
|
+
import { ValidationErrorType } from "@adobe/aio-commerce-lib-core/validation";
|
|
5
|
+
import * as url39 from "url";
|
|
2
6
|
|
|
3
|
-
//#region source/lib/ims-auth.d.ts
|
|
4
|
-
declare const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
//#region source/lib/ims-auth/schema.d.ts
|
|
8
|
+
declare const IMS_AUTH_ENV: {
|
|
9
|
+
readonly PROD: "prod";
|
|
10
|
+
readonly STAGE: "stage";
|
|
11
|
+
};
|
|
12
|
+
declare const ImsAuthEnvSchema: valibot0.EnumSchema<{
|
|
13
|
+
readonly PROD: "prod";
|
|
14
|
+
readonly STAGE: "stage";
|
|
15
|
+
}, undefined>;
|
|
16
|
+
declare const ImsAuthParamsSchema: valibot0.ObjectSchema<{
|
|
17
|
+
readonly AIO_COMMERCE_IMS_CLIENT_ID: valibot0.SchemaWithPipe<readonly [valibot0.StringSchema<`Expected a string value for the IMS auth parameter ${string}`>, valibot0.NonEmptyAction<string, `Expected a non-empty string value for the IMS auth parameter ${string}`>]>;
|
|
18
|
+
readonly AIO_COMMERCE_IMS_CLIENT_SECRETS: valibot0.SchemaWithPipe<readonly [valibot0.SchemaWithPipe<readonly [valibot0.StringSchema<`Expected a string value for the IMS auth parameter ${string}`>, valibot0.NonEmptyAction<string, `Expected a non-empty string value for the IMS auth parameter ${string}`>, valibot0.ParseJsonAction<string, undefined, undefined>]>, valibot0.ArraySchema<valibot0.StringSchema<undefined>, `Expected a stringified JSON array value for the IMS auth parameter ${string}`>]>;
|
|
19
|
+
readonly AIO_COMMERCE_IMS_TECHNICAL_ACCOUNT_ID: valibot0.SchemaWithPipe<readonly [valibot0.StringSchema<`Expected a string value for the IMS auth parameter ${string}`>, valibot0.NonEmptyAction<string, `Expected a non-empty string value for the IMS auth parameter ${string}`>]>;
|
|
20
|
+
readonly AIO_COMMERCE_IMS_TECHNICAL_ACCOUNT_EMAIL: valibot0.SchemaWithPipe<readonly [valibot0.StringSchema<`Expected a string value for the IMS auth parameter ${string}`>, valibot0.NonEmptyAction<string, `Expected a non-empty string value for the IMS auth parameter ${string}`>]>;
|
|
21
|
+
readonly AIO_COMMERCE_IMS_IMS_ORG_ID: valibot0.SchemaWithPipe<readonly [valibot0.StringSchema<`Expected a string value for the IMS auth parameter ${string}`>, valibot0.NonEmptyAction<string, `Expected a non-empty string value for the IMS auth parameter ${string}`>]>;
|
|
22
|
+
readonly AIO_COMMERCE_IMS_ENV: valibot0.SchemaWithPipe<readonly [valibot0.OptionalSchema<valibot0.EnumSchema<{
|
|
23
|
+
readonly PROD: "prod";
|
|
24
|
+
readonly STAGE: "stage";
|
|
25
|
+
}, undefined>, "prod">]>;
|
|
26
|
+
readonly AIO_COMMERCE_IMS_CTX: valibot0.SchemaWithPipe<readonly [valibot0.OptionalSchema<valibot0.StringSchema<undefined>, "aio-commerce-sdk-creds">]>;
|
|
27
|
+
readonly AIO_COMMERCE_IMS_SCOPES: valibot0.SchemaWithPipe<readonly [valibot0.SchemaWithPipe<readonly [valibot0.StringSchema<`Expected a string value for the IMS auth parameter ${string}`>, valibot0.NonEmptyAction<string, `Expected a non-empty string value for the IMS auth parameter ${string}`>, valibot0.ParseJsonAction<string, undefined, undefined>]>, valibot0.ArraySchema<valibot0.StringSchema<undefined>, `Expected a stringified JSON array value for the IMS auth parameter ${string}`>]>;
|
|
28
|
+
}, undefined>;
|
|
29
|
+
type ImsAuthParams = InferOutput<typeof ImsAuthParamsSchema>;
|
|
30
|
+
type ImsAuthEnv = InferOutput<typeof ImsAuthEnvSchema>;
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region source/lib/ims-auth/provider.d.ts
|
|
10
33
|
type ImsAccessToken = string;
|
|
34
|
+
type ImsAuthHeader = "Authorization" | "x-api-key";
|
|
35
|
+
type ImsAuthHeaders = Record<ImsAuthHeader, string>;
|
|
36
|
+
type ImsAuthValidationError = ValidationErrorType<"ImsAuthValidationError", InferIssue<typeof ImsAuthParamsSchema>[]>;
|
|
37
|
+
type ImsAuthError<TError = unknown> = ErrorType<"ImsAuthError", {
|
|
38
|
+
message: string;
|
|
39
|
+
error: TError;
|
|
40
|
+
}>;
|
|
41
|
+
interface ImsAuthConfig {
|
|
42
|
+
clientId: string;
|
|
43
|
+
clientSecrets: string[];
|
|
44
|
+
technicalAccountId: string;
|
|
45
|
+
technicalAccountEmail: string;
|
|
46
|
+
imsOrgId: string;
|
|
47
|
+
scopes: string[];
|
|
48
|
+
environment: ImsAuthEnv;
|
|
49
|
+
context: string;
|
|
50
|
+
}
|
|
11
51
|
interface ImsAuthProvider {
|
|
12
|
-
|
|
13
|
-
|
|
52
|
+
getAccessToken: () => Promise<Result<ImsAccessToken, ImsAuthError>>;
|
|
53
|
+
getHeaders: () => Promise<Result<ImsAuthHeaders, ImsAuthError>>;
|
|
14
54
|
}
|
|
15
|
-
declare function getImsAuthProvider(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
55
|
+
declare function getImsAuthProvider(config: ImsAuthConfig): ImsAuthProvider;
|
|
56
|
+
declare function tryGetImsAuthProvider(params: InferInput<typeof ImsAuthParamsSchema>): Result<ImsAuthProvider, ImsAuthValidationError>;
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region source/lib/integration-auth/schema.d.ts
|
|
59
|
+
declare const HttpMethodSchema: valibot0.PicklistSchema<readonly ["GET", "POST", "PUT", "PATCH", "DELETE"], undefined>;
|
|
60
|
+
type HttpMethodInput = InferInput<typeof HttpMethodSchema>;
|
|
61
|
+
declare const UrlSchema: valibot0.SchemaWithPipe<readonly [valibot0.UnionSchema<[valibot0.SchemaWithPipe<readonly [valibot0.StringSchema<"Expected a string for the Adobe Commerce endpoint">, valibot0.NonEmptyAction<string, "Expected a non-empty string for the Adobe Commerce endpoint">, valibot0.UrlAction<string, "Expected a valid url for the Adobe Commerce endpoint">]>, valibot0.InstanceSchema<typeof url39.URL, undefined>], undefined>, valibot0.TransformAction<string | url39.URL, string>]>;
|
|
62
|
+
type AdobeCommerceUri = InferInput<typeof UrlSchema>;
|
|
63
|
+
declare const IntegrationAuthParamsSchema: valibot0.NonOptionalSchema<valibot0.ObjectSchema<{
|
|
64
|
+
readonly AIO_COMMERCE_INTEGRATIONS_CONSUMER_KEY: valibot0.SchemaWithPipe<readonly [valibot0.StringSchema<`Expected a string value for the Commerce Integration parameter ${string}`>, valibot0.NonEmptyAction<string, `Expected a non-empty string value for the Commerce Integration parameter ${string}`>]>;
|
|
65
|
+
readonly AIO_COMMERCE_INTEGRATIONS_CONSUMER_SECRET: valibot0.SchemaWithPipe<readonly [valibot0.StringSchema<`Expected a string value for the Commerce Integration parameter ${string}`>, valibot0.NonEmptyAction<string, `Expected a non-empty string value for the Commerce Integration parameter ${string}`>]>;
|
|
66
|
+
readonly AIO_COMMERCE_INTEGRATIONS_ACCESS_TOKEN: valibot0.SchemaWithPipe<readonly [valibot0.StringSchema<`Expected a string value for the Commerce Integration parameter ${string}`>, valibot0.NonEmptyAction<string, `Expected a non-empty string value for the Commerce Integration parameter ${string}`>]>;
|
|
67
|
+
readonly AIO_COMMERCE_INTEGRATIONS_ACCESS_TOKEN_SECRET: valibot0.SchemaWithPipe<readonly [valibot0.StringSchema<`Expected a string value for the Commerce Integration parameter ${string}`>, valibot0.NonEmptyAction<string, `Expected a non-empty string value for the Commerce Integration parameter ${string}`>]>;
|
|
68
|
+
}, undefined>, undefined>;
|
|
69
|
+
type IntegrationAuthParams = InferInput<typeof IntegrationAuthParamsSchema>;
|
|
22
70
|
//#endregion
|
|
23
|
-
//#region source/lib/integration-auth.d.ts
|
|
24
|
-
|
|
25
|
-
declare const INTEGRATION_AUTH_HEADERS: readonly ["Authorization"];
|
|
26
|
-
declare const INTEGRATION_AUTH_PARAMS: readonly ["AIO_COMMERCE_INTEGRATIONS_CONSUMER_KEY", "AIO_COMMERCE_INTEGRATIONS_CONSUMER_SECRET", "AIO_COMMERCE_INTEGRATIONS_ACCESS_TOKEN", "AIO_COMMERCE_INTEGRATIONS_ACCESS_TOKEN_SECRET"];
|
|
27
|
-
type IntegrationAuthParam = (typeof INTEGRATION_AUTH_PARAMS)[number];
|
|
28
|
-
type IntegrationAuthParams = Partial<Record<IntegrationAuthParam, string>>;
|
|
29
|
-
type IntegrationAuthHeader = (typeof INTEGRATION_AUTH_HEADERS)[number];
|
|
71
|
+
//#region source/lib/integration-auth/provider.d.ts
|
|
72
|
+
type IntegrationAuthHeader = "Authorization";
|
|
30
73
|
type IntegrationAuthHeaders = Record<IntegrationAuthHeader, string>;
|
|
31
|
-
|
|
74
|
+
interface IntegrationConfig {
|
|
75
|
+
consumerKey: string;
|
|
76
|
+
consumerSecret: string;
|
|
77
|
+
accessToken: string;
|
|
78
|
+
accessTokenSecret: string;
|
|
79
|
+
}
|
|
80
|
+
type ValidationIssues = InferIssue<typeof IntegrationAuthParamsSchema>[] | InferIssue<typeof UrlSchema>[];
|
|
81
|
+
type IntegrationAuthError = ValidationErrorType<"IntegrationAuthValidationError", ValidationIssues>;
|
|
32
82
|
interface IntegrationAuthProvider {
|
|
33
|
-
getHeaders: (method:
|
|
83
|
+
getHeaders: (method: HttpMethodInput, url: AdobeCommerceUri) => Result<IntegrationAuthHeaders, IntegrationAuthError>;
|
|
34
84
|
}
|
|
35
|
-
declare function getIntegrationAuthProvider(
|
|
36
|
-
|
|
37
|
-
} | undefined;
|
|
85
|
+
declare function getIntegrationAuthProvider(config: IntegrationConfig): IntegrationAuthProvider;
|
|
86
|
+
declare function tryGetIntegrationAuthProvider(params: IntegrationAuthParams): Result<IntegrationAuthProvider, IntegrationAuthError>;
|
|
38
87
|
//#endregion
|
|
39
|
-
export {
|
|
88
|
+
export { IMS_AUTH_ENV, ImsAuthConfig, ImsAuthEnv, ImsAuthError, ImsAuthParams, ImsAuthProvider, IntegrationAuthError, IntegrationAuthParams, IntegrationAuthProvider, IntegrationConfig, getImsAuthProvider, getIntegrationAuthProvider, tryGetImsAuthProvider, tryGetIntegrationAuthProvider };
|
package/dist/es/index.js
CHANGED
|
@@ -1,111 +1,201 @@
|
|
|
1
|
+
import { err, map, ok } from "@adobe/aio-commerce-lib-core/result";
|
|
1
2
|
import { context, getToken } from "@adobe/aio-lib-ims";
|
|
3
|
+
import { array, enum as enum$1, instance, message, nonEmpty, nonOptional, object, optional, parseJson, picklist, pipe, safeParse, string, transform, union, url } from "valibot";
|
|
2
4
|
import crypto from "node:crypto";
|
|
3
5
|
import OAuth1a from "oauth-1.0a";
|
|
4
6
|
|
|
5
|
-
//#region source/lib/
|
|
7
|
+
//#region source/lib/ims-auth/schema.ts
|
|
8
|
+
const imsAuthParameter = (name) => pipe(string(`Expected a string value for the IMS auth parameter ${name}`), nonEmpty(`Expected a non-empty string value for the IMS auth parameter ${name}`));
|
|
9
|
+
const jsonStringArray = (name) => {
|
|
10
|
+
const jsonStringArraySchema = message(pipe(string(`Expected a string value for the IMS auth parameter ${name}`), nonEmpty(`Expected a non-empty string value for the IMS auth parameter ${name}`), parseJson()), `An error occurred while parsing the JSON string array parameter ${name}`);
|
|
11
|
+
return pipe(jsonStringArraySchema, array(string(), `Expected a stringified JSON array value for the IMS auth parameter ${name}`));
|
|
12
|
+
};
|
|
13
|
+
/** The environments accepted by the IMS auth service. */
|
|
14
|
+
const IMS_AUTH_ENV = {
|
|
15
|
+
PROD: "prod",
|
|
16
|
+
STAGE: "stage"
|
|
17
|
+
};
|
|
18
|
+
const ImsAuthEnvSchema = enum$1(IMS_AUTH_ENV);
|
|
19
|
+
/** Defines the schema to validate the necessary parameters for the IMS auth service. */
|
|
20
|
+
const ImsAuthParamsSchema = object({
|
|
21
|
+
AIO_COMMERCE_IMS_CLIENT_ID: imsAuthParameter("AIO_COMMERCE_IMS_CLIENT_ID"),
|
|
22
|
+
AIO_COMMERCE_IMS_CLIENT_SECRETS: jsonStringArray("AIO_COMMERCE_IMS_CLIENT_SECRETS"),
|
|
23
|
+
AIO_COMMERCE_IMS_TECHNICAL_ACCOUNT_ID: imsAuthParameter("AIO_COMMERCE_IMS_TECHNICAL_ACCOUNT_ID"),
|
|
24
|
+
AIO_COMMERCE_IMS_TECHNICAL_ACCOUNT_EMAIL: imsAuthParameter("AIO_COMMERCE_IMS_TECHNICAL_ACCOUNT_EMAIL"),
|
|
25
|
+
AIO_COMMERCE_IMS_IMS_ORG_ID: imsAuthParameter("AIO_COMMERCE_IMS_IMS_ORG_ID"),
|
|
26
|
+
AIO_COMMERCE_IMS_ENV: pipe(optional(ImsAuthEnvSchema, IMS_AUTH_ENV.PROD)),
|
|
27
|
+
AIO_COMMERCE_IMS_CTX: pipe(optional(string(), "aio-commerce-sdk-creds")),
|
|
28
|
+
AIO_COMMERCE_IMS_SCOPES: jsonStringArray("AIO_COMMERCE_IMS_SCOPES")
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region source/lib/ims-auth/provider.ts
|
|
33
|
+
function snakeCaseImsAuthConfig(config) {
|
|
34
|
+
return {
|
|
35
|
+
...config,
|
|
36
|
+
client_id: config.clientId,
|
|
37
|
+
client_secrets: config.clientSecrets,
|
|
38
|
+
technical_account_id: config.technicalAccountId,
|
|
39
|
+
technical_account_email: config.technicalAccountEmail,
|
|
40
|
+
ims_org_id: config.imsOrgId
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function makeImsAuthValidationError(message$1, issues) {
|
|
44
|
+
return {
|
|
45
|
+
_tag: "ImsAuthValidationError",
|
|
46
|
+
message: message$1,
|
|
47
|
+
issues
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function makeImsAuthError(message$1, error) {
|
|
51
|
+
return {
|
|
52
|
+
_tag: "ImsAuthError",
|
|
53
|
+
message: message$1,
|
|
54
|
+
error
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function fromParams$1(params) {
|
|
58
|
+
return {
|
|
59
|
+
clientId: params.AIO_COMMERCE_IMS_CLIENT_ID,
|
|
60
|
+
clientSecrets: params.AIO_COMMERCE_IMS_CLIENT_SECRETS,
|
|
61
|
+
technicalAccountId: params.AIO_COMMERCE_IMS_TECHNICAL_ACCOUNT_ID,
|
|
62
|
+
technicalAccountEmail: params.AIO_COMMERCE_IMS_TECHNICAL_ACCOUNT_EMAIL,
|
|
63
|
+
imsOrgId: params.AIO_COMMERCE_IMS_IMS_ORG_ID,
|
|
64
|
+
scopes: params.AIO_COMMERCE_IMS_SCOPES,
|
|
65
|
+
environment: params.AIO_COMMERCE_IMS_ENV,
|
|
66
|
+
context: params.AIO_COMMERCE_IMS_CTX
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
async function tryGetAccessToken(contextName) {
|
|
70
|
+
try {
|
|
71
|
+
const accessToken = await getToken(contextName, {});
|
|
72
|
+
return ok(accessToken);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
return err(makeImsAuthError("Failed to retrieve IMS access token", error));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
6
77
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* @
|
|
10
|
-
* @param value of the parameter.
|
|
78
|
+
* Creates an {@link ImsAuthProvider} based on the provided configuration.
|
|
79
|
+
* @param config The configuration for the IMS Auth Provider.
|
|
80
|
+
* @returns An {@link ImsAuthProvider} instance that can be used to get access token and auth headers.
|
|
11
81
|
*/
|
|
12
|
-
function
|
|
13
|
-
const
|
|
14
|
-
|
|
82
|
+
function getImsAuthProvider(config) {
|
|
83
|
+
const getAccessToken = async () => {
|
|
84
|
+
const snakeCasedConfig = snakeCaseImsAuthConfig(config);
|
|
85
|
+
await context.set(config.context, snakeCasedConfig);
|
|
86
|
+
return tryGetAccessToken(config.context);
|
|
87
|
+
};
|
|
88
|
+
const getHeaders = async () => {
|
|
89
|
+
const result = await getAccessToken();
|
|
90
|
+
return map(result, (accessToken) => ({
|
|
91
|
+
Authorization: `Bearer ${accessToken}`,
|
|
92
|
+
"x-api-key": config.clientId
|
|
93
|
+
}));
|
|
94
|
+
};
|
|
95
|
+
return {
|
|
96
|
+
getAccessToken,
|
|
97
|
+
getHeaders
|
|
98
|
+
};
|
|
15
99
|
}
|
|
16
100
|
/**
|
|
17
|
-
*
|
|
18
|
-
* @param params
|
|
19
|
-
* @
|
|
101
|
+
* Tries to create an {@link ImsAuthProvider} based on the provided parameters.
|
|
102
|
+
* @param params The parameters required to create the IMS Auth Provider.
|
|
103
|
+
* @returns An {@link ImsAuthProvider} instance that can be used to get access token and auth headers.
|
|
20
104
|
*/
|
|
21
|
-
function
|
|
22
|
-
|
|
105
|
+
function tryGetImsAuthProvider(params) {
|
|
106
|
+
const validation = safeParse(ImsAuthParamsSchema, params);
|
|
107
|
+
if (!validation.success) return err(makeImsAuthValidationError("Failed to validate the provided IMS parameters", validation.issues));
|
|
108
|
+
return ok(getImsAuthProvider(fromParams$1(validation.output)));
|
|
23
109
|
}
|
|
24
110
|
|
|
25
111
|
//#endregion
|
|
26
|
-
//#region source/lib/
|
|
112
|
+
//#region source/lib/integration-auth/schema.ts
|
|
27
113
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
114
|
+
* The HTTP methods supported by Commerce.
|
|
115
|
+
* This is used to determine which headers to include in the signing of the authorization header.
|
|
30
116
|
*/
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
function resolveImsConfig(params) {
|
|
49
|
-
if (allNonEmpty(params, [
|
|
50
|
-
"AIO_COMMERCE_IMS_CLIENT_ID",
|
|
51
|
-
"AIO_COMMERCE_IMS_CLIENT_SECRETS",
|
|
52
|
-
"AIO_COMMERCE_IMS_TECHNICAL_ACCOUNT_ID",
|
|
53
|
-
"AIO_COMMERCE_IMS_TECHNICAL_ACCOUNT_EMAIL",
|
|
54
|
-
"AIO_COMMERCE_IMS_IMS_ORG_ID",
|
|
55
|
-
"AIO_COMMERCE_IMS_SCOPES"
|
|
56
|
-
])) return {
|
|
57
|
-
client_id: params.AIO_COMMERCE_IMS_CLIENT_ID,
|
|
58
|
-
client_secrets: JSON.parse(params.AIO_COMMERCE_IMS_CLIENT_SECRETS ?? "[]"),
|
|
59
|
-
technical_account_id: params.AIO_COMMERCE_IMS_TECHNICAL_ACCOUNT_ID,
|
|
60
|
-
technical_account_email: params.AIO_COMMERCE_IMS_TECHNICAL_ACCOUNT_EMAIL,
|
|
61
|
-
ims_org_id: params.AIO_COMMERCE_IMS_IMS_ORG_ID,
|
|
62
|
-
scopes: JSON.parse(params.AIO_COMMERCE_IMS_SCOPES ?? "[]"),
|
|
63
|
-
environment: params.AIO_COMMERCE_IMS_ENV ?? "prod"
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
//#endregion
|
|
68
|
-
//#region source/lib/integration-auth.ts
|
|
117
|
+
const AllowedHttpMethod = [
|
|
118
|
+
"GET",
|
|
119
|
+
"POST",
|
|
120
|
+
"PUT",
|
|
121
|
+
"PATCH",
|
|
122
|
+
"DELETE"
|
|
123
|
+
];
|
|
124
|
+
const HttpMethodSchema = picklist(AllowedHttpMethod);
|
|
125
|
+
const integrationAuthParameter = (name) => pipe(string(`Expected a string value for the Commerce Integration parameter ${name}`), nonEmpty(`Expected a non-empty string value for the Commerce Integration parameter ${name}`));
|
|
126
|
+
const BaseUrlSchema = pipe(string("Expected a string for the Adobe Commerce endpoint"), nonEmpty("Expected a non-empty string for the Adobe Commerce endpoint"), url("Expected a valid url for the Adobe Commerce endpoint"));
|
|
127
|
+
const UrlSchema = pipe(union([BaseUrlSchema, instance(URL)]), transform((url$1) => {
|
|
128
|
+
if (url$1 instanceof URL) return url$1.toString();
|
|
129
|
+
return url$1;
|
|
130
|
+
}));
|
|
69
131
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
132
|
+
* The schema for the Commerce Integration parameters.
|
|
133
|
+
* This is used to validate the parameters passed to the Commerce Integration provider.
|
|
72
134
|
*/
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return { getHeaders(method, url) {
|
|
89
|
-
return oauth.toHeader(oauth.authorize({
|
|
90
|
-
url,
|
|
91
|
-
method
|
|
92
|
-
}, oauthToken));
|
|
93
|
-
} };
|
|
94
|
-
}
|
|
135
|
+
const IntegrationAuthParamsSchema = nonOptional(object({
|
|
136
|
+
AIO_COMMERCE_INTEGRATIONS_CONSUMER_KEY: integrationAuthParameter("AIO_COMMERCE_INTEGRATIONS_CONSUMER_KEY"),
|
|
137
|
+
AIO_COMMERCE_INTEGRATIONS_CONSUMER_SECRET: integrationAuthParameter("AIO_COMMERCE_INTEGRATIONS_CONSUMER_SECRET"),
|
|
138
|
+
AIO_COMMERCE_INTEGRATIONS_ACCESS_TOKEN: integrationAuthParameter("AIO_COMMERCE_INTEGRATIONS_ACCESS_TOKEN"),
|
|
139
|
+
AIO_COMMERCE_INTEGRATIONS_ACCESS_TOKEN_SECRET: integrationAuthParameter("AIO_COMMERCE_INTEGRATIONS_ACCESS_TOKEN_SECRET")
|
|
140
|
+
}));
|
|
141
|
+
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region source/lib/integration-auth/provider.ts
|
|
144
|
+
function makeIntegrationAuthValidationError(message$1, issues) {
|
|
145
|
+
return {
|
|
146
|
+
_tag: "IntegrationAuthValidationError",
|
|
147
|
+
message: message$1,
|
|
148
|
+
issues
|
|
149
|
+
};
|
|
95
150
|
}
|
|
96
|
-
function
|
|
97
|
-
|
|
98
|
-
"AIO_COMMERCE_INTEGRATIONS_CONSUMER_KEY",
|
|
99
|
-
"AIO_COMMERCE_INTEGRATIONS_CONSUMER_SECRET",
|
|
100
|
-
"AIO_COMMERCE_INTEGRATIONS_ACCESS_TOKEN",
|
|
101
|
-
"AIO_COMMERCE_INTEGRATIONS_ACCESS_TOKEN_SECRET"
|
|
102
|
-
])) return {
|
|
151
|
+
function fromParams(params) {
|
|
152
|
+
return {
|
|
103
153
|
consumerKey: params.AIO_COMMERCE_INTEGRATIONS_CONSUMER_KEY,
|
|
104
154
|
consumerSecret: params.AIO_COMMERCE_INTEGRATIONS_CONSUMER_SECRET,
|
|
105
155
|
accessToken: params.AIO_COMMERCE_INTEGRATIONS_ACCESS_TOKEN,
|
|
106
156
|
accessTokenSecret: params.AIO_COMMERCE_INTEGRATIONS_ACCESS_TOKEN_SECRET
|
|
107
157
|
};
|
|
108
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* Creates an {@link IntegrationAuthProvider} based on the provided configuration.
|
|
161
|
+
* @param config The configuration for the integration.
|
|
162
|
+
* @returns An {@link IntegrationAuthProvider} instance that can be used to get auth headers.
|
|
163
|
+
*/
|
|
164
|
+
function getIntegrationAuthProvider(config) {
|
|
165
|
+
const oauth = new OAuth1a({
|
|
166
|
+
consumer: {
|
|
167
|
+
key: config.consumerKey,
|
|
168
|
+
secret: config.consumerSecret
|
|
169
|
+
},
|
|
170
|
+
signature_method: "HMAC-SHA256",
|
|
171
|
+
hash_function: (baseString, key) => crypto.createHmac("sha256", key).update(baseString).digest("base64")
|
|
172
|
+
});
|
|
173
|
+
const oauthToken = {
|
|
174
|
+
key: config.accessToken,
|
|
175
|
+
secret: config.accessTokenSecret
|
|
176
|
+
};
|
|
177
|
+
const getHeaders = (method, url$1) => {
|
|
178
|
+
const uriValidation = safeParse(UrlSchema, url$1);
|
|
179
|
+
if (!uriValidation.success) return err(makeIntegrationAuthValidationError("Failed to validate the provided Adobe Commerce URL", uriValidation.issues));
|
|
180
|
+
const finalUrl = uriValidation.output;
|
|
181
|
+
const headers = oauth.toHeader(oauth.authorize({
|
|
182
|
+
url: finalUrl,
|
|
183
|
+
method
|
|
184
|
+
}, oauthToken));
|
|
185
|
+
return ok(headers);
|
|
186
|
+
};
|
|
187
|
+
return { getHeaders };
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Tries to create an {@link IntegrationAuthProvider} based on the provided parameters.
|
|
191
|
+
* @param params The parameters required for integration authentication.
|
|
192
|
+
* @returns An {@link IntegrationAuthProvider} instance that can be used to get auth headers.
|
|
193
|
+
*/
|
|
194
|
+
function tryGetIntegrationAuthProvider(params) {
|
|
195
|
+
const validation = safeParse(IntegrationAuthParamsSchema, params);
|
|
196
|
+
if (!validation.success) return err(makeIntegrationAuthValidationError("Failed to validate the provided integration parameters", validation.issues));
|
|
197
|
+
return ok(getIntegrationAuthProvider(fromParams(validation.output)));
|
|
198
|
+
}
|
|
109
199
|
|
|
110
200
|
//#endregion
|
|
111
|
-
export { getImsAuthProvider, getIntegrationAuthProvider };
|
|
201
|
+
export { IMS_AUTH_ENV, getImsAuthProvider, getIntegrationAuthProvider, tryGetImsAuthProvider, tryGetIntegrationAuthProvider };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/aio-commerce-lib-auth",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Adobe Inc.",
|
|
7
7
|
"engines": {
|
|
@@ -42,7 +42,10 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@adobe/aio-lib-ims": "^8.1.0",
|
|
45
|
-
"
|
|
45
|
+
"ansis": "^4.1.0",
|
|
46
|
+
"oauth-1.0a": "^2.2.6",
|
|
47
|
+
"valibot": "^1.1.0",
|
|
48
|
+
"@adobe/aio-commerce-lib-core": "0.2.0"
|
|
46
49
|
},
|
|
47
50
|
"devDependencies": {
|
|
48
51
|
"vitest": "^3.2.4",
|
package/source/index.ts
CHANGED
|
@@ -10,5 +10,26 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
export
|
|
14
|
-
|
|
13
|
+
export {
|
|
14
|
+
getImsAuthProvider,
|
|
15
|
+
type ImsAuthConfig,
|
|
16
|
+
type ImsAuthError,
|
|
17
|
+
type ImsAuthProvider,
|
|
18
|
+
tryGetImsAuthProvider,
|
|
19
|
+
} from "./lib/ims-auth/provider";
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
IMS_AUTH_ENV,
|
|
23
|
+
type ImsAuthEnv,
|
|
24
|
+
type ImsAuthParams,
|
|
25
|
+
} from "./lib/ims-auth/schema";
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
getIntegrationAuthProvider,
|
|
29
|
+
type IntegrationAuthError,
|
|
30
|
+
type IntegrationAuthProvider,
|
|
31
|
+
type IntegrationConfig,
|
|
32
|
+
tryGetIntegrationAuthProvider,
|
|
33
|
+
} from "./lib/integration-auth/provider";
|
|
34
|
+
|
|
35
|
+
export type { IntegrationAuthParams } from "./lib/integration-auth/schema";
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ErrorType,
|
|
3
|
+
err,
|
|
4
|
+
map,
|
|
5
|
+
ok,
|
|
6
|
+
type Result,
|
|
7
|
+
} from "@adobe/aio-commerce-lib-core/result";
|
|
8
|
+
|
|
9
|
+
import type { ValidationErrorType } from "@adobe/aio-commerce-lib-core/validation";
|
|
10
|
+
import { context, getToken } from "@adobe/aio-lib-ims";
|
|
11
|
+
import type { SnakeCasedProperties } from "type-fest";
|
|
12
|
+
import { type InferInput, type InferIssue, safeParse } from "valibot";
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
type ImsAuthEnv,
|
|
16
|
+
type ImsAuthParams,
|
|
17
|
+
ImsAuthParamsSchema,
|
|
18
|
+
} from "./schema";
|
|
19
|
+
|
|
20
|
+
type ImsAccessToken = string;
|
|
21
|
+
type ImsAuthHeader = "Authorization" | "x-api-key";
|
|
22
|
+
type ImsAuthHeaders = Record<ImsAuthHeader, string>;
|
|
23
|
+
|
|
24
|
+
/** Defines a validation error type for the IMS auth service. */
|
|
25
|
+
export type ImsAuthValidationError = ValidationErrorType<
|
|
26
|
+
"ImsAuthValidationError",
|
|
27
|
+
InferIssue<typeof ImsAuthParamsSchema>[]
|
|
28
|
+
>;
|
|
29
|
+
|
|
30
|
+
/** Defines an error type for the IMS auth service. */
|
|
31
|
+
export type ImsAuthError<TError = unknown> = ErrorType<
|
|
32
|
+
"ImsAuthError",
|
|
33
|
+
{
|
|
34
|
+
message: string;
|
|
35
|
+
error: TError;
|
|
36
|
+
}
|
|
37
|
+
>;
|
|
38
|
+
|
|
39
|
+
/** Defines the configuration options to create an {@link ImsAuthProvider}. */
|
|
40
|
+
export interface ImsAuthConfig {
|
|
41
|
+
clientId: string;
|
|
42
|
+
clientSecrets: string[];
|
|
43
|
+
technicalAccountId: string;
|
|
44
|
+
technicalAccountEmail: string;
|
|
45
|
+
imsOrgId: string;
|
|
46
|
+
scopes: string[];
|
|
47
|
+
environment: ImsAuthEnv;
|
|
48
|
+
context: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Defines an authentication provider for Adobe IMS. */
|
|
52
|
+
export interface ImsAuthProvider {
|
|
53
|
+
getAccessToken: () => Promise<Result<ImsAccessToken, ImsAuthError>>;
|
|
54
|
+
getHeaders: () => Promise<Result<ImsAuthHeaders, ImsAuthError>>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function snakeCaseImsAuthConfig(
|
|
58
|
+
config: ImsAuthConfig,
|
|
59
|
+
): SnakeCasedProperties<ImsAuthConfig> {
|
|
60
|
+
return {
|
|
61
|
+
...config,
|
|
62
|
+
client_id: config.clientId,
|
|
63
|
+
client_secrets: config.clientSecrets,
|
|
64
|
+
technical_account_id: config.technicalAccountId,
|
|
65
|
+
technical_account_email: config.technicalAccountEmail,
|
|
66
|
+
ims_org_id: config.imsOrgId,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function makeImsAuthValidationError(
|
|
71
|
+
message: string,
|
|
72
|
+
issues: InferIssue<typeof ImsAuthParamsSchema>[],
|
|
73
|
+
) {
|
|
74
|
+
return {
|
|
75
|
+
_tag: "ImsAuthValidationError",
|
|
76
|
+
message,
|
|
77
|
+
issues,
|
|
78
|
+
} satisfies ImsAuthValidationError;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function makeImsAuthError(message: string, error: unknown) {
|
|
82
|
+
return {
|
|
83
|
+
_tag: "ImsAuthError",
|
|
84
|
+
message,
|
|
85
|
+
error,
|
|
86
|
+
} satisfies ImsAuthError;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function fromParams(params: ImsAuthParams) {
|
|
90
|
+
return {
|
|
91
|
+
clientId: params.AIO_COMMERCE_IMS_CLIENT_ID,
|
|
92
|
+
clientSecrets: params.AIO_COMMERCE_IMS_CLIENT_SECRETS,
|
|
93
|
+
technicalAccountId: params.AIO_COMMERCE_IMS_TECHNICAL_ACCOUNT_ID,
|
|
94
|
+
technicalAccountEmail: params.AIO_COMMERCE_IMS_TECHNICAL_ACCOUNT_EMAIL,
|
|
95
|
+
imsOrgId: params.AIO_COMMERCE_IMS_IMS_ORG_ID,
|
|
96
|
+
scopes: params.AIO_COMMERCE_IMS_SCOPES,
|
|
97
|
+
environment: params.AIO_COMMERCE_IMS_ENV,
|
|
98
|
+
context: params.AIO_COMMERCE_IMS_CTX,
|
|
99
|
+
} satisfies ImsAuthConfig;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function tryGetAccessToken(
|
|
103
|
+
contextName: string,
|
|
104
|
+
): Promise<Result<ImsAccessToken, ImsAuthError>> {
|
|
105
|
+
try {
|
|
106
|
+
const accessToken = await getToken(contextName, {});
|
|
107
|
+
return ok(accessToken);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
return err(makeImsAuthError("Failed to retrieve IMS access token", error));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Creates an {@link ImsAuthProvider} based on the provided configuration.
|
|
115
|
+
* @param config The configuration for the IMS Auth Provider.
|
|
116
|
+
* @returns An {@link ImsAuthProvider} instance that can be used to get access token and auth headers.
|
|
117
|
+
*/
|
|
118
|
+
export function getImsAuthProvider(config: ImsAuthConfig): ImsAuthProvider {
|
|
119
|
+
const getAccessToken = async () => {
|
|
120
|
+
const snakeCasedConfig = snakeCaseImsAuthConfig(config);
|
|
121
|
+
|
|
122
|
+
await context.set(config.context, snakeCasedConfig);
|
|
123
|
+
return tryGetAccessToken(config.context);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const getHeaders = async () => {
|
|
127
|
+
const result = await getAccessToken();
|
|
128
|
+
return map(result, (accessToken) => ({
|
|
129
|
+
Authorization: `Bearer ${accessToken}`,
|
|
130
|
+
"x-api-key": config.clientId,
|
|
131
|
+
}));
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
getAccessToken,
|
|
136
|
+
getHeaders,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Tries to create an {@link ImsAuthProvider} based on the provided parameters.
|
|
142
|
+
* @param params The parameters required to create the IMS Auth Provider.
|
|
143
|
+
* @returns An {@link ImsAuthProvider} instance that can be used to get access token and auth headers.
|
|
144
|
+
*/
|
|
145
|
+
export function tryGetImsAuthProvider(
|
|
146
|
+
params: InferInput<typeof ImsAuthParamsSchema>,
|
|
147
|
+
): Result<ImsAuthProvider, ImsAuthValidationError> {
|
|
148
|
+
const validation = safeParse(ImsAuthParamsSchema, params);
|
|
149
|
+
|
|
150
|
+
if (!validation.success) {
|
|
151
|
+
return err(
|
|
152
|
+
makeImsAuthValidationError(
|
|
153
|
+
"Failed to validate the provided IMS parameters",
|
|
154
|
+
validation.issues,
|
|
155
|
+
),
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return ok(getImsAuthProvider(fromParams(validation.output)));
|
|
160
|
+
}
|