@augmenting-integrations/aws 0.0.1
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/README.md +17 -0
- package/dist/cognito.d.ts +46 -0
- package/dist/cognito.d.ts.map +1 -0
- package/dist/email.d.ts +18 -0
- package/dist/email.d.ts.map +1 -0
- package/dist/secrets.d.ts +7 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/server.cjs +166 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +140 -0
- package/dist/server.js.map +1 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# @augmenting-integrations/aws
|
|
2
|
+
|
|
3
|
+
Server-only AWS SDK wrappers used across spokes:
|
|
4
|
+
|
|
5
|
+
- `getSecret(arn)` -- module-cached Secrets Manager fetch
|
|
6
|
+
- `createEmailSender({fromAddress, region?})` -- factory returning a typed `sendEmail`
|
|
7
|
+
- Cognito per-user flows (`associateSoftwareToken`, `verifySoftwareToken`, `setUserMfaPreference`, `changePassword`) plus helpers (`regionFromIssuer`, `buildOtpAuthUri`, `computeSecretHash`)
|
|
8
|
+
|
|
9
|
+
All exports are under the `/server` subpath. Importing from `@augmenting-integrations/aws/server` keeps the AWS SDK out of any client bundle.
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import {
|
|
13
|
+
getSecret,
|
|
14
|
+
createEmailSender,
|
|
15
|
+
associateSoftwareToken,
|
|
16
|
+
} from "@augmenting-integrations/aws/server";
|
|
17
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse the Cognito region out of the issuer URL.
|
|
3
|
+
* issuer = https://cognito-idp.<region>.amazonaws.com/<pool>
|
|
4
|
+
*/
|
|
5
|
+
export declare function regionFromIssuer(issuer: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* SECRET_HASH for Cognito app clients that have a client secret. Only needed
|
|
8
|
+
* for username+password flows like InitiateAuth -- the access-token-based
|
|
9
|
+
* flows below don't use it.
|
|
10
|
+
*/
|
|
11
|
+
export declare function computeSecretHash(username: string, clientId: string, clientSecret: string): string;
|
|
12
|
+
export declare function associateSoftwareToken(args: {
|
|
13
|
+
accessToken: string;
|
|
14
|
+
region?: string;
|
|
15
|
+
}): Promise<{
|
|
16
|
+
secretCode: string;
|
|
17
|
+
}>;
|
|
18
|
+
export declare function verifySoftwareToken(args: {
|
|
19
|
+
accessToken: string;
|
|
20
|
+
code: string;
|
|
21
|
+
friendlyDeviceName?: string;
|
|
22
|
+
region?: string;
|
|
23
|
+
}): Promise<{
|
|
24
|
+
status: string;
|
|
25
|
+
}>;
|
|
26
|
+
export declare function setUserMfaPreference(args: {
|
|
27
|
+
accessToken: string;
|
|
28
|
+
enabled: boolean;
|
|
29
|
+
region?: string;
|
|
30
|
+
}): Promise<void>;
|
|
31
|
+
export declare function changePassword(args: {
|
|
32
|
+
accessToken: string;
|
|
33
|
+
previousPassword: string;
|
|
34
|
+
proposedPassword: string;
|
|
35
|
+
region?: string;
|
|
36
|
+
}): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Build the otpauth:// URI an authenticator app expects. Caller turns this
|
|
39
|
+
* into a QR code (data URL) before sending it to the browser.
|
|
40
|
+
*/
|
|
41
|
+
export declare function buildOtpAuthUri(params: {
|
|
42
|
+
secret: string;
|
|
43
|
+
accountName: string;
|
|
44
|
+
issuer: string;
|
|
45
|
+
}): string;
|
|
46
|
+
//# sourceMappingURL=cognito.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cognito.d.ts","sourceRoot":"","sources":["../src/cognito.ts"],"names":[],"mappings":"AA0BA;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAMvD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,MAAM,CAIR;AAED,wBAAsB,sBAAsB,CAAC,IAAI,EAAE;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAQlC;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAS9B;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhB;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQhB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,MAAM,CAKT"}
|
package/dist/email.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type SendEmailInput = {
|
|
2
|
+
to: string;
|
|
3
|
+
subject: string;
|
|
4
|
+
html: string;
|
|
5
|
+
text?: string;
|
|
6
|
+
};
|
|
7
|
+
export type SendEmailResult = {
|
|
8
|
+
messageId: string | undefined;
|
|
9
|
+
};
|
|
10
|
+
export type CreateEmailSenderOptions = {
|
|
11
|
+
/** RFC 5322 from address. Required. */
|
|
12
|
+
fromAddress: string;
|
|
13
|
+
/** AWS region; defaults to the SDK's environment-derived region. */
|
|
14
|
+
region?: string;
|
|
15
|
+
};
|
|
16
|
+
export type EmailSender = (input: SendEmailInput) => Promise<SendEmailResult>;
|
|
17
|
+
export declare function createEmailSender(opts: CreateEmailSenderOptions): EmailSender;
|
|
18
|
+
//# sourceMappingURL=email.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;AAE9E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,wBAAwB,GAAG,WAAW,CA6B7E"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch a secret value by ARN from Secrets Manager. Caches the resolved
|
|
3
|
+
* value for the lifetime of the Lambda container. If `arn` is undefined or
|
|
4
|
+
* empty, returns undefined -- consumer falls back to other config.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getSecret(arn: string | undefined): Promise<string | undefined>;
|
|
7
|
+
//# sourceMappingURL=secrets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AAgBA;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAQpF"}
|
package/dist/server.cjs
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/server.ts
|
|
21
|
+
var server_exports = {};
|
|
22
|
+
__export(server_exports, {
|
|
23
|
+
associateSoftwareToken: () => associateSoftwareToken,
|
|
24
|
+
buildOtpAuthUri: () => buildOtpAuthUri,
|
|
25
|
+
changePassword: () => changePassword,
|
|
26
|
+
computeSecretHash: () => computeSecretHash,
|
|
27
|
+
createEmailSender: () => createEmailSender,
|
|
28
|
+
getSecret: () => getSecret,
|
|
29
|
+
regionFromIssuer: () => regionFromIssuer,
|
|
30
|
+
setUserMfaPreference: () => setUserMfaPreference,
|
|
31
|
+
verifySoftwareToken: () => verifySoftwareToken
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(server_exports);
|
|
34
|
+
|
|
35
|
+
// src/secrets.ts
|
|
36
|
+
var import_client_secrets_manager = require("@aws-sdk/client-secrets-manager");
|
|
37
|
+
var cache = /* @__PURE__ */ new Map();
|
|
38
|
+
var client;
|
|
39
|
+
function getClient() {
|
|
40
|
+
if (!client) client = new import_client_secrets_manager.SecretsManagerClient({});
|
|
41
|
+
return client;
|
|
42
|
+
}
|
|
43
|
+
async function getSecret(arn) {
|
|
44
|
+
if (!arn) return void 0;
|
|
45
|
+
const cached = cache.get(arn);
|
|
46
|
+
if (cached !== void 0) return cached;
|
|
47
|
+
const out = await getClient().send(new import_client_secrets_manager.GetSecretValueCommand({ SecretId: arn }));
|
|
48
|
+
const value = out.SecretString ?? "";
|
|
49
|
+
cache.set(arn, value);
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// src/email.ts
|
|
54
|
+
var import_client_sesv2 = require("@aws-sdk/client-sesv2");
|
|
55
|
+
function createEmailSender(opts) {
|
|
56
|
+
if (!opts.fromAddress) {
|
|
57
|
+
throw new Error("[aws] createEmailSender requires `fromAddress`");
|
|
58
|
+
}
|
|
59
|
+
let client2;
|
|
60
|
+
const getClient3 = () => {
|
|
61
|
+
if (!client2) client2 = new import_client_sesv2.SESv2Client(opts.region ? { region: opts.region } : {});
|
|
62
|
+
return client2;
|
|
63
|
+
};
|
|
64
|
+
return async (input) => {
|
|
65
|
+
const command = new import_client_sesv2.SendEmailCommand({
|
|
66
|
+
FromEmailAddress: opts.fromAddress,
|
|
67
|
+
Destination: { ToAddresses: [input.to] },
|
|
68
|
+
Content: {
|
|
69
|
+
Simple: {
|
|
70
|
+
Subject: { Data: input.subject, Charset: "UTF-8" },
|
|
71
|
+
Body: {
|
|
72
|
+
Html: { Data: input.html, Charset: "UTF-8" },
|
|
73
|
+
Text: {
|
|
74
|
+
Data: input.text ?? stripHtml(input.html),
|
|
75
|
+
Charset: "UTF-8"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
const res = await getClient3().send(command);
|
|
82
|
+
return { messageId: res.MessageId };
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function stripHtml(html) {
|
|
86
|
+
return html.replace(/<style[\s\S]*?<\/style>/gi, "").replace(/<script[\s\S]*?<\/script>/gi, "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// src/cognito.ts
|
|
90
|
+
var import_node_crypto = require("crypto");
|
|
91
|
+
var import_client_cognito_identity_provider = require("@aws-sdk/client-cognito-identity-provider");
|
|
92
|
+
var cachedClient;
|
|
93
|
+
function getClient2(region) {
|
|
94
|
+
if (!cachedClient) {
|
|
95
|
+
cachedClient = new import_client_cognito_identity_provider.CognitoIdentityProviderClient(region ? { region } : {});
|
|
96
|
+
}
|
|
97
|
+
return cachedClient;
|
|
98
|
+
}
|
|
99
|
+
function regionFromIssuer(issuer) {
|
|
100
|
+
const m = issuer.match(/cognito-idp\.([^.]+)\.amazonaws\.com/);
|
|
101
|
+
if (!m?.[1]) {
|
|
102
|
+
throw new Error(`[aws/cognito] issuer not parseable for region: ${issuer}`);
|
|
103
|
+
}
|
|
104
|
+
return m[1];
|
|
105
|
+
}
|
|
106
|
+
function computeSecretHash(username, clientId, clientSecret) {
|
|
107
|
+
return (0, import_node_crypto.createHmac)("sha256", clientSecret).update(username + clientId).digest("base64");
|
|
108
|
+
}
|
|
109
|
+
async function associateSoftwareToken(args) {
|
|
110
|
+
const out = await getClient2(args.region).send(
|
|
111
|
+
new import_client_cognito_identity_provider.AssociateSoftwareTokenCommand({ AccessToken: args.accessToken })
|
|
112
|
+
);
|
|
113
|
+
if (!out.SecretCode) {
|
|
114
|
+
throw new Error("[aws/cognito] AssociateSoftwareToken returned no SecretCode");
|
|
115
|
+
}
|
|
116
|
+
return { secretCode: out.SecretCode };
|
|
117
|
+
}
|
|
118
|
+
async function verifySoftwareToken(args) {
|
|
119
|
+
const out = await getClient2(args.region).send(
|
|
120
|
+
new import_client_cognito_identity_provider.VerifySoftwareTokenCommand({
|
|
121
|
+
AccessToken: args.accessToken,
|
|
122
|
+
UserCode: args.code,
|
|
123
|
+
FriendlyDeviceName: args.friendlyDeviceName
|
|
124
|
+
})
|
|
125
|
+
);
|
|
126
|
+
return { status: out.Status ?? "UNKNOWN" };
|
|
127
|
+
}
|
|
128
|
+
async function setUserMfaPreference(args) {
|
|
129
|
+
await getClient2(args.region).send(
|
|
130
|
+
new import_client_cognito_identity_provider.SetUserMFAPreferenceCommand({
|
|
131
|
+
AccessToken: args.accessToken,
|
|
132
|
+
SoftwareTokenMfaSettings: {
|
|
133
|
+
Enabled: args.enabled,
|
|
134
|
+
PreferredMfa: args.enabled
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
async function changePassword(args) {
|
|
140
|
+
await getClient2(args.region).send(
|
|
141
|
+
new import_client_cognito_identity_provider.ChangePasswordCommand({
|
|
142
|
+
AccessToken: args.accessToken,
|
|
143
|
+
PreviousPassword: args.previousPassword,
|
|
144
|
+
ProposedPassword: args.proposedPassword
|
|
145
|
+
})
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
function buildOtpAuthUri(params) {
|
|
149
|
+
const issuer = encodeURIComponent(params.issuer);
|
|
150
|
+
const account = encodeURIComponent(params.accountName);
|
|
151
|
+
const secret = encodeURIComponent(params.secret);
|
|
152
|
+
return `otpauth://totp/${issuer}:${account}?secret=${secret}&issuer=${issuer}&algorithm=SHA1&digits=6&period=30`;
|
|
153
|
+
}
|
|
154
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
155
|
+
0 && (module.exports = {
|
|
156
|
+
associateSoftwareToken,
|
|
157
|
+
buildOtpAuthUri,
|
|
158
|
+
changePassword,
|
|
159
|
+
computeSecretHash,
|
|
160
|
+
createEmailSender,
|
|
161
|
+
getSecret,
|
|
162
|
+
regionFromIssuer,
|
|
163
|
+
setUserMfaPreference,
|
|
164
|
+
verifySoftwareToken
|
|
165
|
+
});
|
|
166
|
+
//# sourceMappingURL=server.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server.ts","../src/secrets.ts","../src/email.ts","../src/cognito.ts"],"sourcesContent":["export { getSecret } from \"./secrets.js\";\nexport {\n createEmailSender,\n type EmailSender,\n type SendEmailInput,\n type SendEmailResult,\n type CreateEmailSenderOptions,\n} from \"./email.js\";\nexport {\n associateSoftwareToken,\n verifySoftwareToken,\n setUserMfaPreference,\n changePassword,\n computeSecretHash,\n regionFromIssuer,\n buildOtpAuthUri,\n} from \"./cognito.js\";\n","import {\n SecretsManagerClient,\n GetSecretValueCommand,\n} from \"@aws-sdk/client-secrets-manager\";\n\n// Module-scope cache. One Lambda container = one client + one cache map.\n// Container recycling flushes the cache, picking up rotated secrets without\n// redeploy.\nconst cache = new Map<string, string>();\nlet client: SecretsManagerClient | undefined;\n\nfunction getClient(): SecretsManagerClient {\n if (!client) client = new SecretsManagerClient({});\n return client;\n}\n\n/**\n * Fetch a secret value by ARN from Secrets Manager. Caches the resolved\n * value for the lifetime of the Lambda container. If `arn` is undefined or\n * empty, returns undefined -- consumer falls back to other config.\n */\nexport async function getSecret(arn: string | undefined): Promise<string | undefined> {\n if (!arn) return undefined;\n const cached = cache.get(arn);\n if (cached !== undefined) return cached;\n const out = await getClient().send(new GetSecretValueCommand({ SecretId: arn }));\n const value = out.SecretString ?? \"\";\n cache.set(arn, value);\n return value;\n}\n","import { SESv2Client, SendEmailCommand } from \"@aws-sdk/client-sesv2\";\n\n// =============================================================================\n// SES v2 email sender.\n//\n// Each consuming app calls createEmailSender() with its own fromAddress (the\n// SES domain identity differs per tenant). The returned `sendEmail` function\n// is cached + ready for use.\n// =============================================================================\n\nexport type SendEmailInput = {\n to: string;\n subject: string;\n html: string;\n text?: string;\n};\n\nexport type SendEmailResult = {\n messageId: string | undefined;\n};\n\nexport type CreateEmailSenderOptions = {\n /** RFC 5322 from address. Required. */\n fromAddress: string;\n /** AWS region; defaults to the SDK's environment-derived region. */\n region?: string;\n};\n\nexport type EmailSender = (input: SendEmailInput) => Promise<SendEmailResult>;\n\nexport function createEmailSender(opts: CreateEmailSenderOptions): EmailSender {\n if (!opts.fromAddress) {\n throw new Error(\"[aws] createEmailSender requires `fromAddress`\");\n }\n let client: SESv2Client | undefined;\n const getClient = () => {\n if (!client) client = new SESv2Client(opts.region ? { region: opts.region } : {});\n return client;\n };\n return async (input) => {\n const command = new SendEmailCommand({\n FromEmailAddress: opts.fromAddress,\n Destination: { ToAddresses: [input.to] },\n Content: {\n Simple: {\n Subject: { Data: input.subject, Charset: \"UTF-8\" },\n Body: {\n Html: { Data: input.html, Charset: \"UTF-8\" },\n Text: {\n Data: input.text ?? stripHtml(input.html),\n Charset: \"UTF-8\",\n },\n },\n },\n },\n });\n const res = await getClient().send(command);\n return { messageId: res.MessageId };\n };\n}\n\nfunction stripHtml(html: string): string {\n return html\n .replace(/<style[\\s\\S]*?<\\/style>/gi, \"\")\n .replace(/<script[\\s\\S]*?<\\/script>/gi, \"\")\n .replace(/<[^>]+>/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n","import { createHmac } from \"node:crypto\";\nimport {\n CognitoIdentityProviderClient,\n AssociateSoftwareTokenCommand,\n VerifySoftwareTokenCommand,\n SetUserMFAPreferenceCommand,\n ChangePasswordCommand,\n} from \"@aws-sdk/client-cognito-identity-provider\";\n\n// =============================================================================\n// Cognito Identity Provider wrappers.\n//\n// User-pool is provisioned by central infra. Per-user flows (TOTP enrollment,\n// password change) need the caller's *access token* -- no admin-* fallback\n// because that would require broad cognito:* IAM permissions on every Lambda.\n// =============================================================================\n\nlet cachedClient: CognitoIdentityProviderClient | undefined;\n\nfunction getClient(region?: string): CognitoIdentityProviderClient {\n if (!cachedClient) {\n cachedClient = new CognitoIdentityProviderClient(region ? { region } : {});\n }\n return cachedClient;\n}\n\n/**\n * Parse the Cognito region out of the issuer URL.\n * issuer = https://cognito-idp.<region>.amazonaws.com/<pool>\n */\nexport function regionFromIssuer(issuer: string): string {\n const m = issuer.match(/cognito-idp\\.([^.]+)\\.amazonaws\\.com/);\n if (!m?.[1]) {\n throw new Error(`[aws/cognito] issuer not parseable for region: ${issuer}`);\n }\n return m[1];\n}\n\n/**\n * SECRET_HASH for Cognito app clients that have a client secret. Only needed\n * for username+password flows like InitiateAuth -- the access-token-based\n * flows below don't use it.\n */\nexport function computeSecretHash(\n username: string,\n clientId: string,\n clientSecret: string,\n): string {\n return createHmac(\"sha256\", clientSecret)\n .update(username + clientId)\n .digest(\"base64\");\n}\n\nexport async function associateSoftwareToken(args: {\n accessToken: string;\n region?: string;\n}): Promise<{ secretCode: string }> {\n const out = await getClient(args.region).send(\n new AssociateSoftwareTokenCommand({ AccessToken: args.accessToken }),\n );\n if (!out.SecretCode) {\n throw new Error(\"[aws/cognito] AssociateSoftwareToken returned no SecretCode\");\n }\n return { secretCode: out.SecretCode };\n}\n\nexport async function verifySoftwareToken(args: {\n accessToken: string;\n code: string;\n friendlyDeviceName?: string;\n region?: string;\n}): Promise<{ status: string }> {\n const out = await getClient(args.region).send(\n new VerifySoftwareTokenCommand({\n AccessToken: args.accessToken,\n UserCode: args.code,\n FriendlyDeviceName: args.friendlyDeviceName,\n }),\n );\n return { status: out.Status ?? \"UNKNOWN\" };\n}\n\nexport async function setUserMfaPreference(args: {\n accessToken: string;\n enabled: boolean;\n region?: string;\n}): Promise<void> {\n await getClient(args.region).send(\n new SetUserMFAPreferenceCommand({\n AccessToken: args.accessToken,\n SoftwareTokenMfaSettings: {\n Enabled: args.enabled,\n PreferredMfa: args.enabled,\n },\n }),\n );\n}\n\nexport async function changePassword(args: {\n accessToken: string;\n previousPassword: string;\n proposedPassword: string;\n region?: string;\n}): Promise<void> {\n await getClient(args.region).send(\n new ChangePasswordCommand({\n AccessToken: args.accessToken,\n PreviousPassword: args.previousPassword,\n ProposedPassword: args.proposedPassword,\n }),\n );\n}\n\n/**\n * Build the otpauth:// URI an authenticator app expects. Caller turns this\n * into a QR code (data URL) before sending it to the browser.\n */\nexport function buildOtpAuthUri(params: {\n secret: string;\n accountName: string;\n issuer: string;\n}): string {\n const issuer = encodeURIComponent(params.issuer);\n const account = encodeURIComponent(params.accountName);\n const secret = encodeURIComponent(params.secret);\n return `otpauth://totp/${issuer}:${account}?secret=${secret}&issuer=${issuer}&algorithm=SHA1&digits=6&period=30`;\n}\n"],"mappings":"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","names":["client","getClient","getClient"]}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { getSecret } from "./secrets.js";
|
|
2
|
+
export { createEmailSender, type EmailSender, type SendEmailInput, type SendEmailResult, type CreateEmailSenderOptions, } from "./email.js";
|
|
3
|
+
export { associateSoftwareToken, verifySoftwareToken, setUserMfaPreference, changePassword, computeSecretHash, regionFromIssuer, buildOtpAuthUri, } from "./cognito.js";
|
|
4
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EACL,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,wBAAwB,GAC9B,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,GAChB,MAAM,cAAc,CAAC"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// src/secrets.ts
|
|
2
|
+
import {
|
|
3
|
+
SecretsManagerClient,
|
|
4
|
+
GetSecretValueCommand
|
|
5
|
+
} from "@aws-sdk/client-secrets-manager";
|
|
6
|
+
var cache = /* @__PURE__ */ new Map();
|
|
7
|
+
var client;
|
|
8
|
+
function getClient() {
|
|
9
|
+
if (!client) client = new SecretsManagerClient({});
|
|
10
|
+
return client;
|
|
11
|
+
}
|
|
12
|
+
async function getSecret(arn) {
|
|
13
|
+
if (!arn) return void 0;
|
|
14
|
+
const cached = cache.get(arn);
|
|
15
|
+
if (cached !== void 0) return cached;
|
|
16
|
+
const out = await getClient().send(new GetSecretValueCommand({ SecretId: arn }));
|
|
17
|
+
const value = out.SecretString ?? "";
|
|
18
|
+
cache.set(arn, value);
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// src/email.ts
|
|
23
|
+
import { SESv2Client, SendEmailCommand } from "@aws-sdk/client-sesv2";
|
|
24
|
+
function createEmailSender(opts) {
|
|
25
|
+
if (!opts.fromAddress) {
|
|
26
|
+
throw new Error("[aws] createEmailSender requires `fromAddress`");
|
|
27
|
+
}
|
|
28
|
+
let client2;
|
|
29
|
+
const getClient3 = () => {
|
|
30
|
+
if (!client2) client2 = new SESv2Client(opts.region ? { region: opts.region } : {});
|
|
31
|
+
return client2;
|
|
32
|
+
};
|
|
33
|
+
return async (input) => {
|
|
34
|
+
const command = new SendEmailCommand({
|
|
35
|
+
FromEmailAddress: opts.fromAddress,
|
|
36
|
+
Destination: { ToAddresses: [input.to] },
|
|
37
|
+
Content: {
|
|
38
|
+
Simple: {
|
|
39
|
+
Subject: { Data: input.subject, Charset: "UTF-8" },
|
|
40
|
+
Body: {
|
|
41
|
+
Html: { Data: input.html, Charset: "UTF-8" },
|
|
42
|
+
Text: {
|
|
43
|
+
Data: input.text ?? stripHtml(input.html),
|
|
44
|
+
Charset: "UTF-8"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
const res = await getClient3().send(command);
|
|
51
|
+
return { messageId: res.MessageId };
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function stripHtml(html) {
|
|
55
|
+
return html.replace(/<style[\s\S]*?<\/style>/gi, "").replace(/<script[\s\S]*?<\/script>/gi, "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/cognito.ts
|
|
59
|
+
import { createHmac } from "crypto";
|
|
60
|
+
import {
|
|
61
|
+
CognitoIdentityProviderClient,
|
|
62
|
+
AssociateSoftwareTokenCommand,
|
|
63
|
+
VerifySoftwareTokenCommand,
|
|
64
|
+
SetUserMFAPreferenceCommand,
|
|
65
|
+
ChangePasswordCommand
|
|
66
|
+
} from "@aws-sdk/client-cognito-identity-provider";
|
|
67
|
+
var cachedClient;
|
|
68
|
+
function getClient2(region) {
|
|
69
|
+
if (!cachedClient) {
|
|
70
|
+
cachedClient = new CognitoIdentityProviderClient(region ? { region } : {});
|
|
71
|
+
}
|
|
72
|
+
return cachedClient;
|
|
73
|
+
}
|
|
74
|
+
function regionFromIssuer(issuer) {
|
|
75
|
+
const m = issuer.match(/cognito-idp\.([^.]+)\.amazonaws\.com/);
|
|
76
|
+
if (!m?.[1]) {
|
|
77
|
+
throw new Error(`[aws/cognito] issuer not parseable for region: ${issuer}`);
|
|
78
|
+
}
|
|
79
|
+
return m[1];
|
|
80
|
+
}
|
|
81
|
+
function computeSecretHash(username, clientId, clientSecret) {
|
|
82
|
+
return createHmac("sha256", clientSecret).update(username + clientId).digest("base64");
|
|
83
|
+
}
|
|
84
|
+
async function associateSoftwareToken(args) {
|
|
85
|
+
const out = await getClient2(args.region).send(
|
|
86
|
+
new AssociateSoftwareTokenCommand({ AccessToken: args.accessToken })
|
|
87
|
+
);
|
|
88
|
+
if (!out.SecretCode) {
|
|
89
|
+
throw new Error("[aws/cognito] AssociateSoftwareToken returned no SecretCode");
|
|
90
|
+
}
|
|
91
|
+
return { secretCode: out.SecretCode };
|
|
92
|
+
}
|
|
93
|
+
async function verifySoftwareToken(args) {
|
|
94
|
+
const out = await getClient2(args.region).send(
|
|
95
|
+
new VerifySoftwareTokenCommand({
|
|
96
|
+
AccessToken: args.accessToken,
|
|
97
|
+
UserCode: args.code,
|
|
98
|
+
FriendlyDeviceName: args.friendlyDeviceName
|
|
99
|
+
})
|
|
100
|
+
);
|
|
101
|
+
return { status: out.Status ?? "UNKNOWN" };
|
|
102
|
+
}
|
|
103
|
+
async function setUserMfaPreference(args) {
|
|
104
|
+
await getClient2(args.region).send(
|
|
105
|
+
new SetUserMFAPreferenceCommand({
|
|
106
|
+
AccessToken: args.accessToken,
|
|
107
|
+
SoftwareTokenMfaSettings: {
|
|
108
|
+
Enabled: args.enabled,
|
|
109
|
+
PreferredMfa: args.enabled
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
async function changePassword(args) {
|
|
115
|
+
await getClient2(args.region).send(
|
|
116
|
+
new ChangePasswordCommand({
|
|
117
|
+
AccessToken: args.accessToken,
|
|
118
|
+
PreviousPassword: args.previousPassword,
|
|
119
|
+
ProposedPassword: args.proposedPassword
|
|
120
|
+
})
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
function buildOtpAuthUri(params) {
|
|
124
|
+
const issuer = encodeURIComponent(params.issuer);
|
|
125
|
+
const account = encodeURIComponent(params.accountName);
|
|
126
|
+
const secret = encodeURIComponent(params.secret);
|
|
127
|
+
return `otpauth://totp/${issuer}:${account}?secret=${secret}&issuer=${issuer}&algorithm=SHA1&digits=6&period=30`;
|
|
128
|
+
}
|
|
129
|
+
export {
|
|
130
|
+
associateSoftwareToken,
|
|
131
|
+
buildOtpAuthUri,
|
|
132
|
+
changePassword,
|
|
133
|
+
computeSecretHash,
|
|
134
|
+
createEmailSender,
|
|
135
|
+
getSecret,
|
|
136
|
+
regionFromIssuer,
|
|
137
|
+
setUserMfaPreference,
|
|
138
|
+
verifySoftwareToken
|
|
139
|
+
};
|
|
140
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/secrets.ts","../src/email.ts","../src/cognito.ts"],"sourcesContent":["import {\n SecretsManagerClient,\n GetSecretValueCommand,\n} from \"@aws-sdk/client-secrets-manager\";\n\n// Module-scope cache. One Lambda container = one client + one cache map.\n// Container recycling flushes the cache, picking up rotated secrets without\n// redeploy.\nconst cache = new Map<string, string>();\nlet client: SecretsManagerClient | undefined;\n\nfunction getClient(): SecretsManagerClient {\n if (!client) client = new SecretsManagerClient({});\n return client;\n}\n\n/**\n * Fetch a secret value by ARN from Secrets Manager. Caches the resolved\n * value for the lifetime of the Lambda container. If `arn` is undefined or\n * empty, returns undefined -- consumer falls back to other config.\n */\nexport async function getSecret(arn: string | undefined): Promise<string | undefined> {\n if (!arn) return undefined;\n const cached = cache.get(arn);\n if (cached !== undefined) return cached;\n const out = await getClient().send(new GetSecretValueCommand({ SecretId: arn }));\n const value = out.SecretString ?? \"\";\n cache.set(arn, value);\n return value;\n}\n","import { SESv2Client, SendEmailCommand } from \"@aws-sdk/client-sesv2\";\n\n// =============================================================================\n// SES v2 email sender.\n//\n// Each consuming app calls createEmailSender() with its own fromAddress (the\n// SES domain identity differs per tenant). The returned `sendEmail` function\n// is cached + ready for use.\n// =============================================================================\n\nexport type SendEmailInput = {\n to: string;\n subject: string;\n html: string;\n text?: string;\n};\n\nexport type SendEmailResult = {\n messageId: string | undefined;\n};\n\nexport type CreateEmailSenderOptions = {\n /** RFC 5322 from address. Required. */\n fromAddress: string;\n /** AWS region; defaults to the SDK's environment-derived region. */\n region?: string;\n};\n\nexport type EmailSender = (input: SendEmailInput) => Promise<SendEmailResult>;\n\nexport function createEmailSender(opts: CreateEmailSenderOptions): EmailSender {\n if (!opts.fromAddress) {\n throw new Error(\"[aws] createEmailSender requires `fromAddress`\");\n }\n let client: SESv2Client | undefined;\n const getClient = () => {\n if (!client) client = new SESv2Client(opts.region ? { region: opts.region } : {});\n return client;\n };\n return async (input) => {\n const command = new SendEmailCommand({\n FromEmailAddress: opts.fromAddress,\n Destination: { ToAddresses: [input.to] },\n Content: {\n Simple: {\n Subject: { Data: input.subject, Charset: \"UTF-8\" },\n Body: {\n Html: { Data: input.html, Charset: \"UTF-8\" },\n Text: {\n Data: input.text ?? stripHtml(input.html),\n Charset: \"UTF-8\",\n },\n },\n },\n },\n });\n const res = await getClient().send(command);\n return { messageId: res.MessageId };\n };\n}\n\nfunction stripHtml(html: string): string {\n return html\n .replace(/<style[\\s\\S]*?<\\/style>/gi, \"\")\n .replace(/<script[\\s\\S]*?<\\/script>/gi, \"\")\n .replace(/<[^>]+>/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n","import { createHmac } from \"node:crypto\";\nimport {\n CognitoIdentityProviderClient,\n AssociateSoftwareTokenCommand,\n VerifySoftwareTokenCommand,\n SetUserMFAPreferenceCommand,\n ChangePasswordCommand,\n} from \"@aws-sdk/client-cognito-identity-provider\";\n\n// =============================================================================\n// Cognito Identity Provider wrappers.\n//\n// User-pool is provisioned by central infra. Per-user flows (TOTP enrollment,\n// password change) need the caller's *access token* -- no admin-* fallback\n// because that would require broad cognito:* IAM permissions on every Lambda.\n// =============================================================================\n\nlet cachedClient: CognitoIdentityProviderClient | undefined;\n\nfunction getClient(region?: string): CognitoIdentityProviderClient {\n if (!cachedClient) {\n cachedClient = new CognitoIdentityProviderClient(region ? { region } : {});\n }\n return cachedClient;\n}\n\n/**\n * Parse the Cognito region out of the issuer URL.\n * issuer = https://cognito-idp.<region>.amazonaws.com/<pool>\n */\nexport function regionFromIssuer(issuer: string): string {\n const m = issuer.match(/cognito-idp\\.([^.]+)\\.amazonaws\\.com/);\n if (!m?.[1]) {\n throw new Error(`[aws/cognito] issuer not parseable for region: ${issuer}`);\n }\n return m[1];\n}\n\n/**\n * SECRET_HASH for Cognito app clients that have a client secret. Only needed\n * for username+password flows like InitiateAuth -- the access-token-based\n * flows below don't use it.\n */\nexport function computeSecretHash(\n username: string,\n clientId: string,\n clientSecret: string,\n): string {\n return createHmac(\"sha256\", clientSecret)\n .update(username + clientId)\n .digest(\"base64\");\n}\n\nexport async function associateSoftwareToken(args: {\n accessToken: string;\n region?: string;\n}): Promise<{ secretCode: string }> {\n const out = await getClient(args.region).send(\n new AssociateSoftwareTokenCommand({ AccessToken: args.accessToken }),\n );\n if (!out.SecretCode) {\n throw new Error(\"[aws/cognito] AssociateSoftwareToken returned no SecretCode\");\n }\n return { secretCode: out.SecretCode };\n}\n\nexport async function verifySoftwareToken(args: {\n accessToken: string;\n code: string;\n friendlyDeviceName?: string;\n region?: string;\n}): Promise<{ status: string }> {\n const out = await getClient(args.region).send(\n new VerifySoftwareTokenCommand({\n AccessToken: args.accessToken,\n UserCode: args.code,\n FriendlyDeviceName: args.friendlyDeviceName,\n }),\n );\n return { status: out.Status ?? \"UNKNOWN\" };\n}\n\nexport async function setUserMfaPreference(args: {\n accessToken: string;\n enabled: boolean;\n region?: string;\n}): Promise<void> {\n await getClient(args.region).send(\n new SetUserMFAPreferenceCommand({\n AccessToken: args.accessToken,\n SoftwareTokenMfaSettings: {\n Enabled: args.enabled,\n PreferredMfa: args.enabled,\n },\n }),\n );\n}\n\nexport async function changePassword(args: {\n accessToken: string;\n previousPassword: string;\n proposedPassword: string;\n region?: string;\n}): Promise<void> {\n await getClient(args.region).send(\n new ChangePasswordCommand({\n AccessToken: args.accessToken,\n PreviousPassword: args.previousPassword,\n ProposedPassword: args.proposedPassword,\n }),\n );\n}\n\n/**\n * Build the otpauth:// URI an authenticator app expects. Caller turns this\n * into a QR code (data URL) before sending it to the browser.\n */\nexport function buildOtpAuthUri(params: {\n secret: string;\n accountName: string;\n issuer: string;\n}): string {\n const issuer = encodeURIComponent(params.issuer);\n const account = encodeURIComponent(params.accountName);\n const secret = encodeURIComponent(params.secret);\n return `otpauth://totp/${issuer}:${account}?secret=${secret}&issuer=${issuer}&algorithm=SHA1&digits=6&period=30`;\n}\n"],"mappings":"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","names":["client","getClient","getClient"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@augmenting-integrations/aws",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "AWS SDK wrappers for the augint platform: SecretsManager value cache, SES v2 email sender, Cognito IDP per-user flows (TOTP enrollment, password change). All server-only.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"sideEffects": false,
|
|
10
|
+
"main": "./dist/server.cjs",
|
|
11
|
+
"module": "./dist/server.js",
|
|
12
|
+
"types": "./dist/server.d.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
"./server": {
|
|
15
|
+
"types": "./dist/server.d.ts",
|
|
16
|
+
"import": "./dist/server.js",
|
|
17
|
+
"require": "./dist/server.cjs"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup",
|
|
26
|
+
"clean": "rm -rf dist",
|
|
27
|
+
"test": "vitest run --passWithNoTests"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@aws-sdk/client-secrets-manager": "^3.700.0",
|
|
31
|
+
"@aws-sdk/client-sesv2": "^3.700.0",
|
|
32
|
+
"@aws-sdk/client-cognito-identity-provider": "^3.700.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"tsup": "^8.3.5",
|
|
36
|
+
"typescript": "^5.7.2",
|
|
37
|
+
"vitest": "^4.1.5"
|
|
38
|
+
}
|
|
39
|
+
}
|