@aws-amplify/backend-notifications 0.0.0-test-20260708085627
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 +235 -0
- package/lib/campaign-association-asset/index.js +48413 -0
- package/lib/constants.d.ts +122 -0
- package/lib/constants.js +125 -0
- package/lib/construct.d.ts +283 -0
- package/lib/construct.js +824 -0
- package/lib/factory.d.ts +53 -0
- package/lib/factory.js +158 -0
- package/lib/handler-asset/index.js +530 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.js +5 -0
- package/lib/lambda/campaign-association/handler.d.ts +29 -0
- package/lib/lambda/campaign-association/handler.js +80 -0
- package/lib/lambda/campaign-association/integration.d.ts +59 -0
- package/lib/lambda/campaign-association/integration.js +108 -0
- package/lib/lambda/campaign-association/onboarding.d.ts +25 -0
- package/lib/lambda/campaign-association/onboarding.js +74 -0
- package/lib/lambda/identify/device_resolver.d.ts +18 -0
- package/lib/lambda/identify/device_resolver.js +56 -0
- package/lib/lambda/identify/handler.d.ts +17 -0
- package/lib/lambda/identify/handler.js +124 -0
- package/lib/lambda/identify/mapping.d.ts +61 -0
- package/lib/lambda/identify/mapping.js +171 -0
- package/lib/lambda/identify/merge_resolver.d.ts +26 -0
- package/lib/lambda/identify/merge_resolver.js +36 -0
- package/lib/lambda/identify/principal.d.ts +68 -0
- package/lib/lambda/identify/principal.js +48 -0
- package/lib/lambda/identify/profile_resolver.d.ts +35 -0
- package/lib/lambda/identify/profile_resolver.js +65 -0
- package/lib/lambda/identify/types.d.ts +90 -0
- package/lib/lambda/identify/types.js +10 -0
- package/lib/lambda/identify/validation.d.ts +15 -0
- package/lib/lambda/identify/validation.js +99 -0
- package/lib/lambda/push/delivery.d.ts +49 -0
- package/lib/lambda/push/delivery.js +120 -0
- package/lib/lambda/push/device_lookup.d.ts +35 -0
- package/lib/lambda/push/device_lookup.js +88 -0
- package/lib/lambda/push/eum_client.d.ts +31 -0
- package/lib/lambda/push/eum_client.js +140 -0
- package/lib/lambda/push/event.d.ts +32 -0
- package/lib/lambda/push/event.js +122 -0
- package/lib/lambda/push/fixtures/real_journey_event.d.ts +19 -0
- package/lib/lambda/push/fixtures/real_journey_event.js +22 -0
- package/lib/lambda/push/handler.d.ts +15 -0
- package/lib/lambda/push/handler.js +86 -0
- package/lib/lambda/push/message_template.d.ts +72 -0
- package/lib/lambda/push/message_template.js +270 -0
- package/lib/lambda/push/payload.d.ts +25 -0
- package/lib/lambda/push/payload.js +53 -0
- package/lib/lambda/push/types.d.ts +153 -0
- package/lib/lambda/push/types.js +4 -0
- package/lib/lambda/shared/retry.d.ts +18 -0
- package/lib/lambda/shared/retry.js +45 -0
- package/lib/object_types.d.ts +63 -0
- package/lib/object_types.js +131 -0
- package/lib/push-handler-asset/index.js +75517 -0
- package/lib/types.d.ts +125 -0
- package/lib/types.js +2 -0
- package/package.json +49 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { MAX_ATTRIBUTE_LENGTH } from '../../constants.js';
|
|
2
|
+
/**
|
|
3
|
+
* Flatten a multi-value attribute into a single string that fits within the
|
|
4
|
+
* Customer Profiles attribute-value limit.
|
|
5
|
+
*
|
|
6
|
+
* - Zero values -> empty string.
|
|
7
|
+
* - Exactly one value -> that element verbatim.
|
|
8
|
+
* - More than one value -> JSON.stringify of the array (preserves all values).
|
|
9
|
+
* The result is truncated to {@link MAX_ATTRIBUTE_LENGTH} characters.
|
|
10
|
+
*/
|
|
11
|
+
export const flatten = (values) => {
|
|
12
|
+
let out;
|
|
13
|
+
if (!values || values.length === 0) {
|
|
14
|
+
out = '';
|
|
15
|
+
}
|
|
16
|
+
else if (values.length === 1) {
|
|
17
|
+
out = values[0] ?? '';
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
out = JSON.stringify(values);
|
|
21
|
+
}
|
|
22
|
+
return out.length > MAX_ATTRIBUTE_LENGTH
|
|
23
|
+
? out.slice(0, MAX_ATTRIBUTE_LENGTH)
|
|
24
|
+
: out;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Serialize a map of multi-value attributes into a single JSON string.
|
|
28
|
+
*
|
|
29
|
+
* Customer Profiles requires statically declared field mappings, so dynamic key
|
|
30
|
+
* spaces (customProperties / userAttributes) are collapsed into one JSON
|
|
31
|
+
* attribute. Each value is flattened per the single-string rule first.
|
|
32
|
+
*/
|
|
33
|
+
const serializeMultiValueMap = (map) => {
|
|
34
|
+
if (!map) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
const keys = Object.keys(map);
|
|
38
|
+
if (keys.length === 0) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
const flattened = {};
|
|
42
|
+
for (const key of keys) {
|
|
43
|
+
flattened[key] = flatten(map[key]);
|
|
44
|
+
}
|
|
45
|
+
const out = JSON.stringify(flattened);
|
|
46
|
+
return out.length > MAX_ATTRIBUTE_LENGTH
|
|
47
|
+
? out.slice(0, MAX_ATTRIBUTE_LENGTH)
|
|
48
|
+
: out;
|
|
49
|
+
};
|
|
50
|
+
const splitName = (name) => {
|
|
51
|
+
const trimmed = name.trim();
|
|
52
|
+
const idx = trimmed.indexOf(' ');
|
|
53
|
+
if (idx === -1) {
|
|
54
|
+
return { firstName: trimmed };
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
firstName: trimmed.slice(0, idx),
|
|
58
|
+
lastName: trimmed.slice(idx + 1).trim() || undefined,
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
/** Drop keys whose value is undefined/null/empty. */
|
|
62
|
+
const compact = (obj) => {
|
|
63
|
+
const out = {};
|
|
64
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
65
|
+
if (v !== undefined && v !== null && v !== '') {
|
|
66
|
+
out[k] = v;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
};
|
|
71
|
+
/** Which promoted targeting attribute a channel type activates. */
|
|
72
|
+
export const targetingFlagsForChannel = (channelType) => {
|
|
73
|
+
switch (channelType) {
|
|
74
|
+
case 'GCM':
|
|
75
|
+
return { hasGCM: 'true' };
|
|
76
|
+
case 'APNS':
|
|
77
|
+
case 'APNS_SANDBOX':
|
|
78
|
+
return { hasAPNS: 'true' };
|
|
79
|
+
default:
|
|
80
|
+
return {};
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
/** True when the request carries enough data to register/update a device. */
|
|
84
|
+
export const hasDeviceData = (req) => !!req.options?.deviceId;
|
|
85
|
+
/**
|
|
86
|
+
* Build the UpdateProfile payload (standard fields + Attributes map) for the
|
|
87
|
+
* profile identified by the verified {@link Principal} (authed `sub` or guest
|
|
88
|
+
* `cognitoIdentityId`).
|
|
89
|
+
*
|
|
90
|
+
* SECURITY: `principal.value` comes from an authorizer-verified source (JWT
|
|
91
|
+
* claims or the IAM/Cognito identity). The client-supplied `userId` is stored
|
|
92
|
+
* only as the `appUserId` attribute — never the identity.
|
|
93
|
+
*/
|
|
94
|
+
export const buildProfileUpdate = (principal, req) => {
|
|
95
|
+
const profile = req.userProfile ?? {};
|
|
96
|
+
const demographic = profile.demographic;
|
|
97
|
+
const location = profile.location;
|
|
98
|
+
const name = profile.name ? splitName(profile.name) : undefined;
|
|
99
|
+
const flags = targetingFlagsForChannel(req.options?.channelType);
|
|
100
|
+
const attributes = compact({
|
|
101
|
+
// Identity trail (searchable key is separate; this is for reference only).
|
|
102
|
+
[principal.keyName]: principal.value,
|
|
103
|
+
appUserId: req.userId,
|
|
104
|
+
// Targeting / custom attributes for Connect segmentation.
|
|
105
|
+
plan: profile.plan,
|
|
106
|
+
locale: demographic?.locale,
|
|
107
|
+
platform: demographic?.platform,
|
|
108
|
+
appVersion: demographic?.appVersion,
|
|
109
|
+
make: demographic?.make,
|
|
110
|
+
model: demographic?.model,
|
|
111
|
+
modelVersion: demographic?.modelVersion,
|
|
112
|
+
platformVersion: demographic?.platformVersion,
|
|
113
|
+
timezone: demographic?.timezone,
|
|
114
|
+
latitude: location?.latitude !== undefined ? String(location.latitude) : undefined,
|
|
115
|
+
longitude: location?.longitude !== undefined
|
|
116
|
+
? String(location.longitude)
|
|
117
|
+
: undefined,
|
|
118
|
+
customProperties: serializeMultiValueMap(profile.customProperties),
|
|
119
|
+
userAttributes: serializeMultiValueMap(req.options?.userAttributes),
|
|
120
|
+
metrics: profile.metrics ? JSON.stringify(profile.metrics) : undefined,
|
|
121
|
+
// Promoted push-capability flags.
|
|
122
|
+
hasGCM: flags.hasGCM,
|
|
123
|
+
hasAPNS: flags.hasAPNS,
|
|
124
|
+
});
|
|
125
|
+
const address = compact({
|
|
126
|
+
city: location?.city,
|
|
127
|
+
country: location?.country,
|
|
128
|
+
postalCode: location?.postalCode,
|
|
129
|
+
province: location?.region,
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
emailAddress: profile.email,
|
|
133
|
+
firstName: name?.firstName,
|
|
134
|
+
lastName: name?.lastName,
|
|
135
|
+
address: Object.keys(address).length > 0 ? address : undefined,
|
|
136
|
+
attributes,
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Build the `_source.*` payload for the AmplifyDevice object type.
|
|
141
|
+
*
|
|
142
|
+
* Keyed UNIQUELY by the stable `deviceId`; `deviceToken` (options.address) is a
|
|
143
|
+
* mutable field so token refreshes upsert the same object in place. Carries the
|
|
144
|
+
* verified identity field (`cognitoSub` for authed, `cognitoIdentityId` for
|
|
145
|
+
* guest) as the PROFILE-resolution key so the device merges into the profile
|
|
146
|
+
* bound to that {@link Principal}.
|
|
147
|
+
*
|
|
148
|
+
* `platform` / `appVersion` come from the options, falling back to the
|
|
149
|
+
* `userProfile.demographic` equivalents. `updatedAt` is server-set on every
|
|
150
|
+
* write; `createdAt` is the IMMUTABLE first-registration time — the caller
|
|
151
|
+
* passes the value read back from the existing device object (if any) so it is
|
|
152
|
+
* preserved across PutProfileObject replacements, otherwise it defaults to now.
|
|
153
|
+
*/
|
|
154
|
+
export const buildDeviceObject = (principal, req, existingCreatedAt) => {
|
|
155
|
+
const options = req.options ?? {};
|
|
156
|
+
const demographic = req.userProfile?.demographic;
|
|
157
|
+
const platform = options.platform ?? demographic?.platform;
|
|
158
|
+
const appVersion = options.appVersion ?? demographic?.appVersion;
|
|
159
|
+
const now = new Date().toISOString();
|
|
160
|
+
return compact({
|
|
161
|
+
[principal.objectField]: principal.value,
|
|
162
|
+
deviceId: options.deviceId,
|
|
163
|
+
deviceToken: options.address,
|
|
164
|
+
channelType: options.channelType,
|
|
165
|
+
platform,
|
|
166
|
+
appVersion,
|
|
167
|
+
createdAt: existingCreatedAt ?? now,
|
|
168
|
+
updatedAt: now,
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFwcGluZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9sYW1iZGEvaWRlbnRpZnkvbWFwcGluZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQW9CMUQ7Ozs7Ozs7O0dBUUc7QUFDSCxNQUFNLENBQUMsTUFBTSxPQUFPLEdBQUcsQ0FBQyxNQUFnQixFQUFVLEVBQUU7SUFDbEQsSUFBSSxHQUFXLENBQUM7SUFDaEIsSUFBSSxDQUFDLE1BQU0sSUFBSSxNQUFNLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQ25DLEdBQUcsR0FBRyxFQUFFLENBQUM7SUFDWCxDQUFDO1NBQU0sSUFBSSxNQUFNLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQy9CLEdBQUcsR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ3hCLENBQUM7U0FBTSxDQUFDO1FBQ04sR0FBRyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDL0IsQ0FBQztJQUNELE9BQU8sR0FBRyxDQUFDLE1BQU0sR0FBRyxvQkFBb0I7UUFDdEMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLG9CQUFvQixDQUFDO1FBQ3BDLENBQUMsQ0FBQyxHQUFHLENBQUM7QUFDVixDQUFDLENBQUM7QUFFRjs7Ozs7O0dBTUc7QUFDSCxNQUFNLHNCQUFzQixHQUFHLENBQzdCLEdBQXlDLEVBQ3JCLEVBQUU7SUFDdEIsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ1QsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztJQUNELE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDOUIsSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQ3RC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RyxHQUFHLElBQUksSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUM7SUFFckMsT0FBTyxPQUFPLENBQUM7UUFDYixDQUFDLFNBQVMsQ0FBQyxXQUFXLENBQUMsRUFBRSxTQUFTLENBQUMsS0FBSztRQUN4QyxRQUFRLEVBQUUsT0FBTyxDQUFDLFFBQVE7UUFDMUIsV0FBVyxFQUFFLE9BQU8sQ0FBQyxPQUFPO1FBQzVCLFdBQVcsRUFBRSxPQUFPLENBQUMsV0FBVztRQUNoQyxRQUFRO1FBQ1IsVUFBVTtRQUNWLFNBQVMsRUFBRSxpQkFBaUIsSUFBSSxHQUFHO1FBQ25DLFNBQVMsRUFBRSxHQUFHO0tBQ2YsQ0FBMkIsQ0FBQztBQUMvQixDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDaGFubmVsVHlwZSwgSWRlbnRpZnlVc2VyUmVxdWVzdCB9IGZyb20gJy4vdHlwZXMuanMnO1xuaW1wb3J0IHsgTUFYX0FUVFJJQlVURV9MRU5HVEggfSBmcm9tICcuLi8uLi9jb25zdGFudHMuanMnO1xuaW1wb3J0IHsgUHJpbmNpcGFsIH0gZnJvbSAnLi9wcmluY2lwYWwuanMnO1xuXG4vKiogQSBzdGFuZGFyZCBDdXN0b21lciBQcm9maWxlcyBhZGRyZXNzIChzdWJzZXQgdXNlZCBieSB0aGlzIGJhY2tlbmQpLiAqL1xuZXhwb3J0IHR5cGUgUHJvZmlsZUFkZHJlc3MgPSB7XG4gIGNpdHk/OiBzdHJpbmc7XG4gIGNvdW50cnk/OiBzdHJpbmc7XG4gIHBvc3RhbENvZGU/OiBzdHJpbmc7XG4gIHByb3ZpbmNlPzogc3RyaW5nO1xufTtcblxuLyoqIFBlcnNvbiBkYXRhIG1hcHBlZCB0byBVcGRhdGVQcm9maWxlIHN0YW5kYXJkIGZpZWxkcyArIEF0dHJpYnV0ZXMgbWFwLiAqL1xuZXhwb3J0IHR5cGUgUHJvZmlsZVVwZGF0ZSA9IHtcbiAgZW1haWxBZGRyZXNzPzogc3RyaW5nO1xuICBmaXJzdE5hbWU/OiBzdHJpbmc7XG4gIGxhc3ROYW1lPzogc3RyaW5nO1xuICBhZGRyZXNzPzogUHJvZmlsZUFkZHJlc3M7XG4gIGF0dHJpYnV0ZXM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz47XG59O1xuXG4vKipcbiAqIEZsYXR0ZW4gYSBtdWx0aS12YWx1ZSBhdHRyaWJ1dGUgaW50byBhIHNpbmdsZSBzdHJpbmcgdGhhdCBmaXRzIHdpdGhpbiB0aGVcbiAqIEN1c3RvbWVyIFByb2ZpbGVzIGF0dHJpYnV0ZS12YWx1ZSBsaW1pdC5cbiAqXG4gKiAtIFplcm8gdmFsdWVzIC0+IGVtcHR5IHN0cmluZy5cbiAqIC0gRXhhY3RseSBvbmUgdmFsdWUgLT4gdGhhdCBlbGVtZW50IHZlcmJhdGltLlxuICogLSBNb3JlIHRoYW4gb25lIHZhbHVlIC0+IEpTT04uc3RyaW5naWZ5IG9mIHRoZSBhcnJheSAocHJlc2VydmVzIGFsbCB2YWx1ZXMpLlxuICogVGhlIHJlc3VsdCBpcyB0cnVuY2F0ZWQgdG8ge0BsaW5rIE1BWF9BVFRSSUJVVEVfTEVOR1RIfSBjaGFyYWN0ZXJzLlxuICovXG5leHBvcnQgY29uc3QgZmxhdHRlbiA9ICh2YWx1ZXM6IHN0cmluZ1tdKTogc3RyaW5nID0+IHtcbiAgbGV0IG91dDogc3RyaW5nO1xuICBpZiAoIXZhbHVlcyB8fCB2YWx1ZXMubGVuZ3RoID09PSAwKSB7XG4gICAgb3V0ID0gJyc7XG4gIH0gZWxzZSBpZiAodmFsdWVzLmxlbmd0aCA9PT0gMSkge1xuICAgIG91dCA9IHZhbHVlc1swXSA/PyAnJztcbiAgfSBlbHNlIHtcbiAgICBvdXQgPSBKU09OLnN0cmluZ2lmeSh2YWx1ZXMpO1xuICB9XG4gIHJldHVybiBvdXQubGVuZ3RoID4gTUFYX0FUVFJJQlVURV9MRU5HVEhcbiAgICA/IG91dC5zbGljZSgwLCBNQVhfQVRUUklCVVRFX0xFTkdUSClcbiAgICA6IG91dDtcbn07XG5cbi8qKlxuICogU2VyaWFsaXplIGEgbWFwIG9mIG11bHRpLXZhbHVlIGF0dHJpYnV0ZXMgaW50byBhIHNpbmdsZSBKU09OIHN0cmluZy5cbiAqXG4gKiBDdXN0b21lciBQcm9maWxlcyByZXF1aXJlcyBzdGF0aWNhbGx5IGRlY2xhcmVkIGZpZWxkIG1hcHBpbmdzLCBzbyBkeW5hbWljIGtleVxuICogc3BhY2VzIChjdXN0b21Qcm9wZXJ0aWVzIC8gdXNlckF0dHJpYnV0ZXMpIGFyZSBjb2xsYXBzZWQgaW50byBvbmUgSlNPTlxuICogYXR0cmlidXRlLiBFYWNoIHZhbHVlIGlzIGZsYXR0ZW5lZCBwZXIgdGhlIHNpbmdsZS1zdHJpbmcgcnVsZSBmaXJzdC5cbiAqL1xuY29uc3Qgc2VyaWFsaXplTXVsdGlWYWx1ZU1hcCA9IChcbiAgbWFwOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmdbXT4gfCB1bmRlZmluZWQsXG4pOiBzdHJpbmcgfCB1bmRlZmluZWQgPT4ge1xuICBpZiAoIW1hcCkge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbiAgY29uc3Qga2V5cyA9IE9iamVjdC5rZXlzKG1hcCk7XG4gIGlmIChrZXlzLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbiAgY29uc3QgZmxhdHRlbmVkOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG4gIGZvciAoY29uc3Qga2V5IG9mIGtleXMpIHtcbiAgICBmbGF0dGVuZWRba2V5XSA9IGZsYXR0ZW4obWFwW2tleV0pO1xuICB9XG4gIGNvbnN0IG91dCA9IEpTT04uc3RyaW5naWZ5KGZsYXR0ZW5lZCk7XG4gIHJldHVybiBvdXQubGVuZ3RoID4gTUFYX0FUVFJJQlVURV9MRU5HVEhcbiAgICA/IG91dC5zbGljZSgwLCBNQVhfQVRUUklCVVRFX0xFTkdUSClcbiAgICA6IG91dDtcbn07XG5cbmNvbnN0IHNwbGl0TmFtZSA9IChuYW1lOiBzdHJpbmcpOiB7IGZpcnN0TmFtZTogc3RyaW5nOyBsYXN0TmFtZT86IHN0cmluZyB9ID0+IHtcbiAgY29uc3QgdHJpbW1lZCA9IG5hbWUudHJpbSgpO1xuICBjb25zdCBpZHggPSB0cmltbWVkLmluZGV4T2YoJyAnKTtcbiAgaWYgKGlkeCA9PT0gLTEpIHtcbiAgICByZXR1cm4geyBmaXJzdE5hbWU6IHRyaW1tZWQgfTtcbiAgfVxuICByZXR1cm4ge1xuICAgIGZpcnN0TmFtZTogdHJpbW1lZC5zbGljZSgwLCBpZHgpLFxuICAgIGxhc3ROYW1lOiB0cmltbWVkLnNsaWNlKGlkeCArIDEpLnRyaW0oKSB8fCB1bmRlZmluZWQsXG4gIH07XG59O1xuXG4vKiogRHJvcCBrZXlzIHdob3NlIHZhbHVlIGlzIHVuZGVmaW5lZC9udWxsL2VtcHR5LiAqL1xuY29uc3QgY29tcGFjdCA9IDxUIGV4dGVuZHMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4+KG9iajogVCk6IFBhcnRpYWw8VD4gPT4ge1xuICBjb25zdCBvdXQ6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0ge307XG4gIGZvciAoY29uc3QgW2ssIHZdIG9mIE9iamVjdC5lbnRyaWVzKG9iaikpIHtcbiAgICBpZiAodiAhPT0gdW5kZWZpbmVkICYmIHYgIT09IG51bGwgJiYgdiAhPT0gJycpIHtcbiAgICAgIG91dFtrXSA9IHY7XG4gICAgfVxuICB9XG4gIHJldHVybiBvdXQgYXMgUGFydGlhbDxUPjtcbn07XG5cbi8qKiBXaGljaCBwcm9tb3RlZCB0YXJnZXRpbmcgYXR0cmlidXRlIGEgY2hhbm5lbCB0eXBlIGFjdGl2YXRlcy4gKi9cbmV4cG9ydCBjb25zdCB0YXJnZXRpbmdGbGFnc0ZvckNoYW5uZWwgPSAoXG4gIGNoYW5uZWxUeXBlOiBDaGFubmVsVHlwZSB8IHVuZGVmaW5lZCxcbik6IHsgaGFzR0NNPzogc3RyaW5nOyBoYXNBUE5TPzogc3RyaW5nIH0gPT4ge1xuICBzd2l0Y2ggKGNoYW5uZWxUeXBlKSB7XG4gICAgY2FzZSAnR0NNJzpcbiAgICAgIHJldHVybiB7IGhhc0dDTTogJ3RydWUnIH07XG4gICAgY2FzZSAnQVBOUyc6XG4gICAgY2FzZSAnQVBOU19TQU5EQk9YJzpcbiAgICAgIHJldHVybiB7IGhhc0FQTlM6ICd0cnVlJyB9O1xuICAgIGRlZmF1bHQ6XG4gICAgICByZXR1cm4ge307XG4gIH1cbn07XG5cbi8qKiBUcnVlIHdoZW4gdGhlIHJlcXVlc3QgY2FycmllcyBlbm91Z2ggZGF0YSB0byByZWdpc3Rlci91cGRhdGUgYSBkZXZpY2UuICovXG5leHBvcnQgY29uc3QgaGFzRGV2aWNlRGF0YSA9IChyZXE6IElkZW50aWZ5VXNlclJlcXVlc3QpOiBib29sZWFuID0+XG4gICEhcmVxLm9wdGlvbnM/LmRldmljZUlkO1xuXG4vKipcbiAqIEJ1aWxkIHRoZSBVcGRhdGVQcm9maWxlIHBheWxvYWQgKHN0YW5kYXJkIGZpZWxkcyArIEF0dHJpYnV0ZXMgbWFwKSBmb3IgdGhlXG4gKiBwcm9maWxlIGlkZW50aWZpZWQgYnkgdGhlIHZlcmlmaWVkIHtAbGluayBQcmluY2lwYWx9IChhdXRoZWQgYHN1YmAgb3IgZ3Vlc3RcbiAqIGBjb2duaXRvSWRlbnRpdHlJZGApLlxuICpcbiAqIFNFQ1VSSVRZOiBgcHJpbmNpcGFsLnZhbHVlYCBjb21lcyBmcm9tIGFuIGF1dGhvcml6ZXItdmVyaWZpZWQgc291cmNlIChKV1RcbiAqIGNsYWltcyBvciB0aGUgSUFNL0NvZ25pdG8gaWRlbnRpdHkpLiBUaGUgY2xpZW50LXN1cHBsaWVkIGB1c2VySWRgIGlzIHN0b3JlZFxuICogb25seSBhcyB0aGUgYGFwcFVzZXJJZGAgYXR0cmlidXRlIOKAlCBuZXZlciB0aGUgaWRlbnRpdHkuXG4gKi9cbmV4cG9ydCBjb25zdCBidWlsZFByb2ZpbGVVcGRhdGUgPSAoXG4gIHByaW5jaXBhbDogUHJpbmNpcGFsLFxuICByZXE6IElkZW50aWZ5VXNlclJlcXVlc3QsXG4pOiBQcm9maWxlVXBkYXRlID0+IHtcbiAgY29uc3QgcHJvZmlsZSA9IHJlcS51c2VyUHJvZmlsZSA/PyB7fTtcbiAgY29uc3QgZGVtb2dyYXBoaWMgPSBwcm9maWxlLmRlbW9ncmFwaGljO1xuICBjb25zdCBsb2NhdGlvbiA9IHByb2ZpbGUubG9jYXRpb247XG4gIGNvbnN0IG5hbWUgPSBwcm9maWxlLm5hbWUgPyBzcGxpdE5hbWUocHJvZmlsZS5uYW1lKSA6IHVuZGVmaW5lZDtcbiAgY29uc3QgZmxhZ3MgPSB0YXJnZXRpbmdGbGFnc0ZvckNoYW5uZWwocmVxLm9wdGlvbnM/LmNoYW5uZWxUeXBlKTtcblxuICBjb25zdCBhdHRyaWJ1dGVzID0gY29tcGFjdCh7XG4gICAgLy8gSWRlbnRpdHkgdHJhaWwgKHNlYXJjaGFibGUga2V5IGlzIHNlcGFyYXRlOyB0aGlzIGlzIGZvciByZWZlcmVuY2Ugb25seSkuXG4gICAgW3ByaW5jaXBhbC5rZXlOYW1lXTogcHJpbmNpcGFsLnZhbHVlLFxuICAgIGFwcFVzZXJJZDogcmVxLnVzZXJJZCxcblxuICAgIC8vIFRhcmdldGluZyAvIGN1c3RvbSBhdHRyaWJ1dGVzIGZvciBDb25uZWN0IHNlZ21lbnRhdGlvbi5cbiAgICBwbGFuOiBwcm9maWxlLnBsYW4sXG4gICAgbG9jYWxlOiBkZW1vZ3JhcGhpYz8ubG9jYWxlLFxuICAgIHBsYXRmb3JtOiBkZW1vZ3JhcGhpYz8ucGxhdGZvcm0sXG4gICAgYXBwVmVyc2lvbjogZGVtb2dyYXBoaWM/LmFwcFZlcnNpb24sXG4gICAgbWFrZTogZGVtb2dyYXBoaWM/Lm1ha2UsXG4gICAgbW9kZWw6IGRlbW9ncmFwaGljPy5tb2RlbCxcbiAgICBtb2RlbFZlcnNpb246IGRlbW9ncmFwaGljPy5tb2RlbFZlcnNpb24sXG4gICAgcGxhdGZvcm1WZXJzaW9uOiBkZW1vZ3JhcGhpYz8ucGxhdGZvcm1WZXJzaW9uLFxuICAgIHRpbWV6b25lOiBkZW1vZ3JhcGhpYz8udGltZXpvbmUsXG4gICAgbGF0aXR1ZGU6XG4gICAgICBsb2NhdGlvbj8ubGF0aXR1ZGUgIT09IHVuZGVmaW5lZCA/IFN0cmluZyhsb2NhdGlvbi5sYXRpdHVkZSkgOiB1bmRlZmluZWQsXG4gICAgbG9uZ2l0dWRlOlxuICAgICAgbG9jYXRpb24/LmxvbmdpdHVkZSAhPT0gdW5kZWZpbmVkXG4gICAgICAgID8gU3RyaW5nKGxvY2F0aW9uLmxvbmdpdHVkZSlcbiAgICAgICAgOiB1bmRlZmluZWQsXG4gICAgY3VzdG9tUHJvcGVydGllczogc2VyaWFsaXplTXVsdGlWYWx1ZU1hcChwcm9maWxlLmN1c3RvbVByb3BlcnRpZXMpLFxuICAgIHVzZXJBdHRyaWJ1dGVzOiBzZXJpYWxpemVNdWx0aVZhbHVlTWFwKHJlcS5vcHRpb25zPy51c2VyQXR0cmlidXRlcyksXG4gICAgbWV0cmljczogcHJvZmlsZS5tZXRyaWNzID8gSlNPTi5zdHJpbmdpZnkocHJvZmlsZS5tZXRyaWNzKSA6IHVuZGVmaW5lZCxcblxuICAgIC8vIFByb21vdGVkIHB1c2gtY2FwYWJpbGl0eSBmbGFncy5cbiAgICBoYXNHQ006IGZsYWdzLmhhc0dDTSxcbiAgICBoYXNBUE5TOiBmbGFncy5oYXNBUE5TLFxuICB9KSBhcyBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xuXG4gIGNvbnN0IGFkZHJlc3MgPSBjb21wYWN0KHtcbiAgICBjaXR5OiBsb2NhdGlvbj8uY2l0eSxcbiAgICBjb3VudHJ5OiBsb2NhdGlvbj8uY291bnRyeSxcbiAgICBwb3N0YWxDb2RlOiBsb2NhdGlvbj8ucG9zdGFsQ29kZSxcbiAgICBwcm92aW5jZTogbG9jYXRpb24/LnJlZ2lvbixcbiAgfSkgYXMgUHJvZmlsZUFkZHJlc3M7XG5cbiAgcmV0dXJuIHtcbiAgICBlbWFpbEFkZHJlc3M6IHByb2ZpbGUuZW1haWwsXG4gICAgZmlyc3ROYW1lOiBuYW1lPy5maXJzdE5hbWUsXG4gICAgbGFzdE5hbWU6IG5hbWU/Lmxhc3ROYW1lLFxuICAgIGFkZHJlc3M6IE9iamVjdC5rZXlzKGFkZHJlc3MpLmxlbmd0aCA+IDAgPyBhZGRyZXNzIDogdW5kZWZpbmVkLFxuICAgIGF0dHJpYnV0ZXMsXG4gIH07XG59O1xuXG4vKipcbiAqIEJ1aWxkIHRoZSBgX3NvdXJjZS4qYCBwYXlsb2FkIGZvciB0aGUgQW1wbGlmeURldmljZSBvYmplY3QgdHlwZS5cbiAqXG4gKiBLZXllZCBVTklRVUVMWSBieSB0aGUgc3RhYmxlIGBkZXZpY2VJZGA7IGBkZXZpY2VUb2tlbmAgKG9wdGlvbnMuYWRkcmVzcykgaXMgYVxuICogbXV0YWJsZSBmaWVsZCBzbyB0b2tlbiByZWZyZXNoZXMgdXBzZXJ0IHRoZSBzYW1lIG9iamVjdCBpbiBwbGFjZS4gQ2FycmllcyB0aGVcbiAqIHZlcmlmaWVkIGlkZW50aXR5IGZpZWxkIChgY29nbml0b1N1YmAgZm9yIGF1dGhlZCwgYGNvZ25pdG9JZGVudGl0eUlkYCBmb3JcbiAqIGd1ZXN0KSBhcyB0aGUgUFJPRklMRS1yZXNvbHV0aW9uIGtleSBzbyB0aGUgZGV2aWNlIG1lcmdlcyBpbnRvIHRoZSBwcm9maWxlXG4gKiBib3VuZCB0byB0aGF0IHtAbGluayBQcmluY2lwYWx9LlxuICpcbiAqIGBwbGF0Zm9ybWAgLyBgYXBwVmVyc2lvbmAgY29tZSBmcm9tIHRoZSBvcHRpb25zLCBmYWxsaW5nIGJhY2sgdG8gdGhlXG4gKiBgdXNlclByb2ZpbGUuZGVtb2dyYXBoaWNgIGVxdWl2YWxlbnRzLiBgdXBkYXRlZEF0YCBpcyBzZXJ2ZXItc2V0IG9uIGV2ZXJ5XG4gKiB3cml0ZTsgYGNyZWF0ZWRBdGAgaXMgdGhlIElNTVVUQUJMRSBmaXJzdC1yZWdpc3RyYXRpb24gdGltZSDigJQgdGhlIGNhbGxlclxuICogcGFzc2VzIHRoZSB2YWx1ZSByZWFkIGJhY2sgZnJvbSB0aGUgZXhpc3RpbmcgZGV2aWNlIG9iamVjdCAoaWYgYW55KSBzbyBpdCBpc1xuICogcHJlc2VydmVkIGFjcm9zcyBQdXRQcm9maWxlT2JqZWN0IHJlcGxhY2VtZW50cywgb3RoZXJ3aXNlIGl0IGRlZmF1bHRzIHRvIG5vdy5cbiAqL1xuZXhwb3J0IGNvbnN0IGJ1aWxkRGV2aWNlT2JqZWN0ID0gKFxuICBwcmluY2lwYWw6IFByaW5jaXBhbCxcbiAgcmVxOiBJZGVudGlmeVVzZXJSZXF1ZXN0LFxuICBleGlzdGluZ0NyZWF0ZWRBdD86IHN0cmluZyxcbik6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPT4ge1xuICBjb25zdCBvcHRpb25zID0gcmVxLm9wdGlvbnMgPz8ge307XG4gIGNvbnN0IGRlbW9ncmFwaGljID0gcmVxLnVzZXJQcm9maWxlPy5kZW1vZ3JhcGhpYztcbiAgY29uc3QgcGxhdGZvcm0gPSBvcHRpb25zLnBsYXRmb3JtID8/IGRlbW9ncmFwaGljPy5wbGF0Zm9ybTtcbiAgY29uc3QgYXBwVmVyc2lvbiA9IG9wdGlvbnMuYXBwVmVyc2lvbiA/PyBkZW1vZ3JhcGhpYz8uYXBwVmVyc2lvbjtcbiAgY29uc3Qgbm93ID0gbmV3IERhdGUoKS50b0lTT1N0cmluZygpO1xuXG4gIHJldHVybiBjb21wYWN0KHtcbiAgICBbcHJpbmNpcGFsLm9iamVjdEZpZWxkXTogcHJpbmNpcGFsLnZhbHVlLFxuICAgIGRldmljZUlkOiBvcHRpb25zLmRldmljZUlkLFxuICAgIGRldmljZVRva2VuOiBvcHRpb25zLmFkZHJlc3MsXG4gICAgY2hhbm5lbFR5cGU6IG9wdGlvbnMuY2hhbm5lbFR5cGUsXG4gICAgcGxhdGZvcm0sXG4gICAgYXBwVmVyc2lvbixcbiAgICBjcmVhdGVkQXQ6IGV4aXN0aW5nQ3JlYXRlZEF0ID8/IG5vdyxcbiAgICB1cGRhdGVkQXQ6IG5vdyxcbiAgfSkgYXMgUmVjb3JkPHN0cmluZywgc3RyaW5nPjtcbn07XG4iXX0=
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { CustomerProfilesClient } from '@aws-sdk/client-customer-profiles';
|
|
2
|
+
export type MergeOutcome = {
|
|
3
|
+
/** `true` when a distinct guest profile was found and merged. */
|
|
4
|
+
merged: boolean;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Merge-on-sign-in: fold a prior GUEST profile (keyed by `cognitoIdentityKey =
|
|
8
|
+
* guestIdentityId`) into the resolved AUTHENTICATED profile.
|
|
9
|
+
*
|
|
10
|
+
* Uses {@link MergeProfilesCommand}, which atomically (server-side Lambda job):
|
|
11
|
+
* 1. moves all profileKeys from the guest profile to the authed profile,
|
|
12
|
+
* 2. moves all OBJECTS (the guest's AmplifyDevice objects) to the authed
|
|
13
|
+
* profile,
|
|
14
|
+
* 3. deletes the now-empty guest profile.
|
|
15
|
+
*
|
|
16
|
+
* This is the deterministic mechanic (vs AddProfileKey, which only adds a lookup
|
|
17
|
+
* key, does not carry device objects, and risks an ambiguous-match rejection
|
|
18
|
+
* when the guest key already resolves to a distinct profile).
|
|
19
|
+
*
|
|
20
|
+
* No-ops (returns `{merged:false}`) when there is no prior guest profile, or the
|
|
21
|
+
* guest identity already resolves to the SAME profile (idempotent re-runs).
|
|
22
|
+
* Best-effort: a merge failure is surfaced to the caller to log, but never
|
|
23
|
+
* corrupts the primary identify result.
|
|
24
|
+
*/
|
|
25
|
+
export declare const mergeGuestIntoAuthed: (profiles: CustomerProfilesClient, domainName: string, guestIdentityId: string, authedProfileId: string) => Promise<MergeOutcome>;
|
|
26
|
+
//# sourceMappingURL=merge_resolver.d.ts.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { MergeProfilesCommand, } from '@aws-sdk/client-customer-profiles';
|
|
2
|
+
import { withTransientRetry } from '../shared/retry.js';
|
|
3
|
+
import { findProfileId } from './profile_resolver.js';
|
|
4
|
+
import { guestPrincipal } from './principal.js';
|
|
5
|
+
/**
|
|
6
|
+
* Merge-on-sign-in: fold a prior GUEST profile (keyed by `cognitoIdentityKey =
|
|
7
|
+
* guestIdentityId`) into the resolved AUTHENTICATED profile.
|
|
8
|
+
*
|
|
9
|
+
* Uses {@link MergeProfilesCommand}, which atomically (server-side Lambda job):
|
|
10
|
+
* 1. moves all profileKeys from the guest profile to the authed profile,
|
|
11
|
+
* 2. moves all OBJECTS (the guest's AmplifyDevice objects) to the authed
|
|
12
|
+
* profile,
|
|
13
|
+
* 3. deletes the now-empty guest profile.
|
|
14
|
+
*
|
|
15
|
+
* This is the deterministic mechanic (vs AddProfileKey, which only adds a lookup
|
|
16
|
+
* key, does not carry device objects, and risks an ambiguous-match rejection
|
|
17
|
+
* when the guest key already resolves to a distinct profile).
|
|
18
|
+
*
|
|
19
|
+
* No-ops (returns `{merged:false}`) when there is no prior guest profile, or the
|
|
20
|
+
* guest identity already resolves to the SAME profile (idempotent re-runs).
|
|
21
|
+
* Best-effort: a merge failure is surfaced to the caller to log, but never
|
|
22
|
+
* corrupts the primary identify result.
|
|
23
|
+
*/
|
|
24
|
+
export const mergeGuestIntoAuthed = async (profiles, domainName, guestIdentityId, authedProfileId) => {
|
|
25
|
+
const guestProfileId = await findProfileId(profiles, domainName, guestPrincipal(guestIdentityId));
|
|
26
|
+
if (!guestProfileId || guestProfileId === authedProfileId) {
|
|
27
|
+
return { merged: false };
|
|
28
|
+
}
|
|
29
|
+
await withTransientRetry(() => profiles.send(new MergeProfilesCommand({
|
|
30
|
+
DomainName: domainName,
|
|
31
|
+
MainProfileId: authedProfileId,
|
|
32
|
+
ProfileIdsToBeMerged: [guestProfileId],
|
|
33
|
+
})));
|
|
34
|
+
return { merged: true };
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVyZ2VfcmVzb2x2ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGFtYmRhL2lkZW50aWZ5L21lcmdlX3Jlc29sdmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFFTCxvQkFBb0IsR0FDckIsTUFBTSxtQ0FBbUMsQ0FBQztBQUUzQyxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUN4RCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDdEQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBT2hEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FrQkc7QUFDSCxNQUFNLENBQUMsTUFBTSxvQkFBb0IsR0FBRyxLQUFLLEVBQ3ZDLFFBQWdDLEVBQ2hDLFVBQWtCLEVBQ2xCLGVBQXVCLEVBQ3ZCLGVBQXVCLEVBQ0EsRUFBRTtJQUN6QixNQUFNLGNBQWMsR0FBRyxNQUFNLGFBQWEsQ0FDeEMsUUFBUSxFQUNSLFVBQVUsRUFDVixjQUFjLENBQUMsZUFBZSxDQUFDLENBQ2hDLENBQUM7SUFFRixJQUFJLENBQUMsY0FBYyxJQUFJLGNBQWMsS0FBSyxlQUFlLEVBQUUsQ0FBQztRQUMxRCxPQUFPLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxDQUFDO0lBQzNCLENBQUM7SUFFRCxNQUFNLGtCQUFrQixDQUFDLEdBQUcsRUFBRSxDQUM1QixRQUFRLENBQUMsSUFBSSxDQUNYLElBQUksb0JBQW9CLENBQUM7UUFDdkIsVUFBVSxFQUFFLFVBQVU7UUFDdEIsYUFBYSxFQUFFLGVBQWU7UUFDOUIsb0JBQW9CLEVBQUUsQ0FBQyxjQUFjLENBQUM7S0FDdkMsQ0FBQyxDQUNILENBQ0YsQ0FBQztJQUVGLE9BQU8sRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLENBQUM7QUFDMUIsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQ3VzdG9tZXJQcm9maWxlc0NsaWVudCxcbiAgTWVyZ2VQcm9maWxlc0NvbW1hbmQsXG59IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1jdXN0b21lci1wcm9maWxlcyc7XG5cbmltcG9ydCB7IHdpdGhUcmFuc2llbnRSZXRyeSB9IGZyb20gJy4uL3NoYXJlZC9yZXRyeS5qcyc7XG5pbXBvcnQgeyBmaW5kUHJvZmlsZUlkIH0gZnJvbSAnLi9wcm9maWxlX3Jlc29sdmVyLmpzJztcbmltcG9ydCB7IGd1ZXN0UHJpbmNpcGFsIH0gZnJvbSAnLi9wcmluY2lwYWwuanMnO1xuXG5leHBvcnQgdHlwZSBNZXJnZU91dGNvbWUgPSB7XG4gIC8qKiBgdHJ1ZWAgd2hlbiBhIGRpc3RpbmN0IGd1ZXN0IHByb2ZpbGUgd2FzIGZvdW5kIGFuZCBtZXJnZWQuICovXG4gIG1lcmdlZDogYm9vbGVhbjtcbn07XG5cbi8qKlxuICogTWVyZ2Utb24tc2lnbi1pbjogZm9sZCBhIHByaW9yIEdVRVNUIHByb2ZpbGUgKGtleWVkIGJ5IGBjb2duaXRvSWRlbnRpdHlLZXkgPVxuICogZ3Vlc3RJZGVudGl0eUlkYCkgaW50byB0aGUgcmVzb2x2ZWQgQVVUSEVOVElDQVRFRCBwcm9maWxlLlxuICpcbiAqIFVzZXMge0BsaW5rIE1lcmdlUHJvZmlsZXNDb21tYW5kfSwgd2hpY2ggYXRvbWljYWxseSAoc2VydmVyLXNpZGUgTGFtYmRhIGpvYik6XG4gKiAgIDEuIG1vdmVzIGFsbCBwcm9maWxlS2V5cyBmcm9tIHRoZSBndWVzdCBwcm9maWxlIHRvIHRoZSBhdXRoZWQgcHJvZmlsZSxcbiAqICAgMi4gbW92ZXMgYWxsIE9CSkVDVFMgKHRoZSBndWVzdCdzIEFtcGxpZnlEZXZpY2Ugb2JqZWN0cykgdG8gdGhlIGF1dGhlZFxuICogICAgICBwcm9maWxlLFxuICogICAzLiBkZWxldGVzIHRoZSBub3ctZW1wdHkgZ3Vlc3QgcHJvZmlsZS5cbiAqXG4gKiBUaGlzIGlzIHRoZSBkZXRlcm1pbmlzdGljIG1lY2hhbmljICh2cyBBZGRQcm9maWxlS2V5LCB3aGljaCBvbmx5IGFkZHMgYSBsb29rdXBcbiAqIGtleSwgZG9lcyBub3QgY2FycnkgZGV2aWNlIG9iamVjdHMsIGFuZCByaXNrcyBhbiBhbWJpZ3VvdXMtbWF0Y2ggcmVqZWN0aW9uXG4gKiB3aGVuIHRoZSBndWVzdCBrZXkgYWxyZWFkeSByZXNvbHZlcyB0byBhIGRpc3RpbmN0IHByb2ZpbGUpLlxuICpcbiAqIE5vLW9wcyAocmV0dXJucyBge21lcmdlZDpmYWxzZX1gKSB3aGVuIHRoZXJlIGlzIG5vIHByaW9yIGd1ZXN0IHByb2ZpbGUsIG9yIHRoZVxuICogZ3Vlc3QgaWRlbnRpdHkgYWxyZWFkeSByZXNvbHZlcyB0byB0aGUgU0FNRSBwcm9maWxlIChpZGVtcG90ZW50IHJlLXJ1bnMpLlxuICogQmVzdC1lZmZvcnQ6IGEgbWVyZ2UgZmFpbHVyZSBpcyBzdXJmYWNlZCB0byB0aGUgY2FsbGVyIHRvIGxvZywgYnV0IG5ldmVyXG4gKiBjb3JydXB0cyB0aGUgcHJpbWFyeSBpZGVudGlmeSByZXN1bHQuXG4gKi9cbmV4cG9ydCBjb25zdCBtZXJnZUd1ZXN0SW50b0F1dGhlZCA9IGFzeW5jIChcbiAgcHJvZmlsZXM6IEN1c3RvbWVyUHJvZmlsZXNDbGllbnQsXG4gIGRvbWFpbk5hbWU6IHN0cmluZyxcbiAgZ3Vlc3RJZGVudGl0eUlkOiBzdHJpbmcsXG4gIGF1dGhlZFByb2ZpbGVJZDogc3RyaW5nLFxuKTogUHJvbWlzZTxNZXJnZU91dGNvbWU+ID0+IHtcbiAgY29uc3QgZ3Vlc3RQcm9maWxlSWQgPSBhd2FpdCBmaW5kUHJvZmlsZUlkKFxuICAgIHByb2ZpbGVzLFxuICAgIGRvbWFpbk5hbWUsXG4gICAgZ3Vlc3RQcmluY2lwYWwoZ3Vlc3RJZGVudGl0eUlkKSxcbiAgKTtcblxuICBpZiAoIWd1ZXN0UHJvZmlsZUlkIHx8IGd1ZXN0UHJvZmlsZUlkID09PSBhdXRoZWRQcm9maWxlSWQpIHtcbiAgICByZXR1cm4geyBtZXJnZWQ6IGZhbHNlIH07XG4gIH1cblxuICBhd2FpdCB3aXRoVHJhbnNpZW50UmV0cnkoKCkgPT5cbiAgICBwcm9maWxlcy5zZW5kKFxuICAgICAgbmV3IE1lcmdlUHJvZmlsZXNDb21tYW5kKHtcbiAgICAgICAgRG9tYWluTmFtZTogZG9tYWluTmFtZSxcbiAgICAgICAgTWFpblByb2ZpbGVJZDogYXV0aGVkUHJvZmlsZUlkLFxuICAgICAgICBQcm9maWxlSWRzVG9CZU1lcmdlZDogW2d1ZXN0UHJvZmlsZUlkXSxcbiAgICAgIH0pLFxuICAgICksXG4gICk7XG5cbiAgcmV0dXJuIHsgbWVyZ2VkOiB0cnVlIH07XG59O1xuIl19
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The resolved caller identity, abstracted over the two auth modes so the rest
|
|
3
|
+
* of the identify pipeline (profile find-or-create, device upsert, attribute
|
|
4
|
+
* write) is identity-agnostic:
|
|
5
|
+
*
|
|
6
|
+
* - `authed`: a verified Cognito user-pool `sub` (JWT authorizer). Keyed by
|
|
7
|
+
* `cognitoUserKey` (field `cognitoSub`) on the `AmplifyProfile` object type.
|
|
8
|
+
* - `guest`: a verified Cognito Identity Pool `cognitoIdentityId` for an
|
|
9
|
+
* UNAUTHENTICATED identity (IAM/SigV4 authorizer). Keyed by
|
|
10
|
+
* `cognitoIdentityKey` (field `cognitoIdentityId`) on the
|
|
11
|
+
* `AmplifyGuestProfile` object type.
|
|
12
|
+
*
|
|
13
|
+
* `keyName` is the searchable/resolution key on the object types; `objectField`
|
|
14
|
+
* is the field name the Lambda ingests via PutProfileObject (sourced into that
|
|
15
|
+
* key); `profileObjectType` is the object type the find-or-create ingests into
|
|
16
|
+
* (each mode has its OWN type because Customer Profiles requires exactly one
|
|
17
|
+
* UNIQUE key per object type, carried by every ingested object); `value` is the
|
|
18
|
+
* verified identity value. `value` is ALWAYS derived from an authorizer-verified
|
|
19
|
+
* source — never from the request body.
|
|
20
|
+
*/
|
|
21
|
+
export type Principal = {
|
|
22
|
+
readonly kind: 'authed' | 'guest';
|
|
23
|
+
readonly keyName: string;
|
|
24
|
+
readonly objectField: string;
|
|
25
|
+
readonly profileObjectType: string;
|
|
26
|
+
readonly value: string;
|
|
27
|
+
};
|
|
28
|
+
/** Build the authenticated principal from a verified Cognito user-pool `sub`. */
|
|
29
|
+
export declare const authedPrincipal: (sub: string) => Principal;
|
|
30
|
+
/** Build the guest principal from a verified Cognito Identity Pool identityId. */
|
|
31
|
+
export declare const guestPrincipal: (identityId: string) => Principal;
|
|
32
|
+
/**
|
|
33
|
+
* The minimal structural shape shared by the two Lambda-proxy event formats
|
|
34
|
+
* this handler serves:
|
|
35
|
+
* - HTTP API payload format 2.0 + JWT authorizer (authed route): the verified
|
|
36
|
+
* `sub` is at `requestContext.authorizer.jwt.claims.sub`.
|
|
37
|
+
* - HTTP API payload format 1.0 + IAM/SigV4 authorization (guest route): the
|
|
38
|
+
* verified Cognito Identity Pool identity is at `requestContext.identity`
|
|
39
|
+
* (`cognitoIdentityId` + `cognitoAuthenticationType`). NOTE: format 2.0 has
|
|
40
|
+
* NO `identity` block at all, which is exactly why the guest route MUST use
|
|
41
|
+
* payload format 1.0.
|
|
42
|
+
*/
|
|
43
|
+
export type IdentifyEvent = {
|
|
44
|
+
body?: string | null;
|
|
45
|
+
requestContext?: {
|
|
46
|
+
authorizer?: {
|
|
47
|
+
jwt?: {
|
|
48
|
+
claims?: Record<string, unknown>;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
identity?: {
|
|
52
|
+
cognitoIdentityId?: string | null;
|
|
53
|
+
cognitoAuthenticationType?: string | null;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Resolve the verified caller {@link Principal} from either event shape.
|
|
59
|
+
*
|
|
60
|
+
* SECURITY: identity is derived ONLY from authorizer-verified fields — the JWT
|
|
61
|
+
* `sub` claim (which API Gateway populates only after cryptographically
|
|
62
|
+
* verifying the Cognito token) or the SigV4/IAM-verified Cognito
|
|
63
|
+
* `cognitoIdentityId`. The request body can never influence it. A guest is
|
|
64
|
+
* accepted only when `cognitoAuthenticationType === 'unauthenticated'` AND a
|
|
65
|
+
* `cognitoIdentityId` is present. Returns `undefined` when neither is present.
|
|
66
|
+
*/
|
|
67
|
+
export declare const resolvePrincipal: (event: IdentifyEvent) => Principal | undefined;
|
|
68
|
+
//# sourceMappingURL=principal.d.ts.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { COGNITO_IDENTITY_FIELD, COGNITO_IDENTITY_KEY, COGNITO_SUB_FIELD, COGNITO_USER_KEY, OBJECT_TYPE_GUEST_PROFILE, OBJECT_TYPE_PROFILE, } from '../../constants.js';
|
|
2
|
+
/** Build the authenticated principal from a verified Cognito user-pool `sub`. */
|
|
3
|
+
export const authedPrincipal = (sub) => ({
|
|
4
|
+
kind: 'authed',
|
|
5
|
+
keyName: COGNITO_USER_KEY,
|
|
6
|
+
objectField: COGNITO_SUB_FIELD,
|
|
7
|
+
profileObjectType: OBJECT_TYPE_PROFILE,
|
|
8
|
+
value: sub,
|
|
9
|
+
});
|
|
10
|
+
/** Build the guest principal from a verified Cognito Identity Pool identityId. */
|
|
11
|
+
export const guestPrincipal = (identityId) => ({
|
|
12
|
+
kind: 'guest',
|
|
13
|
+
keyName: COGNITO_IDENTITY_KEY,
|
|
14
|
+
objectField: COGNITO_IDENTITY_FIELD,
|
|
15
|
+
profileObjectType: OBJECT_TYPE_GUEST_PROFILE,
|
|
16
|
+
value: identityId,
|
|
17
|
+
});
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the verified caller {@link Principal} from either event shape.
|
|
20
|
+
*
|
|
21
|
+
* SECURITY: identity is derived ONLY from authorizer-verified fields — the JWT
|
|
22
|
+
* `sub` claim (which API Gateway populates only after cryptographically
|
|
23
|
+
* verifying the Cognito token) or the SigV4/IAM-verified Cognito
|
|
24
|
+
* `cognitoIdentityId`. The request body can never influence it. A guest is
|
|
25
|
+
* accepted only when `cognitoAuthenticationType === 'unauthenticated'` AND a
|
|
26
|
+
* `cognitoIdentityId` is present. Returns `undefined` when neither is present.
|
|
27
|
+
*/
|
|
28
|
+
export const resolvePrincipal = (event) => {
|
|
29
|
+
const claims = event.requestContext?.authorizer?.jwt?.claims;
|
|
30
|
+
const sub = claims?.sub;
|
|
31
|
+
// SECURITY: the authed (JWT) principal takes unconditional precedence. In
|
|
32
|
+
// practice the two blocks are mutually exclusive by route/payload format (JWT
|
|
33
|
+
// claims on format 2.0, identity on format 1.0), so this ordering is a
|
|
34
|
+
// defense-in-depth guard against any future misconfiguration that surfaced
|
|
35
|
+
// both — a verified user is never downgraded to a guest.
|
|
36
|
+
if (typeof sub === 'string' && sub.length > 0) {
|
|
37
|
+
return authedPrincipal(sub);
|
|
38
|
+
}
|
|
39
|
+
const identity = event.requestContext?.identity;
|
|
40
|
+
const identityId = identity?.cognitoIdentityId;
|
|
41
|
+
if (identity?.cognitoAuthenticationType === 'unauthenticated' &&
|
|
42
|
+
typeof identityId === 'string' &&
|
|
43
|
+
identityId.length > 0) {
|
|
44
|
+
return guestPrincipal(identityId);
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJpbmNpcGFsLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2xhbWJkYS9pZGVudGlmeS9wcmluY2lwYWwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNMLHNCQUFzQixFQUN0QixvQkFBb0IsRUFDcEIsaUJBQWlCLEVBQ2pCLGdCQUFnQixFQUNoQix5QkFBeUIsRUFDekIsbUJBQW1CLEdBQ3BCLE1BQU0sb0JBQW9CLENBQUM7QUE4QjVCLGlGQUFpRjtBQUNqRixNQUFNLENBQUMsTUFBTSxlQUFlLEdBQUcsQ0FBQyxHQUFXLEVBQWEsRUFBRSxDQUFDLENBQUM7SUFDMUQsSUFBSSxFQUFFLFFBQVE7SUFDZCxPQUFPLEVBQUUsZ0JBQWdCO0lBQ3pCLFdBQVcsRUFBRSxpQkFBaUI7SUFDOUIsaUJBQWlCLEVBQUUsbUJBQW1CO0lBQ3RDLEtBQUssRUFBRSxHQUFHO0NBQ1gsQ0FBQyxDQUFDO0FBRUgsa0ZBQWtGO0FBQ2xGLE1BQU0sQ0FBQyxNQUFNLGNBQWMsR0FBRyxDQUFDLFVBQWtCLEVBQWEsRUFBRSxDQUFDLENBQUM7SUFDaEUsSUFBSSxFQUFFLE9BQU87SUFDYixPQUFPLEVBQUUsb0JBQW9CO0lBQzdCLFdBQVcsRUFBRSxzQkFBc0I7SUFDbkMsaUJBQWlCLEVBQUUseUJBQXlCO0lBQzVDLEtBQUssRUFBRSxVQUFVO0NBQ2xCLENBQUMsQ0FBQztBQXdCSDs7Ozs7Ozs7O0dBU0c7QUFDSCxNQUFNLENBQUMsTUFBTSxnQkFBZ0IsR0FBRyxDQUM5QixLQUFvQixFQUNHLEVBQUU7SUFDekIsTUFBTSxNQUFNLEdBQUcsS0FBSyxDQUFDLGNBQWMsRUFBRSxVQUFVLEVBQUUsR0FBRyxFQUFFLE1BQU0sQ0FBQztJQUM3RCxNQUFNLEdBQUcsR0FBRyxNQUFNLEVBQUUsR0FBRyxDQUFDO0lBQ3hCLDBFQUEwRTtJQUMxRSw4RUFBOEU7SUFDOUUsdUVBQXVFO0lBQ3ZFLDJFQUEyRTtJQUMzRSx5REFBeUQ7SUFDekQsSUFBSSxPQUFPLEdBQUcsS0FBSyxRQUFRLElBQUksR0FBRyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUM5QyxPQUFPLGVBQWUsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUM5QixDQUFDO0lBRUQsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLGNBQWMsRUFBRSxRQUFRLENBQUM7SUFDaEQsTUFBTSxVQUFVLEdBQUcsUUFBUSxFQUFFLGlCQUFpQixDQUFDO0lBQy9DLElBQ0UsUUFBUSxFQUFFLHlCQUF5QixLQUFLLGlCQUFpQjtRQUN6RCxPQUFPLFVBQVUsS0FBSyxRQUFRO1FBQzlCLFVBQVUsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUNyQixDQUFDO1FBQ0QsT0FBTyxjQUFjLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDcEMsQ0FBQztJQUVELE9BQU8sU0FBUyxDQUFDO0FBQ25CLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIENPR05JVE9fSURFTlRJVFlfRklFTEQsXG4gIENPR05JVE9fSURFTlRJVFlfS0VZLFxuICBDT0dOSVRPX1NVQl9GSUVMRCxcbiAgQ09HTklUT19VU0VSX0tFWSxcbiAgT0JKRUNUX1RZUEVfR1VFU1RfUFJPRklMRSxcbiAgT0JKRUNUX1RZUEVfUFJPRklMRSxcbn0gZnJvbSAnLi4vLi4vY29uc3RhbnRzLmpzJztcblxuLyoqXG4gKiBUaGUgcmVzb2x2ZWQgY2FsbGVyIGlkZW50aXR5LCBhYnN0cmFjdGVkIG92ZXIgdGhlIHR3byBhdXRoIG1vZGVzIHNvIHRoZSByZXN0XG4gKiBvZiB0aGUgaWRlbnRpZnkgcGlwZWxpbmUgKHByb2ZpbGUgZmluZC1vci1jcmVhdGUsIGRldmljZSB1cHNlcnQsIGF0dHJpYnV0ZVxuICogd3JpdGUpIGlzIGlkZW50aXR5LWFnbm9zdGljOlxuICpcbiAqICAgLSBgYXV0aGVkYDogYSB2ZXJpZmllZCBDb2duaXRvIHVzZXItcG9vbCBgc3ViYCAoSldUIGF1dGhvcml6ZXIpLiBLZXllZCBieVxuICogICAgIGBjb2duaXRvVXNlcktleWAgKGZpZWxkIGBjb2duaXRvU3ViYCkgb24gdGhlIGBBbXBsaWZ5UHJvZmlsZWAgb2JqZWN0IHR5cGUuXG4gKiAgIC0gYGd1ZXN0YDogYSB2ZXJpZmllZCBDb2duaXRvIElkZW50aXR5IFBvb2wgYGNvZ25pdG9JZGVudGl0eUlkYCBmb3IgYW5cbiAqICAgICBVTkFVVEhFTlRJQ0FURUQgaWRlbnRpdHkgKElBTS9TaWdWNCBhdXRob3JpemVyKS4gS2V5ZWQgYnlcbiAqICAgICBgY29nbml0b0lkZW50aXR5S2V5YCAoZmllbGQgYGNvZ25pdG9JZGVudGl0eUlkYCkgb24gdGhlXG4gKiAgICAgYEFtcGxpZnlHdWVzdFByb2ZpbGVgIG9iamVjdCB0eXBlLlxuICpcbiAqIGBrZXlOYW1lYCBpcyB0aGUgc2VhcmNoYWJsZS9yZXNvbHV0aW9uIGtleSBvbiB0aGUgb2JqZWN0IHR5cGVzOyBgb2JqZWN0RmllbGRgXG4gKiBpcyB0aGUgZmllbGQgbmFtZSB0aGUgTGFtYmRhIGluZ2VzdHMgdmlhIFB1dFByb2ZpbGVPYmplY3QgKHNvdXJjZWQgaW50byB0aGF0XG4gKiBrZXkpOyBgcHJvZmlsZU9iamVjdFR5cGVgIGlzIHRoZSBvYmplY3QgdHlwZSB0aGUgZmluZC1vci1jcmVhdGUgaW5nZXN0cyBpbnRvXG4gKiAoZWFjaCBtb2RlIGhhcyBpdHMgT1dOIHR5cGUgYmVjYXVzZSBDdXN0b21lciBQcm9maWxlcyByZXF1aXJlcyBleGFjdGx5IG9uZVxuICogVU5JUVVFIGtleSBwZXIgb2JqZWN0IHR5cGUsIGNhcnJpZWQgYnkgZXZlcnkgaW5nZXN0ZWQgb2JqZWN0KTsgYHZhbHVlYCBpcyB0aGVcbiAqIHZlcmlmaWVkIGlkZW50aXR5IHZhbHVlLiBgdmFsdWVgIGlzIEFMV0FZUyBkZXJpdmVkIGZyb20gYW4gYXV0aG9yaXplci12ZXJpZmllZFxuICogc291cmNlIOKAlCBuZXZlciBmcm9tIHRoZSByZXF1ZXN0IGJvZHkuXG4gKi9cbmV4cG9ydCB0eXBlIFByaW5jaXBhbCA9IHtcbiAgcmVhZG9ubHkga2luZDogJ2F1dGhlZCcgfCAnZ3Vlc3QnO1xuICByZWFkb25seSBrZXlOYW1lOiBzdHJpbmc7XG4gIHJlYWRvbmx5IG9iamVjdEZpZWxkOiBzdHJpbmc7XG4gIHJlYWRvbmx5IHByb2ZpbGVPYmplY3RUeXBlOiBzdHJpbmc7XG4gIHJlYWRvbmx5IHZhbHVlOiBzdHJpbmc7XG59O1xuXG4vKiogQnVpbGQgdGhlIGF1dGhlbnRpY2F0ZWQgcHJpbmNpcGFsIGZyb20gYSB2ZXJpZmllZCBDb2duaXRvIHVzZXItcG9vbCBgc3ViYC4gKi9cbmV4cG9ydCBjb25zdCBhdXRoZWRQcmluY2lwYWwgPSAoc3ViOiBzdHJpbmcpOiBQcmluY2lwYWwgPT4gKHtcbiAga2luZDogJ2F1dGhlZCcsXG4gIGtleU5hbWU6IENPR05JVE9fVVNFUl9LRVksXG4gIG9iamVjdEZpZWxkOiBDT0dOSVRPX1NVQl9GSUVMRCxcbiAgcHJvZmlsZU9iamVjdFR5cGU6IE9CSkVDVF9UWVBFX1BST0ZJTEUsXG4gIHZhbHVlOiBzdWIsXG59KTtcblxuLyoqIEJ1aWxkIHRoZSBndWVzdCBwcmluY2lwYWwgZnJvbSBhIHZlcmlmaWVkIENvZ25pdG8gSWRlbnRpdHkgUG9vbCBpZGVudGl0eUlkLiAqL1xuZXhwb3J0IGNvbnN0IGd1ZXN0UHJpbmNpcGFsID0gKGlkZW50aXR5SWQ6IHN0cmluZyk6IFByaW5jaXBhbCA9PiAoe1xuICBraW5kOiAnZ3Vlc3QnLFxuICBrZXlOYW1lOiBDT0dOSVRPX0lERU5USVRZX0tFWSxcbiAgb2JqZWN0RmllbGQ6IENPR05JVE9fSURFTlRJVFlfRklFTEQsXG4gIHByb2ZpbGVPYmplY3RUeXBlOiBPQkpFQ1RfVFlQRV9HVUVTVF9QUk9GSUxFLFxuICB2YWx1ZTogaWRlbnRpdHlJZCxcbn0pO1xuXG4vKipcbiAqIFRoZSBtaW5pbWFsIHN0cnVjdHVyYWwgc2hhcGUgc2hhcmVkIGJ5IHRoZSB0d28gTGFtYmRhLXByb3h5IGV2ZW50IGZvcm1hdHNcbiAqIHRoaXMgaGFuZGxlciBzZXJ2ZXM6XG4gKiAgIC0gSFRUUCBBUEkgcGF5bG9hZCBmb3JtYXQgMi4wICsgSldUIGF1dGhvcml6ZXIgKGF1dGhlZCByb3V0ZSk6IHRoZSB2ZXJpZmllZFxuICogICAgIGBzdWJgIGlzIGF0IGByZXF1ZXN0Q29udGV4dC5hdXRob3JpemVyLmp3dC5jbGFpbXMuc3ViYC5cbiAqICAgLSBIVFRQIEFQSSBwYXlsb2FkIGZvcm1hdCAxLjAgKyBJQU0vU2lnVjQgYXV0aG9yaXphdGlvbiAoZ3Vlc3Qgcm91dGUpOiB0aGVcbiAqICAgICB2ZXJpZmllZCBDb2duaXRvIElkZW50aXR5IFBvb2wgaWRlbnRpdHkgaXMgYXQgYHJlcXVlc3RDb250ZXh0LmlkZW50aXR5YFxuICogICAgIChgY29nbml0b0lkZW50aXR5SWRgICsgYGNvZ25pdG9BdXRoZW50aWNhdGlvblR5cGVgKS4gTk9URTogZm9ybWF0IDIuMCBoYXNcbiAqICAgICBOTyBgaWRlbnRpdHlgIGJsb2NrIGF0IGFsbCwgd2hpY2ggaXMgZXhhY3RseSB3aHkgdGhlIGd1ZXN0IHJvdXRlIE1VU1QgdXNlXG4gKiAgICAgcGF5bG9hZCBmb3JtYXQgMS4wLlxuICovXG5leHBvcnQgdHlwZSBJZGVudGlmeUV2ZW50ID0ge1xuICBib2R5Pzogc3RyaW5nIHwgbnVsbDtcbiAgcmVxdWVzdENvbnRleHQ/OiB7XG4gICAgYXV0aG9yaXplcj86IHsgand0PzogeyBjbGFpbXM/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB9IH07XG4gICAgaWRlbnRpdHk/OiB7XG4gICAgICBjb2duaXRvSWRlbnRpdHlJZD86IHN0cmluZyB8IG51bGw7XG4gICAgICBjb2duaXRvQXV0aGVudGljYXRpb25UeXBlPzogc3RyaW5nIHwgbnVsbDtcbiAgICB9O1xuICB9O1xufTtcblxuLyoqXG4gKiBSZXNvbHZlIHRoZSB2ZXJpZmllZCBjYWxsZXIge0BsaW5rIFByaW5jaXBhbH0gZnJvbSBlaXRoZXIgZXZlbnQgc2hhcGUuXG4gKlxuICogU0VDVVJJVFk6IGlkZW50aXR5IGlzIGRlcml2ZWQgT05MWSBmcm9tIGF1dGhvcml6ZXItdmVyaWZpZWQgZmllbGRzIOKAlCB0aGUgSldUXG4gKiBgc3ViYCBjbGFpbSAod2hpY2ggQVBJIEdhdGV3YXkgcG9wdWxhdGVzIG9ubHkgYWZ0ZXIgY3J5cHRvZ3JhcGhpY2FsbHlcbiAqIHZlcmlmeWluZyB0aGUgQ29nbml0byB0b2tlbikgb3IgdGhlIFNpZ1Y0L0lBTS12ZXJpZmllZCBDb2duaXRvXG4gKiBgY29nbml0b0lkZW50aXR5SWRgLiBUaGUgcmVxdWVzdCBib2R5IGNhbiBuZXZlciBpbmZsdWVuY2UgaXQuIEEgZ3Vlc3QgaXNcbiAqIGFjY2VwdGVkIG9ubHkgd2hlbiBgY29nbml0b0F1dGhlbnRpY2F0aW9uVHlwZSA9PT0gJ3VuYXV0aGVudGljYXRlZCdgIEFORCBhXG4gKiBgY29nbml0b0lkZW50aXR5SWRgIGlzIHByZXNlbnQuIFJldHVybnMgYHVuZGVmaW5lZGAgd2hlbiBuZWl0aGVyIGlzIHByZXNlbnQuXG4gKi9cbmV4cG9ydCBjb25zdCByZXNvbHZlUHJpbmNpcGFsID0gKFxuICBldmVudDogSWRlbnRpZnlFdmVudCxcbik6IFByaW5jaXBhbCB8IHVuZGVmaW5lZCA9PiB7XG4gIGNvbnN0IGNsYWltcyA9IGV2ZW50LnJlcXVlc3RDb250ZXh0Py5hdXRob3JpemVyPy5qd3Q/LmNsYWltcztcbiAgY29uc3Qgc3ViID0gY2xhaW1zPy5zdWI7XG4gIC8vIFNFQ1VSSVRZOiB0aGUgYXV0aGVkIChKV1QpIHByaW5jaXBhbCB0YWtlcyB1bmNvbmRpdGlvbmFsIHByZWNlZGVuY2UuIEluXG4gIC8vIHByYWN0aWNlIHRoZSB0d28gYmxvY2tzIGFyZSBtdXR1YWxseSBleGNsdXNpdmUgYnkgcm91dGUvcGF5bG9hZCBmb3JtYXQgKEpXVFxuICAvLyBjbGFpbXMgb24gZm9ybWF0IDIuMCwgaWRlbnRpdHkgb24gZm9ybWF0IDEuMCksIHNvIHRoaXMgb3JkZXJpbmcgaXMgYVxuICAvLyBkZWZlbnNlLWluLWRlcHRoIGd1YXJkIGFnYWluc3QgYW55IGZ1dHVyZSBtaXNjb25maWd1cmF0aW9uIHRoYXQgc3VyZmFjZWRcbiAgLy8gYm90aCDigJQgYSB2ZXJpZmllZCB1c2VyIGlzIG5ldmVyIGRvd25ncmFkZWQgdG8gYSBndWVzdC5cbiAgaWYgKHR5cGVvZiBzdWIgPT09ICdzdHJpbmcnICYmIHN1Yi5sZW5ndGggPiAwKSB7XG4gICAgcmV0dXJuIGF1dGhlZFByaW5jaXBhbChzdWIpO1xuICB9XG5cbiAgY29uc3QgaWRlbnRpdHkgPSBldmVudC5yZXF1ZXN0Q29udGV4dD8uaWRlbnRpdHk7XG4gIGNvbnN0IGlkZW50aXR5SWQgPSBpZGVudGl0eT8uY29nbml0b0lkZW50aXR5SWQ7XG4gIGlmIChcbiAgICBpZGVudGl0eT8uY29nbml0b0F1dGhlbnRpY2F0aW9uVHlwZSA9PT0gJ3VuYXV0aGVudGljYXRlZCcgJiZcbiAgICB0eXBlb2YgaWRlbnRpdHlJZCA9PT0gJ3N0cmluZycgJiZcbiAgICBpZGVudGl0eUlkLmxlbmd0aCA+IDBcbiAgKSB7XG4gICAgcmV0dXJuIGd1ZXN0UHJpbmNpcGFsKGlkZW50aXR5SWQpO1xuICB9XG5cbiAgcmV0dXJuIHVuZGVmaW5lZDtcbn07XG4iXX0=
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { CustomerProfilesClient } from '@aws-sdk/client-customer-profiles';
|
|
2
|
+
import { Principal } from './principal.js';
|
|
3
|
+
export type ResolvedProfile = {
|
|
4
|
+
profileId: string;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Resolve (or create) the Customer Profiles profile bound to a verified caller
|
|
8
|
+
* ({@link Principal} — an authed `sub` or a guest `cognitoIdentityId`) via a
|
|
9
|
+
* single idempotent PutProfileObject find-or-create against the AmplifyProfile
|
|
10
|
+
* object type (keyed PROFILE+UNIQUE by the principal's key), then read back the
|
|
11
|
+
* profileId via SearchProfiles (bounded poll for eventual consistency). The
|
|
12
|
+
* PutProfileObject is wrapped in {@link withTransientRetry} so transient
|
|
13
|
+
* throttling / concurrent-update errors are retried with backoff.
|
|
14
|
+
*
|
|
15
|
+
* Because Customer Profiles enforces find-or-create atomically server-side on
|
|
16
|
+
* the PROFILE+UNIQUE key, concurrent first-logins for the same identity
|
|
17
|
+
* converge on a single profile — no duplicates, no create race.
|
|
18
|
+
*
|
|
19
|
+
* This is the DELIBERATELY ISOLATED identity-resolution seam: callers depend
|
|
20
|
+
* only on the returned `profileId`, regardless of auth mode.
|
|
21
|
+
* @throws if a profileId cannot be resolved after the bounded poll.
|
|
22
|
+
*/
|
|
23
|
+
export declare const resolveOrCreateProfile: (profiles: CustomerProfilesClient, domainName: string, principal: Principal) => Promise<ResolvedProfile>;
|
|
24
|
+
/**
|
|
25
|
+
* Find the profileId bound to a principal WITHOUT creating one (single
|
|
26
|
+
* SearchProfiles, no poll). Returns `undefined` when no profile exists yet.
|
|
27
|
+
* Used by merge-on-sign-in to locate a prior guest profile.
|
|
28
|
+
*
|
|
29
|
+
* A single attempt (no retry poll, unlike {@link resolveOrCreateProfile}) is
|
|
30
|
+
* correct here: the guest profile, if any, was created on a PRIOR invocation —
|
|
31
|
+
* not in this request — so there is no just-written object to wait for eventual
|
|
32
|
+
* consistency on. A miss means "no guest profile", not "not yet visible".
|
|
33
|
+
*/
|
|
34
|
+
export declare const findProfileId: (profiles: CustomerProfilesClient, domainName: string, principal: Principal) => Promise<string | undefined>;
|
|
35
|
+
//# sourceMappingURL=profile_resolver.d.ts.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { PutProfileObjectCommand, SearchProfilesCommand, } from '@aws-sdk/client-customer-profiles';
|
|
2
|
+
import { withTransientRetry } from '../shared/retry.js';
|
|
3
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
4
|
+
/**
|
|
5
|
+
* Look up the profile bound to `value` via the searchable `keyName`, polling
|
|
6
|
+
* briefly to absorb SearchProfiles' eventual consistency.
|
|
7
|
+
*/
|
|
8
|
+
const searchProfileId = async (profiles, domainName, keyName, value, attempts, baseDelayMs) => {
|
|
9
|
+
for (let i = 0; i < attempts; i++) {
|
|
10
|
+
const search = await profiles.send(new SearchProfilesCommand({
|
|
11
|
+
DomainName: domainName,
|
|
12
|
+
KeyName: keyName,
|
|
13
|
+
Values: [value],
|
|
14
|
+
}));
|
|
15
|
+
const found = search.Items?.find((item) => !!item.ProfileId)?.ProfileId;
|
|
16
|
+
if (found) {
|
|
17
|
+
return found;
|
|
18
|
+
}
|
|
19
|
+
if (i < attempts - 1) {
|
|
20
|
+
await sleep(baseDelayMs * (i + 1));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Resolve (or create) the Customer Profiles profile bound to a verified caller
|
|
27
|
+
* ({@link Principal} — an authed `sub` or a guest `cognitoIdentityId`) via a
|
|
28
|
+
* single idempotent PutProfileObject find-or-create against the AmplifyProfile
|
|
29
|
+
* object type (keyed PROFILE+UNIQUE by the principal's key), then read back the
|
|
30
|
+
* profileId via SearchProfiles (bounded poll for eventual consistency). The
|
|
31
|
+
* PutProfileObject is wrapped in {@link withTransientRetry} so transient
|
|
32
|
+
* throttling / concurrent-update errors are retried with backoff.
|
|
33
|
+
*
|
|
34
|
+
* Because Customer Profiles enforces find-or-create atomically server-side on
|
|
35
|
+
* the PROFILE+UNIQUE key, concurrent first-logins for the same identity
|
|
36
|
+
* converge on a single profile — no duplicates, no create race.
|
|
37
|
+
*
|
|
38
|
+
* This is the DELIBERATELY ISOLATED identity-resolution seam: callers depend
|
|
39
|
+
* only on the returned `profileId`, regardless of auth mode.
|
|
40
|
+
* @throws if a profileId cannot be resolved after the bounded poll.
|
|
41
|
+
*/
|
|
42
|
+
export const resolveOrCreateProfile = async (profiles, domainName, principal) => {
|
|
43
|
+
await withTransientRetry(() => profiles.send(new PutProfileObjectCommand({
|
|
44
|
+
DomainName: domainName,
|
|
45
|
+
ObjectTypeName: principal.profileObjectType,
|
|
46
|
+
Object: JSON.stringify({ [principal.objectField]: principal.value }),
|
|
47
|
+
})));
|
|
48
|
+
const profileId = await searchProfileId(profiles, domainName, principal.keyName, principal.value, 8, 150);
|
|
49
|
+
if (!profileId) {
|
|
50
|
+
throw new Error('PutProfileObject find-or-create did not resolve a ProfileId');
|
|
51
|
+
}
|
|
52
|
+
return { profileId };
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Find the profileId bound to a principal WITHOUT creating one (single
|
|
56
|
+
* SearchProfiles, no poll). Returns `undefined` when no profile exists yet.
|
|
57
|
+
* Used by merge-on-sign-in to locate a prior guest profile.
|
|
58
|
+
*
|
|
59
|
+
* A single attempt (no retry poll, unlike {@link resolveOrCreateProfile}) is
|
|
60
|
+
* correct here: the guest profile, if any, was created on a PRIOR invocation —
|
|
61
|
+
* not in this request — so there is no just-written object to wait for eventual
|
|
62
|
+
* consistency on. A miss means "no guest profile", not "not yet visible".
|
|
63
|
+
*/
|
|
64
|
+
export const findProfileId = async (profiles, domainName, principal) => searchProfileId(profiles, domainName, principal.keyName, principal.value, 1, 0);
|
|
65
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvZmlsZV9yZXNvbHZlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9sYW1iZGEvaWRlbnRpZnkvcHJvZmlsZV9yZXNvbHZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBRUwsdUJBQXVCLEVBQ3ZCLHFCQUFxQixHQUN0QixNQUFNLG1DQUFtQyxDQUFDO0FBRTNDLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBT3hELE1BQU0sS0FBSyxHQUFHLENBQUMsRUFBVSxFQUFpQixFQUFFLENBQzFDLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxVQUFVLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFFcEQ7OztHQUdHO0FBQ0gsTUFBTSxlQUFlLEdBQUcsS0FBSyxFQUMzQixRQUFnQyxFQUNoQyxVQUFrQixFQUNsQixPQUFlLEVBQ2YsS0FBYSxFQUNiLFFBQWdCLEVBQ2hCLFdBQW1CLEVBQ1UsRUFBRTtJQUMvQixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsUUFBUSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDbEMsTUFBTSxNQUFNLEdBQUcsTUFBTSxRQUFRLENBQUMsSUFBSSxDQUNoQyxJQUFJLHFCQUFxQixDQUFDO1lBQ3hCLFVBQVUsRUFBRSxVQUFVO1lBQ3RCLE9BQU8sRUFBRSxPQUFPO1lBQ2hCLE1BQU0sRUFBRSxDQUFDLEtBQUssQ0FBQztTQUNoQixDQUFDLENBQ0gsQ0FBQztRQUNGLE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLFNBQVMsQ0FBQztRQUN4RSxJQUFJLEtBQUssRUFBRSxDQUFDO1lBQ1YsT0FBTyxLQUFLLENBQUM7UUFDZixDQUFDO1FBQ0QsSUFBSSxDQUFDLEdBQUcsUUFBUSxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3JCLE1BQU0sS0FBSyxDQUFDLFdBQVcsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3JDLENBQUM7SUFDSCxDQUFDO0lBQ0QsT0FBTyxTQUFTLENBQUM7QUFDbkIsQ0FBQyxDQUFDO0FBRUY7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQkc7QUFDSCxNQUFNLENBQUMsTUFBTSxzQkFBc0IsR0FBRyxLQUFLLEVBQ3pDLFFBQWdDLEVBQ2hDLFVBQWtCLEVBQ2xCLFNBQW9CLEVBQ00sRUFBRTtJQUM1QixNQUFNLGtCQUFrQixDQUFDLEdBQUcsRUFBRSxDQUM1QixRQUFRLENBQUMsSUFBSSxDQUNYLElBQUksdUJBQXVCLENBQUM7UUFDMUIsVUFBVSxFQUFFLFVBQVU7UUFDdEIsY0FBYyxFQUFFLFNBQVMsQ0FBQyxpQkFBaUI7UUFDM0MsTUFBTSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDLFNBQVMsQ0FBQyxXQUFXLENBQUMsRUFBRSxTQUFTLENBQUMsS0FBSyxFQUFFLENBQUM7S0FDckUsQ0FBQyxDQUNILENBQ0YsQ0FBQztJQUVGLE1BQU0sU0FBUyxHQUFHLE1BQU0sZUFBZSxDQUNyQyxRQUFRLEVBQ1IsVUFBVSxFQUNWLFNBQVMsQ0FBQyxPQUFPLEVBQ2pCLFNBQVMsQ0FBQyxLQUFLLEVBQ2YsQ0FBQyxFQUNELEdBQUcsQ0FDSixDQUFDO0lBQ0YsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQ2YsTUFBTSxJQUFJLEtBQUssQ0FDYiw2REFBNkQsQ0FDOUQsQ0FBQztJQUNKLENBQUM7SUFDRCxPQUFPLEVBQUUsU0FBUyxFQUFFLENBQUM7QUFDdkIsQ0FBQyxDQUFDO0FBRUY7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sYUFBYSxHQUFHLEtBQUssRUFDaEMsUUFBZ0MsRUFDaEMsVUFBa0IsRUFDbEIsU0FBb0IsRUFDUyxFQUFFLENBQy9CLGVBQWUsQ0FDYixRQUFRLEVBQ1IsVUFBVSxFQUNWLFNBQVMsQ0FBQyxPQUFPLEVBQ2pCLFNBQVMsQ0FBQyxLQUFLLEVBQ2YsQ0FBQyxFQUNELENBQUMsQ0FDRixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQ3VzdG9tZXJQcm9maWxlc0NsaWVudCxcbiAgUHV0UHJvZmlsZU9iamVjdENvbW1hbmQsXG4gIFNlYXJjaFByb2ZpbGVzQ29tbWFuZCxcbn0gZnJvbSAnQGF3cy1zZGsvY2xpZW50LWN1c3RvbWVyLXByb2ZpbGVzJztcblxuaW1wb3J0IHsgd2l0aFRyYW5zaWVudFJldHJ5IH0gZnJvbSAnLi4vc2hhcmVkL3JldHJ5LmpzJztcbmltcG9ydCB7IFByaW5jaXBhbCB9IGZyb20gJy4vcHJpbmNpcGFsLmpzJztcblxuZXhwb3J0IHR5cGUgUmVzb2x2ZWRQcm9maWxlID0ge1xuICBwcm9maWxlSWQ6IHN0cmluZztcbn07XG5cbmNvbnN0IHNsZWVwID0gKG1zOiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+ID0+XG4gIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIG1zKSk7XG5cbi8qKlxuICogTG9vayB1cCB0aGUgcHJvZmlsZSBib3VuZCB0byBgdmFsdWVgIHZpYSB0aGUgc2VhcmNoYWJsZSBga2V5TmFtZWAsIHBvbGxpbmdcbiAqIGJyaWVmbHkgdG8gYWJzb3JiIFNlYXJjaFByb2ZpbGVzJyBldmVudHVhbCBjb25zaXN0ZW5jeS5cbiAqL1xuY29uc3Qgc2VhcmNoUHJvZmlsZUlkID0gYXN5bmMgKFxuICBwcm9maWxlczogQ3VzdG9tZXJQcm9maWxlc0NsaWVudCxcbiAgZG9tYWluTmFtZTogc3RyaW5nLFxuICBrZXlOYW1lOiBzdHJpbmcsXG4gIHZhbHVlOiBzdHJpbmcsXG4gIGF0dGVtcHRzOiBudW1iZXIsXG4gIGJhc2VEZWxheU1zOiBudW1iZXIsXG4pOiBQcm9taXNlPHN0cmluZyB8IHVuZGVmaW5lZD4gPT4ge1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGF0dGVtcHRzOyBpKyspIHtcbiAgICBjb25zdCBzZWFyY2ggPSBhd2FpdCBwcm9maWxlcy5zZW5kKFxuICAgICAgbmV3IFNlYXJjaFByb2ZpbGVzQ29tbWFuZCh7XG4gICAgICAgIERvbWFpbk5hbWU6IGRvbWFpbk5hbWUsXG4gICAgICAgIEtleU5hbWU6IGtleU5hbWUsXG4gICAgICAgIFZhbHVlczogW3ZhbHVlXSxcbiAgICAgIH0pLFxuICAgICk7XG4gICAgY29uc3QgZm91bmQgPSBzZWFyY2guSXRlbXM/LmZpbmQoKGl0ZW0pID0+ICEhaXRlbS5Qcm9maWxlSWQpPy5Qcm9maWxlSWQ7XG4gICAgaWYgKGZvdW5kKSB7XG4gICAgICByZXR1cm4gZm91bmQ7XG4gICAgfVxuICAgIGlmIChpIDwgYXR0ZW1wdHMgLSAxKSB7XG4gICAgICBhd2FpdCBzbGVlcChiYXNlRGVsYXlNcyAqIChpICsgMSkpO1xuICAgIH1cbiAgfVxuICByZXR1cm4gdW5kZWZpbmVkO1xufTtcblxuLyoqXG4gKiBSZXNvbHZlIChvciBjcmVhdGUpIHRoZSBDdXN0b21lciBQcm9maWxlcyBwcm9maWxlIGJvdW5kIHRvIGEgdmVyaWZpZWQgY2FsbGVyXG4gKiAoe0BsaW5rIFByaW5jaXBhbH0g4oCUIGFuIGF1dGhlZCBgc3ViYCBvciBhIGd1ZXN0IGBjb2duaXRvSWRlbnRpdHlJZGApIHZpYSBhXG4gKiBzaW5nbGUgaWRlbXBvdGVudCBQdXRQcm9maWxlT2JqZWN0IGZpbmQtb3ItY3JlYXRlIGFnYWluc3QgdGhlIEFtcGxpZnlQcm9maWxlXG4gKiBvYmplY3QgdHlwZSAoa2V5ZWQgUFJPRklMRStVTklRVUUgYnkgdGhlIHByaW5jaXBhbCdzIGtleSksIHRoZW4gcmVhZCBiYWNrIHRoZVxuICogcHJvZmlsZUlkIHZpYSBTZWFyY2hQcm9maWxlcyAoYm91bmRlZCBwb2xsIGZvciBldmVudHVhbCBjb25zaXN0ZW5jeSkuIFRoZVxuICogUHV0UHJvZmlsZU9iamVjdCBpcyB3cmFwcGVkIGluIHtAbGluayB3aXRoVHJhbnNpZW50UmV0cnl9IHNvIHRyYW5zaWVudFxuICogdGhyb3R0bGluZyAvIGNvbmN1cnJlbnQtdXBkYXRlIGVycm9ycyBhcmUgcmV0cmllZCB3aXRoIGJhY2tvZmYuXG4gKlxuICogQmVjYXVzZSBDdXN0b21lciBQcm9maWxlcyBlbmZvcmNlcyBmaW5kLW9yLWNyZWF0ZSBhdG9taWNhbGx5IHNlcnZlci1zaWRlIG9uXG4gKiB0aGUgUFJPRklMRStVTklRVUUga2V5LCBjb25jdXJyZW50IGZpcnN0LWxvZ2lucyBmb3IgdGhlIHNhbWUgaWRlbnRpdHlcbiAqIGNvbnZlcmdlIG9uIGEgc2luZ2xlIHByb2ZpbGUg4oCUIG5vIGR1cGxpY2F0ZXMsIG5vIGNyZWF0ZSByYWNlLlxuICpcbiAqIFRoaXMgaXMgdGhlIERFTElCRVJBVEVMWSBJU09MQVRFRCBpZGVudGl0eS1yZXNvbHV0aW9uIHNlYW06IGNhbGxlcnMgZGVwZW5kXG4gKiBvbmx5IG9uIHRoZSByZXR1cm5lZCBgcHJvZmlsZUlkYCwgcmVnYXJkbGVzcyBvZiBhdXRoIG1vZGUuXG4gKiBAdGhyb3dzIGlmIGEgcHJvZmlsZUlkIGNhbm5vdCBiZSByZXNvbHZlZCBhZnRlciB0aGUgYm91bmRlZCBwb2xsLlxuICovXG5leHBvcnQgY29uc3QgcmVzb2x2ZU9yQ3JlYXRlUHJvZmlsZSA9IGFzeW5jIChcbiAgcHJvZmlsZXM6IEN1c3RvbWVyUHJvZmlsZXNDbGllbnQsXG4gIGRvbWFpbk5hbWU6IHN0cmluZyxcbiAgcHJpbmNpcGFsOiBQcmluY2lwYWwsXG4pOiBQcm9taXNlPFJlc29sdmVkUHJvZmlsZT4gPT4ge1xuICBhd2FpdCB3aXRoVHJhbnNpZW50UmV0cnkoKCkgPT5cbiAgICBwcm9maWxlcy5zZW5kKFxuICAgICAgbmV3IFB1dFByb2ZpbGVPYmplY3RDb21tYW5kKHtcbiAgICAgICAgRG9tYWluTmFtZTogZG9tYWluTmFtZSxcbiAgICAgICAgT2JqZWN0VHlwZU5hbWU6IHByaW5jaXBhbC5wcm9maWxlT2JqZWN0VHlwZSxcbiAgICAgICAgT2JqZWN0OiBKU09OLnN0cmluZ2lmeSh7IFtwcmluY2lwYWwub2JqZWN0RmllbGRdOiBwcmluY2lwYWwudmFsdWUgfSksXG4gICAgICB9KSxcbiAgICApLFxuICApO1xuXG4gIGNvbnN0IHByb2ZpbGVJZCA9IGF3YWl0IHNlYXJjaFByb2ZpbGVJZChcbiAgICBwcm9maWxlcyxcbiAgICBkb21haW5OYW1lLFxuICAgIHByaW5jaXBhbC5rZXlOYW1lLFxuICAgIHByaW5jaXBhbC52YWx1ZSxcbiAgICA4LFxuICAgIDE1MCxcbiAgKTtcbiAgaWYgKCFwcm9maWxlSWQpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAnUHV0UHJvZmlsZU9iamVjdCBmaW5kLW9yLWNyZWF0ZSBkaWQgbm90IHJlc29sdmUgYSBQcm9maWxlSWQnLFxuICAgICk7XG4gIH1cbiAgcmV0dXJuIHsgcHJvZmlsZUlkIH07XG59O1xuXG4vKipcbiAqIEZpbmQgdGhlIHByb2ZpbGVJZCBib3VuZCB0byBhIHByaW5jaXBhbCBXSVRIT1VUIGNyZWF0aW5nIG9uZSAoc2luZ2xlXG4gKiBTZWFyY2hQcm9maWxlcywgbm8gcG9sbCkuIFJldHVybnMgYHVuZGVmaW5lZGAgd2hlbiBubyBwcm9maWxlIGV4aXN0cyB5ZXQuXG4gKiBVc2VkIGJ5IG1lcmdlLW9uLXNpZ24taW4gdG8gbG9jYXRlIGEgcHJpb3IgZ3Vlc3QgcHJvZmlsZS5cbiAqXG4gKiBBIHNpbmdsZSBhdHRlbXB0IChubyByZXRyeSBwb2xsLCB1bmxpa2Uge0BsaW5rIHJlc29sdmVPckNyZWF0ZVByb2ZpbGV9KSBpc1xuICogY29ycmVjdCBoZXJlOiB0aGUgZ3Vlc3QgcHJvZmlsZSwgaWYgYW55LCB3YXMgY3JlYXRlZCBvbiBhIFBSSU9SIGludm9jYXRpb24g4oCUXG4gKiBub3QgaW4gdGhpcyByZXF1ZXN0IOKAlCBzbyB0aGVyZSBpcyBubyBqdXN0LXdyaXR0ZW4gb2JqZWN0IHRvIHdhaXQgZm9yIGV2ZW50dWFsXG4gKiBjb25zaXN0ZW5jeSBvbi4gQSBtaXNzIG1lYW5zIFwibm8gZ3Vlc3QgcHJvZmlsZVwiLCBub3QgXCJub3QgeWV0IHZpc2libGVcIi5cbiAqL1xuZXhwb3J0IGNvbnN0IGZpbmRQcm9maWxlSWQgPSBhc3luYyAoXG4gIHByb2ZpbGVzOiBDdXN0b21lclByb2ZpbGVzQ2xpZW50LFxuICBkb21haW5OYW1lOiBzdHJpbmcsXG4gIHByaW5jaXBhbDogUHJpbmNpcGFsLFxuKTogUHJvbWlzZTxzdHJpbmcgfCB1bmRlZmluZWQ+ID0+XG4gIHNlYXJjaFByb2ZpbGVJZChcbiAgICBwcm9maWxlcyxcbiAgICBkb21haW5OYW1lLFxuICAgIHByaW5jaXBhbC5rZXlOYW1lLFxuICAgIHByaW5jaXBhbC52YWx1ZSxcbiAgICAxLFxuICAgIDAsXG4gICk7XG4iXX0=
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frozen request/response contract for the identify-user backend.
|
|
3
|
+
*
|
|
4
|
+
* These types mirror exactly the JSON shape accepted by
|
|
5
|
+
* `POST {apiEndpoint}/identify-user`. They intentionally live in a single
|
|
6
|
+
* module so the handler, the mapping layer, and the tests share one source of
|
|
7
|
+
* truth.
|
|
8
|
+
*/
|
|
9
|
+
export type ChannelType = 'GCM' | 'APNS' | 'APNS_SANDBOX' | 'IN_APP';
|
|
10
|
+
export type OptOut = 'ALL' | 'NONE';
|
|
11
|
+
export type Demographic = {
|
|
12
|
+
appVersion?: string;
|
|
13
|
+
locale?: string;
|
|
14
|
+
make?: string;
|
|
15
|
+
model?: string;
|
|
16
|
+
modelVersion?: string;
|
|
17
|
+
platform?: string;
|
|
18
|
+
platformVersion?: string;
|
|
19
|
+
timezone?: string;
|
|
20
|
+
};
|
|
21
|
+
export type Location = {
|
|
22
|
+
city?: string;
|
|
23
|
+
country?: string;
|
|
24
|
+
latitude?: number;
|
|
25
|
+
longitude?: number;
|
|
26
|
+
postalCode?: string;
|
|
27
|
+
region?: string;
|
|
28
|
+
};
|
|
29
|
+
export type UserProfile = {
|
|
30
|
+
name?: string;
|
|
31
|
+
email?: string;
|
|
32
|
+
plan?: string;
|
|
33
|
+
customProperties?: Record<string, string[]>;
|
|
34
|
+
demographic?: Demographic;
|
|
35
|
+
location?: Location;
|
|
36
|
+
metrics?: Record<string, number>;
|
|
37
|
+
};
|
|
38
|
+
export type IdentifyUserOptions = {
|
|
39
|
+
userAttributes?: Record<string, string[]>;
|
|
40
|
+
/** Mutable push token / endpoint address. Stored as a FIELD, not the key. */
|
|
41
|
+
address?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Stable device identifier (e.g. the client-persisted endpointId). This is
|
|
44
|
+
* the UNIQUE key of the AmplifyDevice object, so a token refresh for the same
|
|
45
|
+
* device upserts in place instead of creating a new device object.
|
|
46
|
+
*/
|
|
47
|
+
deviceId?: string;
|
|
48
|
+
channelType?: ChannelType;
|
|
49
|
+
/**
|
|
50
|
+
* Client OS platform (e.g. `Android` / `iOS`) recorded on the device object.
|
|
51
|
+
* Falls back to `userProfile.demographic.platform` when omitted.
|
|
52
|
+
*/
|
|
53
|
+
platform?: string;
|
|
54
|
+
/**
|
|
55
|
+
* App version at device registration, recorded on the device object. Falls
|
|
56
|
+
* back to `userProfile.demographic.appVersion` when omitted.
|
|
57
|
+
*/
|
|
58
|
+
appVersion?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Accepted for API compatibility with the shared Pinpoint-style options, but
|
|
61
|
+
* has NO effect: it is not written to the device object nor the profile.
|
|
62
|
+
*/
|
|
63
|
+
optOut?: OptOut;
|
|
64
|
+
/**
|
|
65
|
+
* The AUTHENTICATED caller's prior GUEST
|
|
66
|
+
* `cognitoIdentityId`. When present on an authed call, the handler folds the
|
|
67
|
+
* guest profile (keyed by `cognitoIdentityKey = this value`) — and its device
|
|
68
|
+
* objects — into the resolved authed profile via MergeProfiles. Ignored on
|
|
69
|
+
* guest calls. The value is only a merge HINT: it can only trigger merging a
|
|
70
|
+
* guest profile into the caller's OWN verified authed profile.
|
|
71
|
+
*/
|
|
72
|
+
previousGuestIdentityId?: string;
|
|
73
|
+
};
|
|
74
|
+
export type IdentifyUserRequest = {
|
|
75
|
+
/**
|
|
76
|
+
* Client-supplied identifier. Stored ONLY as the `appUserId` attribute.
|
|
77
|
+
* It is NEVER used as the profile identity key — the authoritative identity
|
|
78
|
+
* is the verified Cognito `sub` from the JWT authorizer claims.
|
|
79
|
+
*/
|
|
80
|
+
userId?: string;
|
|
81
|
+
userProfile: UserProfile;
|
|
82
|
+
options?: IdentifyUserOptions;
|
|
83
|
+
};
|
|
84
|
+
export type SuccessResponse = {
|
|
85
|
+
status: 'ok';
|
|
86
|
+
};
|
|
87
|
+
export type ErrorResponse = {
|
|
88
|
+
error: string;
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frozen request/response contract for the identify-user backend.
|
|
3
|
+
*
|
|
4
|
+
* These types mirror exactly the JSON shape accepted by
|
|
5
|
+
* `POST {apiEndpoint}/identify-user`. They intentionally live in a single
|
|
6
|
+
* module so the handler, the mapping layer, and the tests share one source of
|
|
7
|
+
* truth.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGFtYmRhL2lkZW50aWZ5L3R5cGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7O0dBT0ciLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEZyb3plbiByZXF1ZXN0L3Jlc3BvbnNlIGNvbnRyYWN0IGZvciB0aGUgaWRlbnRpZnktdXNlciBiYWNrZW5kLlxuICpcbiAqIFRoZXNlIHR5cGVzIG1pcnJvciBleGFjdGx5IHRoZSBKU09OIHNoYXBlIGFjY2VwdGVkIGJ5XG4gKiBgUE9TVCB7YXBpRW5kcG9pbnR9L2lkZW50aWZ5LXVzZXJgLiBUaGV5IGludGVudGlvbmFsbHkgbGl2ZSBpbiBhIHNpbmdsZVxuICogbW9kdWxlIHNvIHRoZSBoYW5kbGVyLCB0aGUgbWFwcGluZyBsYXllciwgYW5kIHRoZSB0ZXN0cyBzaGFyZSBvbmUgc291cmNlIG9mXG4gKiB0cnV0aC5cbiAqL1xuXG5leHBvcnQgdHlwZSBDaGFubmVsVHlwZSA9ICdHQ00nIHwgJ0FQTlMnIHwgJ0FQTlNfU0FOREJPWCcgfCAnSU5fQVBQJztcbmV4cG9ydCB0eXBlIE9wdE91dCA9ICdBTEwnIHwgJ05PTkUnO1xuXG5leHBvcnQgdHlwZSBEZW1vZ3JhcGhpYyA9IHtcbiAgYXBwVmVyc2lvbj86IHN0cmluZztcbiAgbG9jYWxlPzogc3RyaW5nO1xuICBtYWtlPzogc3RyaW5nO1xuICBtb2RlbD86IHN0cmluZztcbiAgbW9kZWxWZXJzaW9uPzogc3RyaW5nO1xuICBwbGF0Zm9ybT86IHN0cmluZztcbiAgcGxhdGZvcm1WZXJzaW9uPzogc3RyaW5nO1xuICB0aW1lem9uZT86IHN0cmluZztcbn07XG5cbmV4cG9ydCB0eXBlIExvY2F0aW9uID0ge1xuICBjaXR5Pzogc3RyaW5nO1xuICBjb3VudHJ5Pzogc3RyaW5nO1xuICBsYXRpdHVkZT86IG51bWJlcjtcbiAgbG9uZ2l0dWRlPzogbnVtYmVyO1xuICBwb3N0YWxDb2RlPzogc3RyaW5nO1xuICByZWdpb24/OiBzdHJpbmc7XG59O1xuXG5leHBvcnQgdHlwZSBVc2VyUHJvZmlsZSA9IHtcbiAgbmFtZT86IHN0cmluZztcbiAgZW1haWw/OiBzdHJpbmc7XG4gIHBsYW4/OiBzdHJpbmc7XG4gIGN1c3RvbVByb3BlcnRpZXM/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmdbXT47XG4gIGRlbW9ncmFwaGljPzogRGVtb2dyYXBoaWM7XG4gIGxvY2F0aW9uPzogTG9jYXRpb247XG4gIG1ldHJpY3M/OiBSZWNvcmQ8c3RyaW5nLCBudW1iZXI+O1xufTtcblxuZXhwb3J0IHR5cGUgSWRlbnRpZnlVc2VyT3B0aW9ucyA9IHtcbiAgdXNlckF0dHJpYnV0ZXM/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmdbXT47XG4gIC8qKiBNdXRhYmxlIHB1c2ggdG9rZW4gLyBlbmRwb2ludCBhZGRyZXNzLiBTdG9yZWQgYXMgYSBGSUVMRCwgbm90IHRoZSBrZXkuICovXG4gIGFkZHJlc3M/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBTdGFibGUgZGV2aWNlIGlkZW50aWZpZXIgKGUuZy4gdGhlIGNsaWVudC1wZXJzaXN0ZWQgZW5kcG9pbnRJZCkuIFRoaXMgaXNcbiAgICogdGhlIFVOSVFVRSBrZXkgb2YgdGhlIEFtcGxpZnlEZXZpY2Ugb2JqZWN0LCBzbyBhIHRva2VuIHJlZnJlc2ggZm9yIHRoZSBzYW1lXG4gICAqIGRldmljZSB1cHNlcnRzIGluIHBsYWNlIGluc3RlYWQgb2YgY3JlYXRpbmcgYSBuZXcgZGV2aWNlIG9iamVjdC5cbiAgICovXG4gIGRldmljZUlkPzogc3RyaW5nO1xuICBjaGFubmVsVHlwZT86IENoYW5uZWxUeXBlO1xuICAvKipcbiAgICogQ2xpZW50IE9TIHBsYXRmb3JtIChlLmcuIGBBbmRyb2lkYCAvIGBpT1NgKSByZWNvcmRlZCBvbiB0aGUgZGV2aWNlIG9iamVjdC5cbiAgICogRmFsbHMgYmFjayB0byBgdXNlclByb2ZpbGUuZGVtb2dyYXBoaWMucGxhdGZvcm1gIHdoZW4gb21pdHRlZC5cbiAgICovXG4gIHBsYXRmb3JtPzogc3RyaW5nO1xuICAvKipcbiAgICogQXBwIHZlcnNpb24gYXQgZGV2aWNlIHJlZ2lzdHJhdGlvbiwgcmVjb3JkZWQgb24gdGhlIGRldmljZSBvYmplY3QuIEZhbGxzXG4gICAqIGJhY2sgdG8gYHVzZXJQcm9maWxlLmRlbW9ncmFwaGljLmFwcFZlcnNpb25gIHdoZW4gb21pdHRlZC5cbiAgICovXG4gIGFwcFZlcnNpb24/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBBY2NlcHRlZCBmb3IgQVBJIGNvbXBhdGliaWxpdHkgd2l0aCB0aGUgc2hhcmVkIFBpbnBvaW50LXN0eWxlIG9wdGlvbnMsIGJ1dFxuICAgKiBoYXMgTk8gZWZmZWN0OiBpdCBpcyBub3Qgd3JpdHRlbiB0byB0aGUgZGV2aWNlIG9iamVjdCBub3IgdGhlIHByb2ZpbGUuXG4gICAqL1xuICBvcHRPdXQ/OiBPcHRPdXQ7XG4gIC8qKlxuICAgKiBUaGUgQVVUSEVOVElDQVRFRCBjYWxsZXIncyBwcmlvciBHVUVTVFxuICAgKiBgY29nbml0b0lkZW50aXR5SWRgLiBXaGVuIHByZXNlbnQgb24gYW4gYXV0aGVkIGNhbGwsIHRoZSBoYW5kbGVyIGZvbGRzIHRoZVxuICAgKiBndWVzdCBwcm9maWxlIChrZXllZCBieSBgY29nbml0b0lkZW50aXR5S2V5ID0gdGhpcyB2YWx1ZWApIOKAlCBhbmQgaXRzIGRldmljZVxuICAgKiBvYmplY3RzIOKAlCBpbnRvIHRoZSByZXNvbHZlZCBhdXRoZWQgcHJvZmlsZSB2aWEgTWVyZ2VQcm9maWxlcy4gSWdub3JlZCBvblxuICAgKiBndWVzdCBjYWxscy4gVGhlIHZhbHVlIGlzIG9ubHkgYSBtZXJnZSBISU5UOiBpdCBjYW4gb25seSB0cmlnZ2VyIG1lcmdpbmcgYVxuICAgKiBndWVzdCBwcm9maWxlIGludG8gdGhlIGNhbGxlcidzIE9XTiB2ZXJpZmllZCBhdXRoZWQgcHJvZmlsZS5cbiAgICovXG4gIHByZXZpb3VzR3Vlc3RJZGVudGl0eUlkPzogc3RyaW5nO1xufTtcblxuZXhwb3J0IHR5cGUgSWRlbnRpZnlVc2VyUmVxdWVzdCA9IHtcbiAgLyoqXG4gICAqIENsaWVudC1zdXBwbGllZCBpZGVudGlmaWVyLiBTdG9yZWQgT05MWSBhcyB0aGUgYGFwcFVzZXJJZGAgYXR0cmlidXRlLlxuICAgKiBJdCBpcyBORVZFUiB1c2VkIGFzIHRoZSBwcm9maWxlIGlkZW50aXR5IGtleSDigJQgdGhlIGF1dGhvcml0YXRpdmUgaWRlbnRpdHlcbiAgICogaXMgdGhlIHZlcmlmaWVkIENvZ25pdG8gYHN1YmAgZnJvbSB0aGUgSldUIGF1dGhvcml6ZXIgY2xhaW1zLlxuICAgKi9cbiAgdXNlcklkPzogc3RyaW5nO1xuICB1c2VyUHJvZmlsZTogVXNlclByb2ZpbGU7XG4gIG9wdGlvbnM/OiBJZGVudGlmeVVzZXJPcHRpb25zO1xufTtcblxuZXhwb3J0IHR5cGUgU3VjY2Vzc1Jlc3BvbnNlID0ge1xuICBzdGF0dXM6ICdvayc7XG59O1xuXG5leHBvcnQgdHlwZSBFcnJvclJlc3BvbnNlID0ge1xuICBlcnJvcjogc3RyaW5nO1xufTtcbiJdfQ==
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { IdentifyUserRequest } from './types.js';
|
|
2
|
+
export type ValidationResult = {
|
|
3
|
+
ok: boolean;
|
|
4
|
+
error?: string;
|
|
5
|
+
value?: IdentifyUserRequest;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Validate the frozen identify-user request contract.
|
|
9
|
+
*
|
|
10
|
+
* Returns `{ ok: false, error }` on the first violation so the handler can map
|
|
11
|
+
* it to a 400. Identity is NOT validated here — the verified `sub` is supplied
|
|
12
|
+
* separately by the authorizer.
|
|
13
|
+
*/
|
|
14
|
+
export declare const validateBody: (body: unknown) => ValidationResult;
|
|
15
|
+
//# sourceMappingURL=validation.d.ts.map
|