@adobe/aio-commerce-lib-auth 0.8.0 → 1.0.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/dist/es/index.mjs CHANGED
@@ -1 +1,737 @@
1
- import{createHeaderAccessor,getHeader,getHeadersFromParams,parseBearerToken}from"@adobe/aio-commerce-lib-core/headers";import*as v from"valibot";import{array,email,instance,minLength,nonEmpty,nonOptional,object,optional,parse,picklist,pipe,rawTransform,safeParse,string,transform,union,url}from"valibot";import{CommerceSdkValidationError}from"@adobe/aio-commerce-lib-core/error";import aioLibIms from"@adobe/aio-lib-ims";import crypto from"crypto";import OAuth1a from"oauth-1.0a";import{allNonEmpty}from"@adobe/aio-commerce-lib-core/params";function stringValueSchema(name){return v.string(`Expected a string value for '${name}'`)}function parseOrThrow(schema,input,message){let result=v.safeParse(schema,input);if(!result.success)throw new CommerceSdkValidationError(message??`Invalid input`,{issues:result.issues});return result.output}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}`)),stringArray=(name,minimumLength)=>pipe(array(string(),`Expected a string array value for the IMS auth parameter ${name}`),minLength(minimumLength,`Expected at least ${minimumLength} items for the IMS auth parameter ${name}`)),StringArrayTransformSchema=name=>pipe(union([stringArray(name,1),pipe(string(),rawTransform(({dataset:{value:v$1},addIssue,NEVER})=>{if(v$1.startsWith(`[`)&&v$1.endsWith(`]`))try{let parsed=JSON.parse(v$1);return Array.isArray(parsed)?parsed:(addIssue({received:v$1,message:`Expected a valid JSON array for the IMS auth parameter ${name}: ${v$1}`}),NEVER)}catch(error){let errorMessage=error.message;return addIssue({received:v$1,message:`Expected a valid JSON array for the IMS auth parameter ${name}: ${errorMessage}`}),NEVER}return[v$1]}))]),stringArray(`value`,1)),ImsAuthEnvSchema=picklist([`prod`,`stage`]),ImsAuthParamsSchema=object({clientId:imsAuthParameter(`clientId`),clientSecrets:stringArray(`clientSecrets`,1),technicalAccountId:imsAuthParameter(`technicalAccountId`),technicalAccountEmail:pipe(string(`Expected a string value for the IMS auth parameter technicalAccountEmail`),email(`Expected a valid email format for technicalAccountEmail`)),imsOrgId:imsAuthParameter(`imsOrgId`),environment:pipe(optional(ImsAuthEnvSchema)),context:pipe(optional(string())),scopes:stringArray(`scopes`,1)});function __transformStringArray(name,value){if(value===void 0)return;let result=safeParse(StringArrayTransformSchema(name),value);if(!result.success)throw new CommerceSdkValidationError(`Invalid ImsAuthProvider configuration`,{issues:result.issues});return result.output}function __parseImsAuthParams(config){let result=safeParse(ImsAuthParamsSchema,config);if(!result.success)throw new CommerceSdkValidationError(`Invalid ImsAuthProvider configuration`,{issues:result.issues});return result.output}function assertImsAuthParams(config){__parseImsAuthParams(config)}function resolveImsAuthParams(params){return __parseImsAuthParams({clientId:params.AIO_COMMERCE_AUTH_IMS_CLIENT_ID,clientSecrets:__transformStringArray(`AIO_COMMERCE_AUTH_IMS_CLIENT_SECRETS`,params.AIO_COMMERCE_AUTH_IMS_CLIENT_SECRETS),technicalAccountId:params.AIO_COMMERCE_AUTH_IMS_TECHNICAL_ACCOUNT_ID,technicalAccountEmail:params.AIO_COMMERCE_AUTH_IMS_TECHNICAL_ACCOUNT_EMAIL,imsOrgId:params.AIO_COMMERCE_AUTH_IMS_ORG_ID,scopes:__transformStringArray(`AIO_COMMERCE_AUTH_IMS_SCOPES`,params.AIO_COMMERCE_AUTH_IMS_SCOPES),environment:params.AIO_COMMERCE_AUTH_IMS_ENVIRONMENT,context:params.AIO_COMMERCE_AUTH_IMS_CONTEXT})}function buildImsHeaders(accessToken,apiKey){let imsHeaders={Authorization:`Bearer ${accessToken}`};return apiKey&&(imsHeaders[`x-api-key`]=apiKey),imsHeaders}const IMS_AUTH_TOKEN_PARAM=`AIO_COMMERCE_AUTH_IMS_TOKEN`,IMS_AUTH_API_KEY_PARAM=`AIO_COMMERCE_AUTH_IMS_API_KEY`,ImsAuthParamsInputSchema=v.looseObject({[IMS_AUTH_TOKEN_PARAM]:stringValueSchema(IMS_AUTH_TOKEN_PARAM),[IMS_AUTH_API_KEY_PARAM]:v.optional(stringValueSchema(IMS_AUTH_API_KEY_PARAM))}),ForwardedImsAuthSourceSchema=v.variant(`from`,[v.object({from:v.literal(`headers`),headers:v.record(v.string(),v.optional(v.string()))}),v.object({from:v.literal(`getter`),getHeaders:v.custom(input=>typeof input==`function`,`Expected a function for getHeaders`)}),v.object({from:v.literal(`params`),params:ImsAuthParamsInputSchema})]);function getForwardedImsAuthProvider(source){let validatedSource=parseOrThrow(ForwardedImsAuthSourceSchema,source,`Invalid forwarded IMS auth source`);switch(validatedSource.from){case`headers`:{let{authorization}=createHeaderAccessor(validatedSource.headers,[`Authorization`]),apiKey=getHeader(validatedSource.headers,`x-api-key`),{token}=parseBearerToken(authorization);return{getAccessToken:()=>token,getHeaders:()=>buildImsHeaders(token,apiKey)}}case`getter`:return{getHeaders:validatedSource.getHeaders,getAccessToken:async()=>{let{token}=parseBearerToken((await validatedSource.getHeaders()).Authorization);return token}};case`params`:{let{params}=validatedSource,accessToken=params[IMS_AUTH_TOKEN_PARAM],apiKey=params[IMS_AUTH_API_KEY_PARAM];return{getAccessToken:()=>accessToken,getHeaders:()=>buildImsHeaders(accessToken,apiKey)}}}}function forwardImsAuthProviderFromRequest(params){return getForwardedImsAuthProvider({from:`headers`,headers:getHeadersFromParams(params)})}function forwardImsAuthProviderFromParams(params){return getForwardedImsAuthProvider({from:`params`,params:parseOrThrow(ImsAuthParamsInputSchema,params,`Missing AIO_COMMERCE_AUTH_IMS_TOKEN in params`)})}function forwardImsAuthProvider(params){try{return forwardImsAuthProviderFromParams(params)}catch{}try{return forwardImsAuthProviderFromRequest(params)}catch{}throw Error(`Can't forward IMS authentication from the given params. Make sure your params contain an AIO_COMMERCE_AUTH_IMS_TOKEN input or an Authorization header with an IMS token.`)}const{context,getToken}=aioLibIms;function toImsAuthConfig(config){return{scopes:config.scopes,env:config?.environment??`prod`,context:config.context??`aio-commerce-lib-auth-creds`,client_id:config.clientId,client_secrets:config.clientSecrets,technical_account_id:config.technicalAccountId,technical_account_email:config.technicalAccountEmail,ims_org_id:config.imsOrgId}}function isImsAuthProvider(provider){return typeof provider==`object`&&!!provider&&`getAccessToken`in provider&&`getHeaders`in provider&&typeof provider.getAccessToken==`function`&&typeof provider.getHeaders==`function`}function getImsAuthProvider(authParams){let getAccessToken=async()=>{let imsAuthConfig=toImsAuthConfig(authParams);return await context.set(imsAuthConfig.context,imsAuthConfig),getToken(imsAuthConfig.context,{})};return{getAccessToken,getHeaders:async()=>buildImsHeaders(await getAccessToken(),authParams.clientId)}}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}`)),UrlSchema=pipe(union([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`)),instance(URL)]),transform(url$1=>url$1 instanceof URL?url$1.toString():url$1)),IntegrationAuthParamsSchema=nonOptional(object({consumerKey:integrationAuthParameter(`consumerKey`),consumerSecret:integrationAuthParameter(`consumerSecret`),accessToken:integrationAuthParameter(`accessToken`),accessTokenSecret:integrationAuthParameter(`accessTokenSecret`)}));function isIntegrationAuthProvider(provider){return typeof provider==`object`&&!!provider&&`getHeaders`in provider&&typeof provider.getHeaders==`function`}function getIntegrationAuthProvider(authParams){let oauth=new OAuth1a({consumer:{key:authParams.consumerKey,secret:authParams.consumerSecret},signature_method:`HMAC-SHA256`,hash_function:(baseString,key)=>crypto.createHmac(`sha256`,key).update(baseString).digest(`base64`)}),oauthToken={key:authParams.accessToken,secret:authParams.accessTokenSecret};return{getHeaders:(method,url$1)=>{let urlString=parse(UrlSchema,url$1);return oauth.toHeader(oauth.authorize({url:urlString,method},oauthToken))}}}function __parseIntegrationAuthParams(config){let result=safeParse(IntegrationAuthParamsSchema,config);if(!result.success)throw new CommerceSdkValidationError(`Invalid IntegrationAuthProvider configuration`,{issues:result.issues});return result.output}function assertIntegrationAuthParams(config){__parseIntegrationAuthParams(config)}function resolveIntegrationAuthParams(params){return __parseIntegrationAuthParams({consumerKey:params.AIO_COMMERCE_AUTH_INTEGRATION_CONSUMER_KEY,consumerSecret:params.AIO_COMMERCE_AUTH_INTEGRATION_CONSUMER_SECRET,accessToken:params.AIO_COMMERCE_AUTH_INTEGRATION_ACCESS_TOKEN,accessTokenSecret:params.AIO_COMMERCE_AUTH_INTEGRATION_ACCESS_TOKEN_SECRET})}const IMS_AUTH_PARAMS=[`AIO_COMMERCE_AUTH_IMS_CLIENT_ID`,`AIO_COMMERCE_AUTH_IMS_CLIENT_SECRETS`,`AIO_COMMERCE_AUTH_IMS_TECHNICAL_ACCOUNT_ID`,`AIO_COMMERCE_AUTH_IMS_TECHNICAL_ACCOUNT_EMAIL`,`AIO_COMMERCE_AUTH_IMS_ORG_ID`,`AIO_COMMERCE_AUTH_IMS_SCOPES`],INTEGRATION_AUTH_PARAMS=[`AIO_COMMERCE_AUTH_INTEGRATION_CONSUMER_KEY`,`AIO_COMMERCE_AUTH_INTEGRATION_CONSUMER_SECRET`,`AIO_COMMERCE_AUTH_INTEGRATION_ACCESS_TOKEN`,`AIO_COMMERCE_AUTH_INTEGRATION_ACCESS_TOKEN_SECRET`];function resolveAuthParams(params){if(allNonEmpty(params,IMS_AUTH_PARAMS))return{...resolveImsAuthParams(params),strategy:`ims`};if(allNonEmpty(params,INTEGRATION_AUTH_PARAMS))return{...resolveIntegrationAuthParams(params),strategy:`integration`};throw Error(`Can't resolve authentication options for the given params. Please provide either IMS options (${IMS_AUTH_PARAMS.join(`, `)}) or Commerce integration options (${INTEGRATION_AUTH_PARAMS.join(`, `)}).`)}export{assertImsAuthParams,assertIntegrationAuthParams,forwardImsAuthProvider,getForwardedImsAuthProvider,getImsAuthProvider,getIntegrationAuthProvider,isImsAuthProvider,isIntegrationAuthProvider,resolveAuthParams};
1
+ import { createHeaderAccessor, getHeader, getHeadersFromParams, parseBearerToken } from "@adobe/aio-commerce-lib-core/headers";
2
+ import * as v from "valibot";
3
+ import { array, email, instance, minLength, nonEmpty, nonOptional, object, optional, parse, picklist, pipe, rawTransform, safeParse, string, transform, union, url } from "valibot";
4
+ import { CommerceSdkValidationError } from "@adobe/aio-commerce-lib-core/error";
5
+ import aioLibIms from "@adobe/aio-lib-ims";
6
+ import crypto from "crypto";
7
+ import OAuth1a from "oauth-1.0a";
8
+ import { allNonEmpty } from "@adobe/aio-commerce-lib-core/params";
9
+
10
+ //#region ../../packages-private/common-utils/source/valibot/schemas.ts
11
+ /**
12
+ * A schema for a string value.
13
+ * @param name The name of the field this schema refers to.
14
+ */
15
+ function stringValueSchema(name) {
16
+ return v.string(`Expected a string value for '${name}'`);
17
+ }
18
+
19
+ //#endregion
20
+ //#region ../../packages-private/common-utils/source/valibot/utils.ts
21
+ /**
22
+ * Parses the input using the provided schema and throws a {@link CommerceSdkValidationError} error if the input is invalid.
23
+ * @param schema - The schema to use for parsing.
24
+ * @param input - The input to parse.
25
+ * @param message - Optional custom error message for the validation error.
26
+ */
27
+ function parseOrThrow(schema, input, message) {
28
+ const result = v.safeParse(schema, input);
29
+ if (!result.success) throw new CommerceSdkValidationError(message ?? "Invalid input", { issues: result.issues });
30
+ return result.output;
31
+ }
32
+
33
+ //#endregion
34
+ //#region source/lib/ims-auth/schema.ts
35
+ /**
36
+ * Creates a validation schema for a required IMS auth string parameter.
37
+ * @param name The name of the parameter for error messages.
38
+ * @returns A validation pipeline that ensures the parameter is a non-empty string.
39
+ */
40
+ 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}`));
41
+ /**
42
+ * Creates a validation schema for an IMS auth string array parameter.
43
+ * @param name The name of the parameter for error messages.
44
+ * @param minimumLength The minimum length of the array.
45
+ * @returns A validation pipeline that ensures the parameter is an array of strings.
46
+ */
47
+ const stringArray = (name, minimumLength) => {
48
+ return pipe(array(string(), `Expected a string array value for the IMS auth parameter ${name}`), minLength(minimumLength, `Expected at least ${minimumLength} items for the IMS auth parameter ${name}`));
49
+ };
50
+ /**
51
+ * Schema for transforming a value that may be a JSON string array or a single string into an array.
52
+ * This schema handles the transformation of App Builder action inputs that may come as JSON strings.
53
+ */
54
+ const StringArrayTransformSchema = (name) => pipe(union([stringArray(name, 1), pipe(string(), rawTransform(({ dataset: { value: v }, addIssue, NEVER }) => {
55
+ if (v.startsWith("[") && v.endsWith("]")) try {
56
+ const parsed = JSON.parse(v);
57
+ if (!Array.isArray(parsed)) {
58
+ addIssue({
59
+ received: v,
60
+ message: `Expected a valid JSON array for the IMS auth parameter ${name}: ${v}`
61
+ });
62
+ return NEVER;
63
+ }
64
+ return parsed;
65
+ } catch (error) {
66
+ const errorMessage = error.message;
67
+ addIssue({
68
+ received: v,
69
+ message: `Expected a valid JSON array for the IMS auth parameter ${name}: ${errorMessage}`
70
+ });
71
+ return NEVER;
72
+ }
73
+ return [v];
74
+ }))]), stringArray("value", 1));
75
+ /** Validation schema for IMS auth environment values. */
76
+ const ImsAuthEnvSchema = picklist(["prod", "stage"]);
77
+ /** Defines the schema to validate the necessary parameters for the IMS auth service. */
78
+ const ImsAuthParamsSchema = object({
79
+ clientId: imsAuthParameter("clientId"),
80
+ clientSecrets: stringArray("clientSecrets", 1),
81
+ technicalAccountId: imsAuthParameter("technicalAccountId"),
82
+ technicalAccountEmail: pipe(string("Expected a string value for the IMS auth parameter technicalAccountEmail"), email("Expected a valid email format for technicalAccountEmail")),
83
+ imsOrgId: imsAuthParameter("imsOrgId"),
84
+ environment: pipe(optional(ImsAuthEnvSchema)),
85
+ context: pipe(optional(string())),
86
+ scopes: stringArray("scopes", 1)
87
+ });
88
+
89
+ //#endregion
90
+ //#region source/lib/ims-auth/utils.ts
91
+ /**
92
+ * Transforms a value using the string array transformation schema.
93
+ * @param value - The value to transform (may be a JSON string, single string, or array).
94
+ * @throws {CommerceSdkValidationError} If the transformation fails.
95
+ *
96
+ * @internal
97
+ */
98
+ function __transformStringArray(name, value) {
99
+ if (value === void 0) return;
100
+ const result = safeParse(StringArrayTransformSchema(name), value);
101
+ if (!result.success) throw new CommerceSdkValidationError("Invalid ImsAuthProvider configuration", { issues: result.issues });
102
+ return result.output;
103
+ }
104
+ /**
105
+ * Parses the provided configuration for an {@link ImsAuthProvider}.
106
+ * @param config - The configuration to parse.
107
+ * @throws {CommerceSdkValidationError} If the configuration is invalid.
108
+ *
109
+ * @internal
110
+ */
111
+ function __parseImsAuthParams(config) {
112
+ const result = safeParse(ImsAuthParamsSchema, config);
113
+ if (!result.success) throw new CommerceSdkValidationError("Invalid ImsAuthProvider configuration", { issues: result.issues });
114
+ return result.output;
115
+ }
116
+ /**
117
+ * Asserts the provided configuration for an {@link ImsAuthProvider}.
118
+ * @param config The configuration to validate.
119
+ * @throws {CommerceSdkValidationError} If the configuration is invalid.
120
+ * @example
121
+ * ```typescript
122
+ * const config = {
123
+ * clientId: "your-client-id",
124
+ * clientSecrets: ["your-client-secret"],
125
+ * technicalAccountId: "your-technical-account-id",
126
+ * technicalAccountEmail: "your-account@example.com",
127
+ * imsOrgId: "your-ims-org-id@AdobeOrg",
128
+ * scopes: ["AdobeID", "openid"],
129
+ * environment: "prod", // or "stage"
130
+ * context: "my-app-context"
131
+ * };
132
+ *
133
+ * // This will validate the config and throw if invalid
134
+ * assertImsAuthParams(config);
135
+ *```
136
+ * @example
137
+ * ```typescript
138
+ * // Example of a failing assert:
139
+ * try {
140
+ * assertImsAuthParams({
141
+ * clientId: "valid-client-id",
142
+ * // Missing required fields like clientSecrets, technicalAccountId, etc.
143
+ * });
144
+ * } catch (error) {
145
+ * console.error(error.message); // "Invalid ImsAuthProvider configuration"
146
+ * console.error(error.issues); // Array of validation issues
147
+ * }
148
+ * ```
149
+ */
150
+ function assertImsAuthParams(config) {
151
+ __parseImsAuthParams(config);
152
+ }
153
+ /**
154
+ * Resolves an {@link ImsAuthParams} from the given App Builder action inputs.
155
+ * @param params The App Builder action inputs to resolve the IMS authentication parameters from.
156
+ * @throws {CommerceSdkValidationError} If the parameters are invalid and cannot be resolved.
157
+ *
158
+ * @example
159
+ * ```typescript
160
+ * // Some App Builder runtime action that needs IMS authentication
161
+ * export function main(params) {
162
+ * const imsAuthProvider = getImsAuthProvider(resolveImsAuthParams(params));
163
+ *
164
+ * // Get headers for API requests
165
+ * const headers = await authProvider.getHeaders();
166
+ * const response = await fetch('https://api.adobe.io/some-endpoint', {
167
+ * headers: await authProvider.getHeaders()
168
+ * });
169
+ * }
170
+ * ```
171
+ */
172
+ function resolveImsAuthParams(params) {
173
+ return __parseImsAuthParams({
174
+ clientId: params.AIO_COMMERCE_AUTH_IMS_CLIENT_ID,
175
+ clientSecrets: __transformStringArray("AIO_COMMERCE_AUTH_IMS_CLIENT_SECRETS", params.AIO_COMMERCE_AUTH_IMS_CLIENT_SECRETS),
176
+ technicalAccountId: params.AIO_COMMERCE_AUTH_IMS_TECHNICAL_ACCOUNT_ID,
177
+ technicalAccountEmail: params.AIO_COMMERCE_AUTH_IMS_TECHNICAL_ACCOUNT_EMAIL,
178
+ imsOrgId: params.AIO_COMMERCE_AUTH_IMS_ORG_ID,
179
+ scopes: __transformStringArray("AIO_COMMERCE_AUTH_IMS_SCOPES", params.AIO_COMMERCE_AUTH_IMS_SCOPES),
180
+ environment: params.AIO_COMMERCE_AUTH_IMS_ENVIRONMENT,
181
+ context: params.AIO_COMMERCE_AUTH_IMS_CONTEXT
182
+ });
183
+ }
184
+ /**
185
+ * Shorthand to build IMS authentication headers.
186
+ * @param accessToken - The token to use in the Authorization header.
187
+ * @param apiKey - The API key to include in the x-api-key header (optional).
188
+ */
189
+ function buildImsHeaders(accessToken, apiKey) {
190
+ const imsHeaders = { Authorization: `Bearer ${accessToken}` };
191
+ if (apiKey) imsHeaders["x-api-key"] = apiKey;
192
+ return imsHeaders;
193
+ }
194
+
195
+ //#endregion
196
+ //#region source/lib/ims-auth/forwarding.ts
197
+ const IMS_AUTH_TOKEN_PARAM = "AIO_COMMERCE_AUTH_IMS_TOKEN";
198
+ const IMS_AUTH_API_KEY_PARAM = "AIO_COMMERCE_AUTH_IMS_API_KEY";
199
+ const ImsAuthParamsInputSchema = v.looseObject({
200
+ [IMS_AUTH_TOKEN_PARAM]: stringValueSchema(IMS_AUTH_TOKEN_PARAM),
201
+ [IMS_AUTH_API_KEY_PARAM]: v.optional(stringValueSchema(IMS_AUTH_API_KEY_PARAM))
202
+ });
203
+ const ForwardedImsAuthSourceSchema = v.variant("from", [
204
+ v.object({
205
+ from: v.literal("headers"),
206
+ headers: v.record(v.string(), v.optional(v.string()))
207
+ }),
208
+ v.object({
209
+ from: v.literal("getter"),
210
+ getHeaders: v.custom((input) => typeof input === "function", "Expected a function for getHeaders")
211
+ }),
212
+ v.object({
213
+ from: v.literal("params"),
214
+ params: ImsAuthParamsInputSchema
215
+ })
216
+ ]);
217
+ /**
218
+ * Creates an {@link ImsAuthProvider} by forwarding authentication credentials from various sources.
219
+ *
220
+ * @param source The source of the credentials to forward, as a {@link ForwardedImsAuthSource}.
221
+ * @returns An {@link ImsAuthProvider} instance that returns the forwarded access token and headers.
222
+ *
223
+ * @throws {CommerceSdkValidationError} If the source object is invalid.
224
+ * @throws {CommerceSdkValidationError} If `from: "headers"` is used and the `Authorization` header is missing.
225
+ * @throws {CommerceSdkValidationError} If `from: "headers"` is used and the `Authorization` header is not in Bearer token format.
226
+ * @throws {CommerceSdkValidationError} If `from: "params"` is used and `AIO_COMMERCE_AUTH_IMS_TOKEN` is missing or empty.
227
+ *
228
+ * @example
229
+ * ```typescript
230
+ * import { getForwardedImsAuthProvider } from "@adobe/aio-commerce-lib-auth";
231
+ *
232
+ * // From raw headers (e.g. from an HTTP request).
233
+ * const provider1 = getForwardedImsAuthProvider({
234
+ * from: "headers",
235
+ * headers: params.__ow_headers,
236
+ * });
237
+ *
238
+ * // From async getter (e.g. fetch from secret manager)
239
+ * const provider2 = getForwardedImsAuthProvider({
240
+ * from: "getter",
241
+ * getHeaders: async () => {
242
+ * const token = await secretManager.getSecret("ims-token");
243
+ * return { Authorization: `Bearer ${token}` };
244
+ * },
245
+ * });
246
+ *
247
+ * // From a params object (using AIO_COMMERCE_AUTH_IMS_TOKEN and AIO_COMMERCE_AUTH_IMS_API_KEY keys)
248
+ * const provider3 = getForwardedImsAuthProvider({
249
+ * from: "params",
250
+ * params: actionParams,
251
+ * });
252
+ *
253
+ * // Use the provider
254
+ * const token = await provider1.getAccessToken();
255
+ * const headers = await provider1.getHeaders();
256
+ * ```
257
+ */
258
+ function getForwardedImsAuthProvider(source) {
259
+ const validatedSource = parseOrThrow(ForwardedImsAuthSourceSchema, source, "Invalid forwarded IMS auth source");
260
+ switch (validatedSource.from) {
261
+ case "headers": {
262
+ const { authorization } = createHeaderAccessor(validatedSource.headers, ["Authorization"]);
263
+ const apiKey = getHeader(validatedSource.headers, "x-api-key");
264
+ const { token } = parseBearerToken(authorization);
265
+ return {
266
+ getAccessToken: () => token,
267
+ getHeaders: () => buildImsHeaders(token, apiKey)
268
+ };
269
+ }
270
+ case "getter": return {
271
+ getHeaders: validatedSource.getHeaders,
272
+ getAccessToken: async () => {
273
+ const { token } = parseBearerToken((await validatedSource.getHeaders()).Authorization);
274
+ return token;
275
+ }
276
+ };
277
+ case "params": {
278
+ const { params } = validatedSource;
279
+ const accessToken = params[IMS_AUTH_TOKEN_PARAM];
280
+ const apiKey = params[IMS_AUTH_API_KEY_PARAM];
281
+ return {
282
+ getAccessToken: () => accessToken,
283
+ getHeaders: () => buildImsHeaders(accessToken, apiKey)
284
+ };
285
+ }
286
+ }
287
+ }
288
+ /**
289
+ * Creates an {@link ImsAuthProvider} by forwarding authentication credentials from incoming
290
+ * runtime action request headers.
291
+ *
292
+ * This is a convenience wrapper around {@link getForwardedImsAuthProvider} for the common case
293
+ * of forwarding credentials from Adobe I/O Runtime action parameters.
294
+ *
295
+ * @param params The runtime action parameters containing the `__ow_headers` object with authentication headers.
296
+ * @returns An {@link ImsAuthProvider} instance that returns the forwarded access token and headers.
297
+ *
298
+ * @throws {Error} If the `Authorization` header is missing from the request headers.
299
+ * @throws {Error} If the `Authorization` header is not in the correct Bearer token format.
300
+ *
301
+ * @example
302
+ * ```typescript
303
+ * import { forwardImsAuthProviderFromRequest } from "@adobe/aio-commerce-lib-auth";
304
+ *
305
+ * // In an Adobe I/O Runtime action
306
+ * async function main(params) {
307
+ * // params.__ow_headers contains: { Authorization: "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9..."}
308
+ *
309
+ * // Forward the authentication from the incoming request
310
+ * const authProvider = forwardImsAuthProviderFromRequest(params);
311
+ *
312
+ * // Get the forwarded access token
313
+ * const token = await authProvider.getAccessToken();
314
+ * console.log(token); // "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9..."
315
+ *
316
+ * // Get headers for downstream API requests
317
+ * const headers = await authProvider.getHeaders();
318
+ * console.log(headers);
319
+ * // {
320
+ * // Authorization: "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
321
+ * // "x-api-key": "client-id-from-request" // Only if present in original request
322
+ * // }
323
+ *
324
+ * // Use the forwarded credentials in downstream API calls
325
+ * const response = await fetch('...', {
326
+ * headers: await authProvider.getHeaders()
327
+ * });
328
+ *
329
+ * return { statusCode: 200, body: await response.json() };
330
+ * }
331
+ * ```
332
+ */
333
+ function forwardImsAuthProviderFromRequest(params) {
334
+ return getForwardedImsAuthProvider({
335
+ from: "headers",
336
+ headers: getHeadersFromParams(params)
337
+ });
338
+ }
339
+ /**
340
+ * Creates an {@link ImsAuthProvider} by forwarding authentication credentials from a params object.
341
+ *
342
+ * This is a convenience wrapper around {@link getForwardedImsAuthProvider} for the common case
343
+ * of forwarding credentials from runtime action parameters. It reads:
344
+ * - `AIO_COMMERCE_AUTH_IMS_TOKEN` for the access token (required)
345
+ * - `AIO_COMMERCE_AUTH_IMS_API_KEY` for the API key (optional)
346
+ *
347
+ * @param params The params object containing the authentication credentials.
348
+ * @returns An {@link ImsAuthProvider} instance that returns the access token and headers from the params.
349
+ *
350
+ * @throws {CommerceSdkValidationError} If `AIO_COMMERCE_AUTH_IMS_TOKEN` is not set or is empty.
351
+ *
352
+ * @example
353
+ * ```typescript
354
+ * import { forwardImsAuthProviderFromParams } from "@adobe/aio-commerce-lib-auth";
355
+ *
356
+ * // In an Adobe I/O Runtime action
357
+ * async function main(params) {
358
+ * // params contains: { AIO_COMMERCE_AUTH_IMS_TOKEN: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9..." }
359
+ * const authProvider = forwardImsAuthProviderFromParams(params);
360
+ *
361
+ * // Get the access token
362
+ * const token = await authProvider.getAccessToken();
363
+ *
364
+ * // Get headers for downstream API requests
365
+ * const headers = await authProvider.getHeaders();
366
+ * // {
367
+ * // Authorization: "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
368
+ * // "x-api-key": "my-api-key" // Only if AIO_COMMERCE_AUTH_IMS_API_KEY is set
369
+ * // }
370
+ * }
371
+ * ```
372
+ */
373
+ function forwardImsAuthProviderFromParams(params) {
374
+ return getForwardedImsAuthProvider({
375
+ from: "params",
376
+ params: parseOrThrow(ImsAuthParamsInputSchema, params, "Missing AIO_COMMERCE_AUTH_IMS_TOKEN in params")
377
+ });
378
+ }
379
+ /**
380
+ * Creates an {@link ImsAuthProvider} by forwarding authentication credentials from runtime action parameters.
381
+ *
382
+ * This function automatically detects the source of credentials by trying multiple strategies in order:
383
+ * 1. **Params token** - Looks for `AIO_COMMERCE_AUTH_IMS_TOKEN` (and optionally `AIO_COMMERCE_AUTH_IMS_API_KEY`) in the params object
384
+ * 2. **HTTP headers** - Falls back to extracting the `Authorization` header from `__ow_headers`
385
+ *
386
+ * Use this function when building actions that receive authenticated requests and need to forward
387
+ * those credentials to downstream services (proxy pattern).
388
+ *
389
+ * @param params The runtime action parameters object. Can contain either:
390
+ * - `AIO_COMMERCE_AUTH_IMS_TOKEN` and optionally `AIO_COMMERCE_AUTH_IMS_API_KEY` for direct token forwarding
391
+ * - `__ow_headers` with an `Authorization` header for HTTP request forwarding
392
+ * @returns An {@link ImsAuthProvider} instance that returns the forwarded access token and headers.
393
+ *
394
+ * @throws {Error} If neither a valid token param nor Authorization header is found.
395
+ *
396
+ * @example
397
+ * ```typescript
398
+ * import { forwardImsAuthProvider } from "@adobe/aio-commerce-lib-auth";
399
+ *
400
+ * export async function main(params: Record<string, unknown>) {
401
+ * // Automatically detects credentials from params or headers
402
+ * const authProvider = forwardImsAuthProvider(params);
403
+ *
404
+ * // Get the access token
405
+ * const token = await authProvider.getAccessToken();
406
+ *
407
+ * // Get headers for downstream API requests
408
+ * const headers = await authProvider.getHeaders();
409
+ * // {
410
+ * // Authorization: "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
411
+ * // "x-api-key": "my-api-key" // Only if available
412
+ * // }
413
+ *
414
+ * // Use the forwarded credentials in downstream API calls
415
+ * const response = await fetch("https://api.adobe.io/some-endpoint", {
416
+ * headers,
417
+ * });
418
+ *
419
+ * return { statusCode: 200, body: await response.json() };
420
+ * }
421
+ * ```
422
+ */
423
+ function forwardImsAuthProvider(params) {
424
+ try {
425
+ return forwardImsAuthProviderFromParams(params);
426
+ } catch {}
427
+ try {
428
+ return forwardImsAuthProviderFromRequest(params);
429
+ } catch {}
430
+ throw new Error("Can't forward IMS authentication from the given params. Make sure your params contain an AIO_COMMERCE_AUTH_IMS_TOKEN input or an Authorization header with an IMS token.");
431
+ }
432
+
433
+ //#endregion
434
+ //#region source/lib/ims-auth/provider.ts
435
+ const { context, getToken } = aioLibIms;
436
+ /**
437
+ * Converts IMS auth configuration properties to snake_case format.
438
+ * @param config The IMS auth configuration with camelCase properties.
439
+ * @returns The configuration with snake_case properties.
440
+ */
441
+ function toImsAuthConfig(config) {
442
+ return {
443
+ scopes: config.scopes,
444
+ env: config?.environment ?? "prod",
445
+ context: config.context ?? "aio-commerce-lib-auth-creds",
446
+ client_id: config.clientId,
447
+ client_secrets: config.clientSecrets,
448
+ technical_account_id: config.technicalAccountId,
449
+ technical_account_email: config.technicalAccountEmail,
450
+ ims_org_id: config.imsOrgId
451
+ };
452
+ }
453
+ /**
454
+ * Type guard to check if a value is an ImsAuthProvider instance.
455
+ *
456
+ * @param provider The value to check.
457
+ * @returns `true` if the value is an ImsAuthProvider, `false` otherwise.
458
+ *
459
+ * @example
460
+ * ```typescript
461
+ * import { getImsAuthProvider, isImsAuthProvider } from "@adobe/aio-commerce-lib-auth";
462
+ *
463
+ * // Imagine you have an object that it's not strictly typed as ImsAuthProvider.
464
+ * const provider = getImsAuthProvider({ ... }) as unknown;
465
+ *
466
+ * if (isImsAuthProvider(provider)) {
467
+ * // TypeScript knows provider is ImsAuthProvider
468
+ * const token = await provider.getAccessToken();
469
+ * }
470
+ * ```
471
+ */
472
+ function isImsAuthProvider(provider) {
473
+ return typeof provider === "object" && provider !== null && "getAccessToken" in provider && "getHeaders" in provider && typeof provider.getAccessToken === "function" && typeof provider.getHeaders === "function";
474
+ }
475
+ /**
476
+ * Creates an {@link ImsAuthProvider} based on the provided configuration.
477
+ * @param authParams An {@link ImsAuthParams} parameter that contains the configuration for the {@link ImsAuthProvider}.
478
+ * @returns An {@link ImsAuthProvider} instance that can be used to get access token and auth headers.
479
+ * @example
480
+ * ```typescript
481
+ * const config = {
482
+ * clientId: "your-client-id",
483
+ * clientSecrets: ["your-client-secret"],
484
+ * technicalAccountId: "your-technical-account-id",
485
+ * technicalAccountEmail: "your-account@example.com",
486
+ * imsOrgId: "your-ims-org-id@AdobeOrg",
487
+ * scopes: ["AdobeID", "openid"],
488
+ * environment: "prod",
489
+ * context: "my-app-context"
490
+ * };
491
+ *
492
+ * const authProvider = getImsAuthProvider(config);
493
+ *
494
+ * // Get access token
495
+ * const token = await authProvider.getAccessToken();
496
+ * console.log(token); // "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9..."
497
+ *
498
+ * // Get headers for API requests
499
+ * const headers = await authProvider.getHeaders();
500
+ * console.log(headers);
501
+ * // {
502
+ * // Authorization: "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
503
+ * // "x-api-key": "your-client-id"
504
+ * // }
505
+ *
506
+ * // Use headers in API calls
507
+ * const response = await fetch('https://api.adobe.io/some-endpoint', {
508
+ * headers: await authProvider.getHeaders()
509
+ * });
510
+ * ```
511
+ */
512
+ function getImsAuthProvider(authParams) {
513
+ const getAccessToken = async () => {
514
+ const imsAuthConfig = toImsAuthConfig(authParams);
515
+ await context.set(imsAuthConfig.context, imsAuthConfig);
516
+ return getToken(imsAuthConfig.context, {});
517
+ };
518
+ const getHeaders = async () => {
519
+ return buildImsHeaders(await getAccessToken(), authParams.clientId);
520
+ };
521
+ return {
522
+ getAccessToken,
523
+ getHeaders
524
+ };
525
+ }
526
+
527
+ //#endregion
528
+ //#region source/lib/integration-auth/schema.ts
529
+ /**
530
+ * Creates a validation schema for a required Commerce Integration string parameter.
531
+ * @param name The name of the parameter for error messages.
532
+ * @returns A validation pipeline that ensures the parameter is a non-empty string.
533
+ */
534
+ 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}`));
535
+ /** Validation schema for the Adobe Commerce endpoint base URL. */
536
+ 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"));
537
+ /** Validation schema that accepts either a URL string or URL instance and normalizes to string. */
538
+ const UrlSchema = pipe(union([BaseUrlSchema, instance(URL)]), transform((url) => {
539
+ if (url instanceof URL) return url.toString();
540
+ return url;
541
+ }));
542
+ /**
543
+ * The schema for the Commerce Integration parameters.
544
+ * This is used to validate the parameters passed to the Commerce Integration provider.
545
+ */
546
+ const IntegrationAuthParamsSchema = nonOptional(object({
547
+ consumerKey: integrationAuthParameter("consumerKey"),
548
+ consumerSecret: integrationAuthParameter("consumerSecret"),
549
+ accessToken: integrationAuthParameter("accessToken"),
550
+ accessTokenSecret: integrationAuthParameter("accessTokenSecret")
551
+ }));
552
+
553
+ //#endregion
554
+ //#region source/lib/integration-auth/provider.ts
555
+ /**
556
+ * Type guard to check if a value is an IntegrationAuthProvider instance.
557
+ *
558
+ * @param provider The value to check.
559
+ * @returns `true` if the value is an IntegrationAuthProvider, `false` otherwise.
560
+ *
561
+ * @example
562
+ * ```typescript
563
+ * import { getIntegrationAuthProvider, isIntegrationAuthProvider } from "@adobe/aio-commerce-lib-auth";
564
+ *
565
+ * // Imagine you have an object that it's not strictly typed as IntegrationAuthProvider.
566
+ * const provider = getIntegrationAuthProvider({ ... }) as unknown;
567
+ *
568
+ * if (isIntegrationAuthProvider(provider)) {
569
+ * // TypeScript knows provider is IntegrationAuthProvider
570
+ * const headers = provider.getHeaders("GET", "https://api.example.com");
571
+ * }
572
+ * ```
573
+ */
574
+ function isIntegrationAuthProvider(provider) {
575
+ return typeof provider === "object" && provider !== null && "getHeaders" in provider && typeof provider.getHeaders === "function";
576
+ }
577
+ /**
578
+ * Creates an {@link IntegrationAuthProvider} based on the provided configuration.
579
+ * @param authParams The configuration for the integration.
580
+ * @returns An {@link IntegrationAuthProvider} instance that can be used to get auth headers.
581
+ * @example
582
+ * ```typescript
583
+ * const config = {
584
+ * consumerKey: "your-consumer-key",
585
+ * consumerSecret: "your-consumer-secret",
586
+ * accessToken: "your-access-token",
587
+ * accessTokenSecret: "your-access-token-secret"
588
+ * };
589
+ *
590
+ * const authProvider = getIntegrationAuthProvider(config);
591
+ *
592
+ * // Get OAuth headers for a REST API call
593
+ * const headers = authProvider.getHeaders("GET", "https://your-store.com/rest/V1/products");
594
+ * console.log(headers); // { Authorization: "OAuth oauth_consumer_key=..., oauth_signature=..." }
595
+ *
596
+ * // Can also be used with URL objects
597
+ * const url = new URL("https://your-store.com/rest/V1/customers");
598
+ * const postHeaders = authProvider.getHeaders("POST", url);
599
+ * ```
600
+ */
601
+ function getIntegrationAuthProvider(authParams) {
602
+ const oauth = new OAuth1a({
603
+ consumer: {
604
+ key: authParams.consumerKey,
605
+ secret: authParams.consumerSecret
606
+ },
607
+ signature_method: "HMAC-SHA256",
608
+ hash_function: (baseString, key) => crypto.createHmac("sha256", key).update(baseString).digest("base64")
609
+ });
610
+ const oauthToken = {
611
+ key: authParams.accessToken,
612
+ secret: authParams.accessTokenSecret
613
+ };
614
+ return { getHeaders: (method, url) => {
615
+ const urlString = parse(UrlSchema, url);
616
+ return oauth.toHeader(oauth.authorize({
617
+ url: urlString,
618
+ method
619
+ }, oauthToken));
620
+ } };
621
+ }
622
+
623
+ //#endregion
624
+ //#region source/lib/integration-auth/utils.ts
625
+ /**
626
+ * Parses the provided configuration for an {@link IntegrationAuthProvider}.
627
+ * @param config - The configuration to parse.
628
+ * @throws {CommerceSdkValidationError} If the configuration is invalid.
629
+ *
630
+ * @internal
631
+ */
632
+ function __parseIntegrationAuthParams(config) {
633
+ const result = safeParse(IntegrationAuthParamsSchema, config);
634
+ if (!result.success) throw new CommerceSdkValidationError("Invalid IntegrationAuthProvider configuration", { issues: result.issues });
635
+ return result.output;
636
+ }
637
+ /**
638
+ * Asserts the provided configuration for an Adobe Commerce {@link IntegrationAuthProvider}.
639
+ * @param config The configuration to validate.
640
+ * @throws {CommerceSdkValidationError} If the configuration is invalid.
641
+ * @example
642
+ * ```typescript
643
+ * const config = {
644
+ * consumerKey: "your-consumer-key",
645
+ * consumerSecret: "your-consumer-secret",
646
+ * accessToken: "your-access-token",
647
+ * accessTokenSecret: "your-access-token-secret"
648
+ * };
649
+ *
650
+ * // This will validate the config and throw if invalid
651
+ * assertIntegrationAuthParams(config);
652
+ * ```
653
+ * @example
654
+ * ```typescript
655
+ * // Example of a failing assert:
656
+ * try {
657
+ * assertIntegrationAuthParams({
658
+ * consumerKey: "valid-consumer-key",
659
+ * // Missing required fields like consumerSecret, accessToken, accessTokenSecret
660
+ * });
661
+ * } catch (error) {
662
+ * console.error(error.message); // "Invalid IntegrationAuthProvider configuration"
663
+ * console.error(error.issues); // Array of validation issues
664
+ * }
665
+ * ```
666
+ */
667
+ function assertIntegrationAuthParams(config) {
668
+ __parseIntegrationAuthParams(config);
669
+ }
670
+ /**
671
+ * Resolves an {@link IntegrationAuthParams} from the given App Builder action inputs.
672
+ * @param params The App Builder action inputs to resolve the Integration authentication parameters from.
673
+ * @throws {CommerceSdkValidationError} If the parameters are invalid and cannot be resolved.
674
+ *
675
+ * @example
676
+ * ```typescript
677
+ * export function main(params) {
678
+ * const resolvedParams = resolveIntegrationAuthParams(params);
679
+ * console.log(resolvedParams); // { consumerKey: "your-consumer-key", consumerSecret: "your-consumer-secret", accessToken: "your-access-token", accessTokenSecret: "your-access-token-secret" }
680
+ * }
681
+ * ```
682
+ */
683
+ function resolveIntegrationAuthParams(params) {
684
+ return __parseIntegrationAuthParams({
685
+ consumerKey: params.AIO_COMMERCE_AUTH_INTEGRATION_CONSUMER_KEY,
686
+ consumerSecret: params.AIO_COMMERCE_AUTH_INTEGRATION_CONSUMER_SECRET,
687
+ accessToken: params.AIO_COMMERCE_AUTH_INTEGRATION_ACCESS_TOKEN,
688
+ accessTokenSecret: params.AIO_COMMERCE_AUTH_INTEGRATION_ACCESS_TOKEN_SECRET
689
+ });
690
+ }
691
+
692
+ //#endregion
693
+ //#region source/lib/utils.ts
694
+ const IMS_AUTH_PARAMS = [
695
+ "AIO_COMMERCE_AUTH_IMS_CLIENT_ID",
696
+ "AIO_COMMERCE_AUTH_IMS_CLIENT_SECRETS",
697
+ "AIO_COMMERCE_AUTH_IMS_TECHNICAL_ACCOUNT_ID",
698
+ "AIO_COMMERCE_AUTH_IMS_TECHNICAL_ACCOUNT_EMAIL",
699
+ "AIO_COMMERCE_AUTH_IMS_ORG_ID",
700
+ "AIO_COMMERCE_AUTH_IMS_SCOPES"
701
+ ];
702
+ const INTEGRATION_AUTH_PARAMS = [
703
+ "AIO_COMMERCE_AUTH_INTEGRATION_CONSUMER_KEY",
704
+ "AIO_COMMERCE_AUTH_INTEGRATION_CONSUMER_SECRET",
705
+ "AIO_COMMERCE_AUTH_INTEGRATION_ACCESS_TOKEN",
706
+ "AIO_COMMERCE_AUTH_INTEGRATION_ACCESS_TOKEN_SECRET"
707
+ ];
708
+ /**
709
+ * Automatically detects and resolves authentication parameters from App Builder action inputs.
710
+ * Attempts to resolve IMS authentication first, then falls back to Integration authentication.
711
+ *
712
+ * @param params The App Builder action inputs containing authentication parameters.
713
+ * @throws {CommerceSdkValidationError} If the parameters are invalid.
714
+ * @throws {Error} If neither IMS nor Integration authentication parameters can be resolved.
715
+ * @example
716
+ * ```typescript
717
+ * // Automatic detection (will use IMS if IMS params are present, otherwise Integration)
718
+ * export function main(params) {
719
+ * const authProvider = resolveAuthParams(params);
720
+ * console.log(authProvider.strategy); // "ims" or "integration"
721
+ * }
722
+ * ```
723
+ */
724
+ function resolveAuthParams(params) {
725
+ if (allNonEmpty(params, IMS_AUTH_PARAMS)) return {
726
+ ...resolveImsAuthParams(params),
727
+ strategy: "ims"
728
+ };
729
+ if (allNonEmpty(params, INTEGRATION_AUTH_PARAMS)) return {
730
+ ...resolveIntegrationAuthParams(params),
731
+ strategy: "integration"
732
+ };
733
+ throw new Error(`Can't resolve authentication options for the given params. Please provide either IMS options (${IMS_AUTH_PARAMS.join(", ")}) or Commerce integration options (${INTEGRATION_AUTH_PARAMS.join(", ")}).`);
734
+ }
735
+
736
+ //#endregion
737
+ export { assertImsAuthParams, assertIntegrationAuthParams, forwardImsAuthProvider, getForwardedImsAuthProvider, getImsAuthProvider, getIntegrationAuthProvider, isImsAuthProvider, isIntegrationAuthProvider, resolveAuthParams };