[pull] main from aws-amplify:main - #681
Merged
Merged
Conversation
…r Push Notifications (#14866) * feat(notifications): add Connect Customer Profiles push provider as new subpath export Add an opt-in Push Notifications provider backed by Amazon Connect Customer Profiles, exposed as a new subpath export alongside the existing Pinpoint provider (mirroring the Analytics Customer Profiles provider from #14864): - aws-amplify/push-notifications/customer-profiles - @aws-amplify/notifications/push-notifications/customer-profiles Device-token registration (initializePushNotifications) and identifyUser register the device with Connect Customer Profiles instead of calling the Pinpoint UpdateEndpoint API. Authorization mode is selected automatically from the resolved auth session: - Authenticated (Cognito user-pool): POST {endpoint}/identify-user with Authorization: Bearer <accessToken>; backend keys the profile on `sub`. - Guest (Identity Pool unauthenticated): POST {endpoint}/identify-user-guest, SigV4-signed (execute-api) with the guest credentials; backend keys the profile on `identityId`. Enables registering a device token before sign-in. On a later authenticated call, options.previousGuestIdentityId folds the guest profile (and its devices) into the authenticated profile. Configured via the new notifications.amazon_connect_customer_profiles amplify_outputs key (Notifications.PushNotification.CustomerProfiles = { endpoint, region }). All public method signatures, the native event bus, and the token lifecycle match the Pinpoint provider 1:1; IdentifyUserInput options additionally accept optional deviceId/platform/appVersion/ previousGuestIdentityId device-registration fields. Pinpoint engagement telemetry (_campaign.*/_journey.*) is intentionally not ported (Connect Customer Profiles has no client-side event API). Additive and non-breaking: the default push-notifications export still resolves to Pinpoint; core gains an optional CustomerProfiles config type parsed side-by-side with Pinpoint. * fix(notifications): align Customer Profiles device registration to backend contract The guest/authed device-registration request built a body with flat top-level deviceToken/channelType and no userProfile. The deployed identify-user Lambda requires userProfile to be an object and reads device fields from options (options.deviceId, options.address, options.channelType), so guest registration returned HTTP 400 ("userProfile is required and must be an object"). Build the body as { userId, userProfile: userProfile ?? {}, options: { ...options, deviceId, address: deviceToken, channelType } }. Add getDeviceId(): a stable per-install UUID persisted in AsyncStorage so token refreshes upsert the same device object. channelType mapping (Android -> GCM) and resolveConfig unchanged. * refactor(notifications): extract transport-agnostic push logic into shared provider module Both push-notification providers (pinpoint, customer-profiles) previously duplicated ~80% of their surface. Extract the transport-agnostic logic into a new internal providers/shared/ module and have both providers consume it. Shared (providers/shared/): getBadgeCount, getLaunchNotification, getPermissionStatus, requestPermissions, setBadgeCount, onNotificationOpened, onNotificationReceivedInBackground/Foreground, onTokenReceived (.ts + .native.ts), plus getChannelType, inflightDeviceRegistration, and the shared API/input/output types. Each provider's apis/index.ts and utils/index.ts now re-export from ../../shared/...; provider types re-export shared types and keep only IdentifyUser/IdentifyUserInput/IdentifyUserOptions local. Provider-specific and unchanged: identifyUser + initializePushNotifications (pinpoint wires createMessageEventRecorder analytics listeners; customer-profiles omits them). Pinpoint external behavior is identical after the extraction. Also relocate the transport-agnostic unit tests to __tests__/.../shared/ and fix the stale registerDeviceWithCustomerProfiles test (mock @aws-amplify/react-native and getDeviceId; assert the current nested options:{deviceId,address,channelType} backend contract). Net -1349/+250 lines. Build (esm+cjs) and 36 pushNotifications test suites (118 tests) pass. * ci(release-preid): trigger preid release on feat/connect-customer-profiles-push Add the feature branch to the release-preid push trigger so every push publishes a prerelease. The preid label is derived from the 2nd branch path segment by the existing parse-preid job, yielding npm dist-tag 'connect-customer-profiles-push' (passes the forbidden-preid check). No change to release behavior for any other branch. * chore(notifications): remove changeset from CP push PR * feat(notifications): support browser identifyUser for Customer Profiles and rename internal engine to identifyUserInternal - Browser `identifyUser` now performs a device-less profile identify (e.g. identify end-users by email); the web build no longer throws PlatformNotSupportedError. The browser has no push token, so no device is registered. - Rename the internal engine `registerDeviceWithCustomerProfiles` -> `identifyUserInternal` and make it device-optional: device fields (deviceId/address/channelType) and the stable per-install deviceId are only attached when a device token is present, preserving find-or-create. - Both callers funnel through the one engine, mirroring the Pinpoint two-callers-of-one-core pattern: native `identifyUser` and the token-received `registerDevice` helper call `identifyUserInternal` directly (registerDevice does not route through identifyUser). - Keep `@aws-amplify/react-native` out of the web bundle by lazily importing `getDeviceId` only on the device-registration path. - Engine failure surfaces as PushNotificationError name `IdentifyUserException`. * fix(notifications): resolve deviceId in native layer to avoid __importStar dynamic-import crash in RN The lazy await import('./getDeviceId') in identifyUserInternal transpiled (CJS) to the __importStar tslib helper, which is not injected in the Hermes/React Native runtime -> ReferenceError on the device-registration path (DeviceRegistrationFailed). Remove the runtime dynamic import entirely: the engine no longer imports getDeviceId (static or dynamic). Instead the native callers (identifyUser.native and the token-received registerDevice helper) resolve the stable per-install deviceId via getDeviceId and inject it as options.deviceId. Web identifyUser stays device-less and RN-free without any dynamic import. * refactor(notifications): rename previousGuestIdentityId to guestIdentityId Rename the guest-merge wire-contract field previousGuestIdentityId to guestIdentityId across the Customer Profiles push identify flow, matching the backend contract rename. The IdentifyUserOptions field name is the JSON key sent on the wire in the identify-user request body's options, so this must stay in lockstep with the backend. * refactor(notifications): remove guestIdentityId from customer-profiles IdentifyUserOptions The backend no longer merges guest and authenticated Customer Profiles; guest and authenticated profiles are treated as completely separate profiles. Remove the now-dead client-side guestIdentityId guest->auth linking field from IdentifyUserOptions, its passthrough documentation in identifyUserInternal, and the related test assertion. * feat(notifications): align CP push client to SigV4 identify/register/remove backend contract Rework the customer-profiles push provider to the new backend contract: profile-only identifyUser (drops userId), new registerDevice/removeDevice native APIs, and a single SigV4 (execute-api) signedFetch transport for all three routes for both authenticated and guest Identity Pool sessions. Server derives principalId from the signer identity; the Bearer-JWT and guest-route paths are removed. initializePushNotifications wires TOKEN_RECEIVED->registerDevice and Hub 'signedOut'->removeDevice. * feat(notifications): attach Amplify user-agent header to CP push signedFetch Add the x-amz-user-agent telemetry header (getAmplifyUserAgent with Category.PushNotification + PushNotificationAction.IdentifyUser) to the customer-profiles signedFetch transport for all three routes, matching the prior Pinpoint/SDK path. The header is set before SigV4 signing so it is covered by the signature and the sent headers stay consistent. * refactor(core): rename amplify_outputs notifications key amazon_connect_customer_profiles -> amazon_connect Aligns the client parser with the backend-notifications OUTPUT_KEY rename and client-config schema v1.5. Updates AmplifyOutputsNotificationsProperties, the parseNotifications destructuring, and parseAmplifyOutputs test fixtures. The internal ResourcesConfig.PushNotification.CustomerProfiles key is unchanged. * fix(notifications,core): address CP-push review findings - core: add RegisterDevice + RemoveDevice to PushNotificationAction enum. - signedFetch: take an `action` arg; identify/register/remove pass the correct PushNotificationAction so the x-amz-user-agent encodes the per-route action. - buildDeviceRegistration: guard the token (throw PushNotificationError NoToken when neither a supplied nor a current token is available); return non-optional token. Add a new NoToken validation error code. - resolveCredentials: replace assert with an explicit if-throw so TS narrows credentials to non-undefined. - Tests: assert per-route action in the user-agent; assert signing receives no Authorization/Bearer header and fetch uses exactly the signer-returned headers; add buildDeviceRegistration fallback + no-token throw tests (drop the `as unknown as string` hack). - Docs: note identifyUser.native is intentionally pre-init-callable; document the getDeviceId module cache lifecycle. * fix(notifications): enforce https endpoint in Customer Profiles resolveConfig to prevent SigV4 token exfiltration resolveConfig is the single choke point for all three Customer Profiles push routes. The endpoint flows untrusted from amplify_outputs.json (amazon_connect.endpoint) into signedFetch, which SigV4-signs requests carrying the Identity Pool x-amz-security-token to whatever host it resolves to. Parse the endpoint with new URL() and assert https: scheme (new InvalidEndpoint validation code), rejecting http/ftp/malformed URLs before any credentialed request is signed. Scheme-only enforcement (no hostname allowlist) to stay compatible with custom domains, aws-cn/aws-us-gov, FIPS, and VPC endpoints. * fix(notifications): add client-side UserProfile/customAttributes validation mirroring backend bounds (defense-in-depth) * feat(notifications): warn on the deprecated default push-notifications entry point The default `aws-amplify/push-notifications` entry point is backed by Amazon Pinpoint, which reaches end-of-support on October 30, 2026. It cannot be removed without a breaking change, so all 11 of its runtime APIs now emit a one-time ConsoleLogger warning steering customers to the Customer Profiles sub-path export (`aws-amplify/push-notifications/customer-profiles`), which exposes an equivalent for every one of them. The warning names the deprecated entry point rather than the individual API: 9 of the 11 are transport-agnostic and are re-exported unchanged by every provider, so it is the entry point, not the behaviour of the call, that customers need to migrate away from. Mirrors the Analytics precedent (#14877). Reuses the existing ConsoleLogger convention rather than raw console.warn. Non-breaking: the wrapped consts preserve the exact names, types, and signatures of the underlying APIs (verified by a bidirectional type-assignability check over all 11), and synchronous throws and rejected promises both reach the caller untouched. * chore(notifications): add changeset for the Customer Profiles push provider Covers @aws-amplify/core, @aws-amplify/notifications, and aws-amplify as minor for the new Customer Profiles push-notifications provider and its sub-path exports, plus the default entry point deprecation notice. Follows the repo precedent of a minor bump for a new provider surface. * ci(release-preid): drop the Customer Profiles push branch from the preid trigger list The branch-specific preid publish trigger was only needed while iterating on this feature branch. Removing it keeps the workflow free of branch names that disappear once the feature merges, restoring parity with main. * fix(notifications): restrict Customer Profiles endpoint to execute-api host + normalize trailing slash Requests to the configured endpoint are SigV4-signed for execute-api, so the endpoint host is now validated against the API Gateway host of the resolved region (<api-id>.execute-api.<region>.amazonaws.com). Previously only the presence of endpoint/region and an https: protocol were checked, so a misconfigured or attacker-supplied endpoint would have received the signed credentials. Also strips trailing slashes from the endpoint so {endpoint}{path} never yields a double slash. URL.origin is deliberately not used, as it would drop an API Gateway stage path such as /prod. * fix(notifications): fetch the signed request url signRequest returns an HttpRequest whose url is authoritative and covered by the signature. Fetching the pre-signing url instead could send a url that does not match what was signed. * fix(notifications): dedupe concurrent getDeviceId via in-flight promise AsyncStorage reads are async, so concurrent first-calls could each read no stored id, mint their own UUID and write it, producing divergent device ids for one install. The resolution is now memoized as a single in-flight promise so concurrent callers share one resolution (single UUID, single write). The cached promise is cleared on failure so a transient storage error does not permanently wedge subsequent calls. * fix(notifications): re-register push device on sign-in; drop ineffective sign-out de-registration The auth Hub listener now re-registers the device on `signedIn`. A returning user's push token is unchanged across sign-in, so the native TOKEN_RECEIVED listener short-circuits and nothing re-registered the device — it stayed homed to the guest principal. Backend register-device is an idempotent last-writer-wins upsert on `deviceId`, so re-registering re-homes the existing row instead of creating a duplicate. The `signedOut` -> `removeDevice()` branch is removed. It could not work: `signOut()` clears credentials before the `signedOut` event fires, so the listener signed as a brand-new guest identity, and the backend's principal-gated delete returns 200 for a non-owner (no-info-leak invariant). The removal silently no-opped and `.catch` never ran. `removeDevice` remains a public API for applications to call before `signOut()`. Adds un-mocked credential-path coverage that drives the real resolveCredentials/signedFetch/SigV4 chain (only fetchAuthSession and fetch are stubbed) and asserts which identity signs each register/remove call. * docs(notifications): document the pre-signOut removeDevice contract De-registration is signed with the caller's current credentials and the backend only removes a device the calling principal owns, so `removeDevice()` must be awaited BEFORE `signOut()`. Documents this on both platform variants of the API and describes the device lifecycle in the provider changeset.
…or legacy/Metro resolution (#14899)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )