From f362a2573f39f6b4969d2cae127f1f8fcc70481c Mon Sep 17 00:00:00 2001 From: Fahad Sadiq Date: Mon, 26 Feb 2024 15:51:39 +0530 Subject: [PATCH 1/6] feat(web): Added functions to confirm user and get user attributes --- client/cognito-api.ts | 83 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/client/cognito-api.ts b/client/cognito-api.ts index 9f3aaf5..761ec42 100644 --- a/client/cognito-api.ts +++ b/client/cognito-api.ts @@ -240,6 +240,55 @@ export async function respondToAuthChallenge({ ).then(extractChallengeResponse); } +/** + * Confirms the sign-up of a user in Amazon Cognito. + * + * @param params - The parameters for confirming the sign-up. + * @param params.username - The username or alias (e-mail, phone number) of the user. + * @param params.confirmationCode - The confirmation code received by the user. + * @param [params.clientMetadata] - Additional metadata to be passed to the server. + * @param [params.abort] - An optional AbortSignal object that can be used to abort the request. + * @returns A promise that resolves to the response of the confirmation request. + */ +export async function confirmSignUp({ + username, + confirmationCode, + clientMetadata, + abort, +}: { + username: string; + confirmationCode: string; + clientMetadata?: Record; + abort?: AbortSignal; +}) { + const { fetch, cognitoIdpEndpoint, proxyApiHeaders, clientId, clientSecret } = + configure(); + return fetch( + cognitoIdpEndpoint.match(AWS_REGION_REGEXP) + ? `https://cognito-idp.${cognitoIdpEndpoint}.amazonaws.com/` + : cognitoIdpEndpoint, + { + headers: { + "x-amz-target": "AWSCognitoIdentityProviderService.ConfirmSignUp", + "content-type": "application/x-amz-json-1.1", + ...proxyApiHeaders, + }, + method: "POST", + body: JSON.stringify({ + Username: username, + ConfirmationCode: confirmationCode, + ClientId: clientId, + ClientMetadata: clientMetadata, + ...(clientSecret && { + SecretHash: await calculateSecretHash(username), + }), + }), + signal: abort, + } + ).then(throwIfNot2xx); +} + + export async function revokeToken({ refreshToken, abort, @@ -303,6 +352,40 @@ export async function getId({ .then((res) => res.json() as Promise); } +/** + * Retrieves the user attributes from the Cognito Identity Provider. + * + * @param abort - An optional `AbortSignal` object that can be used to abort the request. + * @returns A promise that resolves to an array of user attributes, where each attribute is represented by an object with `Name` and `Value` properties. + */ +export async function getUserAttributes({ + abort, +}: { + abort?: AbortSignal; +}): Promise<{ Name: string; Value: string }[]> { + const { fetch, cognitoIdpEndpoint, proxyApiHeaders } = configure(); + const tokens = await retrieveTokens(); + return await fetch( + cognitoIdpEndpoint.match(AWS_REGION_REGEXP) + ? `https://cognito-idp.${cognitoIdpEndpoint}.amazonaws.com/` + : cognitoIdpEndpoint, + { + headers: { + "x-amz-target": + "AWSCognitoIdentityProviderService.GetUserAttributes", + "content-type": "application/x-amz-json-1.1", + ...proxyApiHeaders, + }, + method: "POST", + body: JSON.stringify({ + AccessToken: tokens?.accessToken, + }), + signal: abort, + } + ).then(throwIfNot2xx) + .then((res) => res.json() as Promise<{ Name: string; Value: string }[]>) +} + export async function getCredentialsForIdentity({ identityId, abort, From bc5a7d109764a86792f5918222102c47fe48b9fa Mon Sep 17 00:00:00 2001 From: Fahad Sadiq Date: Mon, 26 Feb 2024 16:02:29 +0530 Subject: [PATCH 2/6] feat(cognito client): Added accessToken as a param for some function. --- client/cognito-api.ts | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/client/cognito-api.ts b/client/cognito-api.ts index 761ec42..02e567a 100644 --- a/client/cognito-api.ts +++ b/client/cognito-api.ts @@ -360,11 +360,13 @@ export async function getId({ */ export async function getUserAttributes({ abort, + accessToken }: { abort?: AbortSignal; + accessToken?: string; }): Promise<{ Name: string; Value: string }[]> { const { fetch, cognitoIdpEndpoint, proxyApiHeaders } = configure(); - const tokens = await retrieveTokens(); + const token = accessToken ?? (await retrieveTokens())?.accessToken; return await fetch( cognitoIdpEndpoint.match(AWS_REGION_REGEXP) ? `https://cognito-idp.${cognitoIdpEndpoint}.amazonaws.com/` @@ -378,7 +380,7 @@ export async function getUserAttributes({ }, method: "POST", body: JSON.stringify({ - AccessToken: tokens?.accessToken, + AccessToken: token, }), signal: abort, } @@ -485,13 +487,15 @@ export async function updateUserAttributes({ clientMetadata, userAttributes, abort, + accessToken, }: { userAttributes: { name: string; value: string }[]; clientMetadata?: Record; abort?: AbortSignal; + accessToken?: string; }) { const { fetch, cognitoIdpEndpoint, proxyApiHeaders } = configure(); - const tokens = await retrieveTokens(); + const token = accessToken ?? (await retrieveTokens())?.accessToken; await fetch( cognitoIdpEndpoint.match(AWS_REGION_REGEXP) ? `https://cognito-idp.${cognitoIdpEndpoint}.amazonaws.com/` @@ -505,7 +509,7 @@ export async function updateUserAttributes({ }, method: "POST", body: JSON.stringify({ - AccessToken: tokens?.accessToken, + AccessToken: token, ClientMetadata: clientMetadata, UserAttributes: userAttributes.map(({ name, value }) => ({ Name: name, @@ -521,13 +525,15 @@ export async function getUserAttributeVerificationCode({ attributeName, clientMetadata, abort, + accessToken }: { attributeName: string; clientMetadata?: Record; abort?: AbortSignal; + accessToken?: string; }) { const { fetch, cognitoIdpEndpoint, proxyApiHeaders } = configure(); - const tokens = await retrieveTokens(); + const token = accessToken ?? (await retrieveTokens())?.accessToken; await fetch( cognitoIdpEndpoint.match(AWS_REGION_REGEXP) ? `https://cognito-idp.${cognitoIdpEndpoint}.amazonaws.com/` @@ -541,7 +547,7 @@ export async function getUserAttributeVerificationCode({ }, method: "POST", body: JSON.stringify({ - AccessToken: tokens?.accessToken, + AccessToken: token, ClientMetadata: clientMetadata, AttributeName: attributeName, }), @@ -554,13 +560,15 @@ export async function verifyUserAttribute({ attributeName, code, abort, + accessToken }: { attributeName: string; code: string; abort?: AbortSignal; + accessToken?: string; }) { const { fetch, cognitoIdpEndpoint, proxyApiHeaders } = configure(); - const tokens = await retrieveTokens(); + const token = accessToken ?? (await retrieveTokens())?.accessToken; await fetch( cognitoIdpEndpoint.match(AWS_REGION_REGEXP) ? `https://cognito-idp.${cognitoIdpEndpoint}.amazonaws.com/` @@ -573,7 +581,7 @@ export async function verifyUserAttribute({ }, method: "POST", body: JSON.stringify({ - AccessToken: tokens?.accessToken, + AccessToken: token, AttributeName: attributeName, Code: code, }), @@ -586,13 +594,15 @@ export async function setUserMFAPreference({ smsMfaSettings, softwareTokenMfaSettings, abort, + accessToken, }: { smsMfaSettings?: { enabled?: boolean; preferred?: boolean }; softwareTokenMfaSettings?: { enabled?: boolean; preferred?: boolean }; abort?: AbortSignal; + accessToken?: string; }) { const { fetch, cognitoIdpEndpoint, proxyApiHeaders } = configure(); - const tokens = await retrieveTokens(); + const token = accessToken ?? (await retrieveTokens())?.accessToken; await fetch( cognitoIdpEndpoint.match(AWS_REGION_REGEXP) ? `https://cognito-idp.${cognitoIdpEndpoint}.amazonaws.com/` @@ -606,7 +616,7 @@ export async function setUserMFAPreference({ }, method: "POST", body: JSON.stringify({ - AccessToken: tokens?.accessToken, + AccessToken: token, SMSMfaSettings: smsMfaSettings && { Enabled: smsMfaSettings.enabled, PreferredMfa: smsMfaSettings.preferred, From 4e10d11303dc9c83d6f48237b6f6c4f11bfd7d8f Mon Sep 17 00:00:00 2001 From: Fahad Sadiq Date: Tue, 27 Feb 2024 13:30:16 +0530 Subject: [PATCH 3/6] Ran prettier --- cdk/custom-auth/fido2-notification.ts | 5 +++-- client/cognito-api.ts | 19 +++++++++---------- client/react/hooks.tsx | 11 +++++------ client/refresh.ts | 4 ++-- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/cdk/custom-auth/fido2-notification.ts b/cdk/custom-auth/fido2-notification.ts index 29dea03..5308514 100644 --- a/cdk/custom-auth/fido2-notification.ts +++ b/cdk/custom-auth/fido2-notification.ts @@ -178,8 +178,9 @@ async function getUserEmail(username: string) { logger.debug(`User ${username} doesn't have an e-mail address`); return; } - const emailVerified = UserAttributes?.find((a) => a.Name === "email_verified") - ?.Value; + const emailVerified = UserAttributes?.find( + (a) => a.Name === "email_verified" + )?.Value; if (!emailVerified) { logger.debug( `User with ${username} doesn't have a verified e-mail address` diff --git a/client/cognito-api.ts b/client/cognito-api.ts index 02e567a..be42157 100644 --- a/client/cognito-api.ts +++ b/client/cognito-api.ts @@ -242,7 +242,7 @@ export async function respondToAuthChallenge({ /** * Confirms the sign-up of a user in Amazon Cognito. - * + * * @param params - The parameters for confirming the sign-up. * @param params.username - The username or alias (e-mail, phone number) of the user. * @param params.confirmationCode - The confirmation code received by the user. @@ -288,7 +288,6 @@ export async function confirmSignUp({ ).then(throwIfNot2xx); } - export async function revokeToken({ refreshToken, abort, @@ -354,13 +353,13 @@ export async function getId({ /** * Retrieves the user attributes from the Cognito Identity Provider. - * + * * @param abort - An optional `AbortSignal` object that can be used to abort the request. * @returns A promise that resolves to an array of user attributes, where each attribute is represented by an object with `Name` and `Value` properties. */ export async function getUserAttributes({ abort, - accessToken + accessToken, }: { abort?: AbortSignal; accessToken?: string; @@ -373,8 +372,7 @@ export async function getUserAttributes({ : cognitoIdpEndpoint, { headers: { - "x-amz-target": - "AWSCognitoIdentityProviderService.GetUserAttributes", + "x-amz-target": "AWSCognitoIdentityProviderService.GetUserAttributes", "content-type": "application/x-amz-json-1.1", ...proxyApiHeaders, }, @@ -384,8 +382,9 @@ export async function getUserAttributes({ }), signal: abort, } - ).then(throwIfNot2xx) - .then((res) => res.json() as Promise<{ Name: string; Value: string }[]>) + ) + .then(throwIfNot2xx) + .then((res) => res.json() as Promise<{ Name: string; Value: string }[]>); } export async function getCredentialsForIdentity({ @@ -525,7 +524,7 @@ export async function getUserAttributeVerificationCode({ attributeName, clientMetadata, abort, - accessToken + accessToken, }: { attributeName: string; clientMetadata?: Record; @@ -560,7 +559,7 @@ export async function verifyUserAttribute({ attributeName, code, abort, - accessToken + accessToken, }: { attributeName: string; code: string; diff --git a/client/react/hooks.tsx b/client/react/hooks.tsx index 75ed867..cb743b0 100644 --- a/client/react/hooks.tsx +++ b/client/react/hooks.tsx @@ -168,12 +168,11 @@ function _usePasswordless() { ); const deleteFido2Credential = useCallback( (credentialId: string) => - setFido2Credentials( - (state) => - state?.filter( - (remainingAuthenticator) => - credentialId !== remainingAuthenticator.credentialId - ) + setFido2Credentials((state) => + state?.filter( + (remainingAuthenticator) => + credentialId !== remainingAuthenticator.credentialId + ) ), [] ); diff --git a/client/refresh.ts b/client/refresh.ts index 35f5ee3..99601a2 100644 --- a/client/refresh.ts +++ b/client/refresh.ts @@ -73,8 +73,8 @@ async function _scheduleRefresh({ ); abort?.addEventListener("abort", clearScheduledRefresh); } else { - refreshTokens({ abort, tokensCb, isRefreshingCb, tokens }).catch( - (err) => debug?.("Failed to refresh tokens:", err) + refreshTokens({ abort, tokensCb, isRefreshingCb, tokens }).catch((err) => + debug?.("Failed to refresh tokens:", err) ); } return clearScheduledRefresh; From c0bf07738b3e094cf41dbd5696323adb490e56e8 Mon Sep 17 00:00:00 2001 From: Fahad Sadiq Date: Tue, 27 Feb 2024 14:02:27 +0530 Subject: [PATCH 4/6] Fixed issue with prettier --- cdk/custom-auth/fido2-notification.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cdk/custom-auth/fido2-notification.ts b/cdk/custom-auth/fido2-notification.ts index 5308514..29dea03 100644 --- a/cdk/custom-auth/fido2-notification.ts +++ b/cdk/custom-auth/fido2-notification.ts @@ -178,9 +178,8 @@ async function getUserEmail(username: string) { logger.debug(`User ${username} doesn't have an e-mail address`); return; } - const emailVerified = UserAttributes?.find( - (a) => a.Name === "email_verified" - )?.Value; + const emailVerified = UserAttributes?.find((a) => a.Name === "email_verified") + ?.Value; if (!emailVerified) { logger.debug( `User with ${username} doesn't have a verified e-mail address` From 83858663b1edb81e0787c58b0e505bcd26ba032d Mon Sep 17 00:00:00 2001 From: Fahad Sadiq Date: Wed, 28 Feb 2024 10:11:20 +0530 Subject: [PATCH 5/6] Fixed lint issues with two files. --- client/react/hooks.tsx | 3 ++- client/refresh.ts | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/client/react/hooks.tsx b/client/react/hooks.tsx index cb743b0..dca1c51 100644 --- a/client/react/hooks.tsx +++ b/client/react/hooks.tsx @@ -168,7 +168,8 @@ function _usePasswordless() { ); const deleteFido2Credential = useCallback( (credentialId: string) => - setFido2Credentials((state) => + setFido2Credentials( + (state) => state?.filter( (remainingAuthenticator) => credentialId !== remainingAuthenticator.credentialId diff --git a/client/refresh.ts b/client/refresh.ts index 99601a2..35f5ee3 100644 --- a/client/refresh.ts +++ b/client/refresh.ts @@ -73,8 +73,8 @@ async function _scheduleRefresh({ ); abort?.addEventListener("abort", clearScheduledRefresh); } else { - refreshTokens({ abort, tokensCb, isRefreshingCb, tokens }).catch((err) => - debug?.("Failed to refresh tokens:", err) + refreshTokens({ abort, tokensCb, isRefreshingCb, tokens }).catch( + (err) => debug?.("Failed to refresh tokens:", err) ); } return clearScheduledRefresh; From c386769f8fe08b1516d750aac468ccbec9d1023a Mon Sep 17 00:00:00 2001 From: Fahad Sadiq Date: Wed, 28 Feb 2024 14:06:40 +0530 Subject: [PATCH 6/6] Fixed lint issues with hook file --- client/react/hooks.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/react/hooks.tsx b/client/react/hooks.tsx index dca1c51..75ed867 100644 --- a/client/react/hooks.tsx +++ b/client/react/hooks.tsx @@ -170,10 +170,10 @@ function _usePasswordless() { (credentialId: string) => setFido2Credentials( (state) => - state?.filter( - (remainingAuthenticator) => - credentialId !== remainingAuthenticator.credentialId - ) + state?.filter( + (remainingAuthenticator) => + credentialId !== remainingAuthenticator.credentialId + ) ), [] );