{"id":16551557,"url":"https://github.com/kimfucious/authentify","last_synced_at":"2026-05-01T20:32:02.983Z","repository":{"id":39147531,"uuid":"258509790","full_name":"kimfucious/Authentify","owner":"kimfucious","description":"🕵🏽‍♀️ Authentify : a Cognito Federated Sign-in Demo","archived":false,"fork":false,"pushed_at":"2023-01-05T19:38:16.000Z","size":10201,"stargazers_count":0,"open_issues_count":22,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-04T16:46:56.975Z","etag":null,"topics":["aws-amplify","aws-cognito","federated-signin","passwordless-authentication","react","signinwithapple"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kimfucious.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-04-24T12:44:22.000Z","updated_at":"2020-04-28T15:20:25.000Z","dependencies_parsed_at":"2023-02-04T12:47:11.844Z","dependency_job_id":null,"html_url":"https://github.com/kimfucious/Authentify","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/kimfucious/Authentify","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kimfucious%2FAuthentify","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kimfucious%2FAuthentify/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kimfucious%2FAuthentify/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kimfucious%2FAuthentify/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kimfucious","download_url":"https://codeload.github.com/kimfucious/Authentify/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kimfucious%2FAuthentify/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32512662,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws-amplify","aws-cognito","federated-signin","passwordless-authentication","react","signinwithapple"],"created_at":"2024-10-11T19:37:43.912Z","updated_at":"2026-05-01T20:32:02.962Z","avatar_url":"https://github.com/kimfucious.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Authentify : a Cognito Federated Sign-in Demo\n\n## What is Authentify?\n\nAuthentify is an effort to develop and document the best way to handle federated logins using AWS Cognito.\n\nI got started on this because I needed to add **Sign in with Apple** to a React Native app. This documentation currently covers React only, but I may add React Native later. Most of the basic concepts are the same.\n\nAuthentify supports federated sign-in with Apple and Google via AWS Cognito.\n\n💵 **_Setting up [Sign in with Apple](https://developer.apple.com/sign-in-with-apple/) requires an [Apple Developer](https://developer.apple.com/) account, which costs money. Using AWS services also costs money, but it's pay as you go, and you'll probably stay within th free tier if just testing Authentify out_**\n\nAuthentify was created with the following:\n\n- [AWS Amplify](https://aws-amplify.github.io/docs/js/start?platform=purejs)\n- [Create React App](https://reactjs.org/docs/create-a-new-react-app.html)\n- [React Router](https://reacttraining.com/react-router/web/guides/quick-start)\n- [Redux](https://redux.js.org/)\n\n## What does Authentify do?\n\nAuthentify demonstrates how an app can use AWS Cognito to allow users to sign in with third-party credentials (e.g. Amazon, Apple, Facebook, \u0026 Google), which then allows the user to access both Authentify \"private pages\" (those requiring the user to be signed in to see) and its backend AWS resources, like cloud databases, serverless functions, etc.\n\n### Here's the abbreviated flow:\n\n1. A user accesses Authentify.\n2. If they have not already signed up, they will need to click \"New User\" and follow the sign up process, before they can sign in.\n3. If they are not already signed in, they'll see the Sign-in page, where they are presented with Sign-in buttons.\n4. If they are already signed in, they'll be sent to the Home page, with a brief Loading intermission.\n\n🤔 What I mean by \"already signed in\", is that a user can return to Authentify, having already signed in prior, and be considered signed in.\n\n5. When the (signed up) user clicks on a Sign-in button, the auth process starts for the respective provider (e.g. Apple or Google).\n6. Upon successful authentication, they are returned to Authentify.\n7. There is a loading process, and then the Home page is shown.\n8. If an error occurs in the authentication process, the user is returned to the Authentify Sign-in page.\n9. When the user clicks the Sign out button, they are shown the Sign-in screen.\n\n### Here's a few rules:\n\n1. While the user is signed in, they can access the Home page and open pages (i.e 404, Loading, etc.).\n2. While the user is not signed in, they can access the Sign-in page and open pages.\n3. When the user signs out, all user data should be removed from Authentify's Redux state.\n\nSounds pretty simple, right?\n\n🏗️️ _Below this point is a work in progress._\n\n## This is the part that's too long to read\n\n🛠️ To work with this repo with your own AWS backend follow the instructions [here](#how-to-clone-this-repo-and-use-it-for-yourself-with-your-own-aws-backend).\n\n### Some basic setup detail\n\nThere are five pages in Authentify: Home, Loading, Not Found (404), and Sign-in. The fifth page is AuthCallback, which is used when receiving an auth response from an auth provider (i.e. Apple, Google, etc.)\n\nWhen the user visits Authentify, the **Sign-in** page is displayed.\n\n### Signing Up\n\nThe user must sign up, before they can sign in, so they click on `New User`, which displays a Sign Up Form.\n\nUpon successful submittal of the form (there is client and server validation via Pre sign-up lambda trigger function), the user is shown a screen where they are to enter a verification code. This code is sent from Cognito via email.\n\nOnce the user succesfully submits the code, they are shown a success message and a button that takes them back to the Sign In page. Here they can choose to: Sign in without Password, Sign in with Apple, or Sign in with Google.\n\nTODO: Talk about the Apple and Google email addresses.\n\nTODO: Talk about Access Code (Challenge) or remove it if not implementing.\n\nTODO: Talk about error handling.\n\n### Signing In\n\nTODO: describe Sign in without Password (not implemented yet)\n\nWhen the user clicks one of the **Sign in with...** buttons for Apple or Google, a federated sign-in process is initiated.\n\nThe user is navigated away from Authenitfy to an authorization site, where they can enter their credentials for Apple or Google.\n\nUpon successful third-party authorization, a Pre sign-up lambda function will attempt to match the Apple or Google email with that of the now existing Cognito user. If it is successful, it will link the `EXTERNAL_PROVIDER` account with the user's Cognito (username based) account.\n\nTODO: the known bugs link below doesn't work on Github (prolly a bug)\nThere are a few quirks to this implmentation. See the [Known Bugs section](#known-bugs-🐛) for more info.\n\n### The Redirect URL (redirect_url)\n\nUpon a successful third-party authentication, the user is returned to Authenitfy by way of a **redirect_url**.\n\nThese are different animals in web browsers than they are in native apps. The below covers behavior specific to working in a web browser.\n\nTODO: Create a separate section/repo for React Native.\n\nThe **redirect_url** is configured in the associated **Cognito User Pool** in the AWS Console. Authentify's user pool (and identity pool) was created with Amplify, using `amplify auth add`. See [here](#to-rebuild-amplify-project) for details on that.\n\nIn Authentify's development environment, the **redirect_url** is set to \u003chttps://localhost:3000/auth-callback.\u003e\n\n💁🏽‍♀️ **_`npm start` in `package.json` is configured to launch this app using HTTPS on port 3000. The reason for this is because HTTPS is required in order for federatedSignIn to work. The port 3000 is just the default for a Create React App. You can change this as desired. If you're on windows see [here](#windows)._**\n\nWhen the user is returned to Authenitfy, after successfuly authenticating with Apple or Amazon, the URL in the address bar is the **redirect_uri** with some parameters tacked on:\n\n```console\nhttps://localhost:3000/auth-callback/?code=[somecode]\u0026state=[somestate]\n```\n\nNote the **auth-callback** in the above. This will navigate to the AuthCallback page of the app, as defined in the Authentify's [router configuration](https://github.com/kimfucious/authentify/blob/master/src/router/AppRouter.js#L14).\n\nThe **AuthCallback** page will do one of three things:\n\nTODO: Add other logic for other errors related to Authorization code and \"User already exists\" bug.\n\n1. If there is no error in the **redirect_url**, it will navigate the user to the **Loading** page.\n2. If there is an error in the **redirect_url**, it will show an error message if the error does not include the word \"cancelled\".\n3. If there is an error in the **redirect_url** and the error includes the word \"cancelled\", it will navigate the user back to the **Sign-in** page.\n\n☝️ **_The error handling above is my personal preference, as I feel the user knows they just clicked cancel, and they don't need to see an error message about what they just did._**\n\nAt this point a few things can have happened:\n\n1. The user has successfully authenticated via a third party.\n2. The user has cancelled the authetication process.\n3. There was an error during the authentication process.\n\nIf we did nothing else in Authentify at this point, the user would be stuck on either AuthCallback, Loading, or Sign-in.\n\nBoth the AuthCallback page (which only shows if there is an error that does not included \"cancelled\") and the Sign-in pages have buttons to take an action. But the Loading page just sits and spins.\n\n✔️ Checkpoint: The successfully authenticated user is sitting on the Loading page, per the above. How to they get to the Home page?\n\n### Bring out the Hub 🎡\n\nTo get Authentify to perform specific actions when certain authentication events happen, we need to setup listeners.\n\n[Amplify Hub](https://aws-amplify.github.io/docs/js/hub) allows us to do with relative ease.\n\n🚀 **_When using Authentication with AWS Amplify, you don’t need to refresh Amazon Cognito tokens manually. The tokens are automatically refreshed by the library when necessary._**\n\nAuthentify's main [index.js](https://github.com/kimfucious/authentify/blob/master/src/index.js#L36) file is where the Hub is configured and listens for two key events: **configured** and **signIn**.\n\nLet's talk about **signIn** first.\n\nWhen a user successfully signs into Authentify with Apple or Google via Cognito, the **signIn** event is \"heard\" by Hub, because of the **auth** listener, and it executes the code in the **switch case** matching **\"signIn\"**.\n\nThis does one thing: it calls the **handleSignIn** function, which looks like this:\n\n```js\nconst handleSignin = async (userInfo) =\u003e {\n  try {\n    if (!userInfo) {\n      userInfo = await Auth.currentAuthenticatedUser();\n    }\n    console.log(\"🚚 Dispatching user data to Redux...\");\n    store.dispatch(signInCognitoUser(userInfo));\n  } catch (error) {\n    console.warn(error);\n  }\n};\n```\n\nIn brief, this function uses the Amplify Auth method, **currentAuthenticatedUser** to get information from the user in Cognito. As this function may receive `userInfo` from other callers, it's only used if the supplied argument, `userInfo`, is falsy.\n\nThis **userInfo** data is then set to a Redux action creator, [signInCognitoUser](https://github.com/kimfucious/authentify/blob/master/src/actions/authActions.js), where it gets dispatched to Redux state, after some massaging.\n\n✔️ **_Checkpoint: The user is successfully signed in. They are on the Loading page. How do they get to the Home page?_**\n\n### The Loading Page\n\nBecause there are some async actions that take place, like getting user data from Cognito with **Auth.currentAuthenticatedUser()**, there will likely be a brief moment of time where the user is waiting for said actions to finish. Rather than have the app sit there looking like it's not doing anything, we send the user to the **Loading** page during such times.\n\nIn the auth event **signIn** case, the user is sent to the **Loading** page from the AuthCallback page as mentioned prior. There is one other case where the user is sent to the **Loading** page: when Hub listener \"hears\" the **configured** auth event, which is covered [a bit later on](#the-configuration-auth-event).\n\nThe [Loading page](https://github.com/kimfucious/authentify/blob/master/src/pages/Loading.js) is configured to read Redux state, using a **useSelector** hook (from the react-redux library).\n\nIt's looking specifically for the **username**:\n\n```js\nconst { username } = useSelector((state) =\u003e state.auth);\n```\n\nThe Loading page also uses a React **useEffect** hook to \"watch\" the **username** in the aforementioned selector.\n\n```js\nuseEffect(() =\u003e {\n  if (username) {\n    console.log(\n      \"👀 Loading page sees user data in state.  🏠 Redirecting to home.\"\n    );\n    history.replace(\"/home\");\n  }\n}, [history, username]);\n```\n\n**username** will be _undefined_ until the following things have occurred:\n\n1. User successfully authenticates with Apple or Google\n2. Hub \"hears\" the auth event, **\"signIn\"**\n3. Hub runs the **handleSignIn** function\n4. The **handleSignIn** function dispatches the user data to state (via the signInCognitoUser action creator)\n\nOnce **username** appears in state, the useEffect hook will navigate the user to the Home page.\n\n✔️ Checkpoint: So that's all good for when a user signs on, but what about the **configuration** auth event?\n\n### The Configuration Auth Event\n\nOnce a user has successfully authenticated with a third-party auth provider in Authentify, that login lasts for 30 days. That's the default setting in the **Refresh token expiration (days)** setting found in the **App Client** configuration in the **AWS Cognito User Pool**.\n\nThe upshot of this is that when a user returns to Authentify, before 30 days have elapsed, without having logged out, they will skip the third party auth process.\n\nStrangely, for me at least, the auth event, **signIn** does not occur, which means that Hub will not trigger the **handleSignIn** function. And thus, we handle this scenario with a case block, matching **\"configured\"** in the Hub auth listener.\n\n```js\n    case \"configured\":\n      try {\n        if (history.location.pathname.includes(\"auth-callback\")) {\n          console.log(\"🔍 AuthCallback detected!\");\n        } else {\n          const userInfo = await Auth.currentAuthenticatedUser();\n          console.log(\"✔️ User is already signed in\", userInfo);\n          if (history.location.pathname === \"/\") {\n            console.log(\"🚦 User coming from sign in. Pushing to loading...\");\n            history.push(\"/loading\");\n          }\n          handleSignin();\n        }\n      } catch (error) {\n        if (error === \"not authenticated\") {\n          console.log(\"👮 User is not signed in\");\n        } else {\n          console.warn(\"💣 Error in Hub:\", error);\n        }\n        // Auth.currentAuthenticatedUser() throws an error if not signed in\n        // We don't push from here as the redirect will be handled by protected routing\n      }\n      break;\n```\n\n#### Explaining the above:\n\n1. When the Hub \"hears\" the auth event, **\"configured\"** the above block of code is run.\n2. `history.location.pathname` is checked for the inclusion of `auth-callback`, which will be the case when the user has triggered federatedSignIn by clicking on a button on the Sign-in page. This is **_not_** the case when a user is returning to Authentify, having successfully signed in prior, so we hit the `else` clause.\n3. The `else` clause calls the async method, `Auth.currentAuthenticatedUser`, which will return an error if the user is not authenticated. In the case, where the user is returning to Authentify, having already authenticated prior (within 30 days of having done so), `Auth.currentAuthenticatedUser` will return the user data, and not throw and error.\n4. If the user is coming from `history.location.pathname === \"/\"`, which is to say, the **Sign-in** page, they will get redirected to the **Loading page**, and the **handleSignin** function will get called.\n5. If the user is coming from a `history.location.pathname` of something other than `\"/\"`, it means that they're following a link to a page in Authentify, rather than coming from the **Sign-in** page. This could be them typing something in the address bar, following an old bookmark, or whatever. In this case, they'll be directed to the page they are looking for, or--more likely--they will hit the **Page Not Found (404)** page.\n6. If the user is not authenticated, an error will be thrown. We catch this in the catch block, and if the `error === \"not authenticated\"`, we handle it quietly with a `console.log`. If the error is something else, we handle it loudly with a `console.warn`. TODO: setup better handling for non \"not authorized\" errors.\n\nTODO: Talk about protected routes.\n\nTODO: How to prevent unwanted users from signing up i.e. custom auth flow/pre signup lambda trigger.\n\nTODO: Talk about unexplained flash on signout. \u003c== moot point, remove animation on div.\n\nTODO: Talk about Pre sign-up lambda trigger, including adding read access to \"auth\" and adding \"cognito-idp:AdminLinkProviderForUser\" to lambda-execution-policy. Also explain that I'm cheating with custom attributes, because they are not searchable.\n\nTODO: Why, when the user is logged in and the user navigates (via the address bar) to a page that does not exist, does the app lose it's state?\n\n## How to clone this repo and use it for yourself with your own AWS backend\n\n### Prerequisites\n\n1. [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html)\n2. [AWS Amplify CLI](https://aws-amplify.github.io/docs/cli-toolchain/quickstart)\n3. [Google OAuth setup](https://aws-amplify.github.io/docs/js/cognito-hosted-ui-federated-identity#google-sign-in-instructions)\n4. [Apple OAuth setup](https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple#create-an-app-id)\n\n### Use this Repo As-is\n\nYou can use this repo as-is and create your own AWS backend by running `amplify init` from within the cloned directory. Should you go this route, you'll accept how things are currently named.\n\nYou'll be asked a couple of questions that are specific to you, including the Google OAuth client ID and secret key, for example, but it should \"just work\" 🙏🏽\n\nIf you want to start fresh, follow the instructions below.\n\n### To Rebuild Amplify project\n\nWhen you clone this repo, it comes down with an Amplify directory. While you can probably edit this information and get things running that way, I've always found it easier to wipe out the `amplify` directory and start from scratch on simple projects like this. If working on a larger project, with a lot of backend resources, that might be a different story.\n\n1. Delete the `amplify` directory from the cloned project\n2. Follow the below process, changing values according to your requirements\n\n#### Run: `amplify init`\n\n```console\n$ amplify init\nNote: It is recommended to run this command from the root of your app directory\n? Enter a name for the project Authentify\n? Enter a name for the environment dev\n? Choose your default editor: Visual Studio Code\n? Choose the type of app that you're building javascript\nPlease tell us about your project\n? What javascript framework are you using react\n? Source Directory Path:  src\n? Distribution Directory Path: build\n? Build Command:  npm run-script build\n? Start Command: npm run-script start\nUsing default provider  awscloudformation\n\nFor more information on AWS Profiles, see:\nhttps://docs.aws.amazon.com/cli/latest/userguide/cli-multiple-profiles.html\n\n? Do you want to use an AWS profile? Yes\n? Please choose the profile you want to use default\nAdding backend environment dev to AWS Amplify Console app: di00wqe7newuh \u003c== auto generated\n⠋ Initializing project in the cloud...\n```\n\n#### Run: `amplify auth add`\n\n```console\n$ amplify auth add\nUsing service: Cognito, provided by: awscloudformation\n\n The current configured provider is Amazon Cognito.\n\n Do you want to use the default authentication and security configuration? Default configuration with Social Provider (Federation)\n Warning: you will not be able to edit these selections.\n How do you want users to be able to sign in? Username\n Do you want to configure advanced settings? Yes, I want to make some additional changes.\n Warning: you will not be able to edit these selections.\n What attributes are required for signing up? Email\n Do you want to enable any of the following capabilities?\n What domain name prefix you want us to create for you? authentify61179086-61179086\n Enter your redirect signin URI: https://localhost:3000/auth-callback/\n? Do you want to add another redirect signin URI No\n Enter your redirect signout URI: https://localhost:3000/\n? Do you want to add another redirect signout URI No\n Select the social providers you want to configure for your user pool: Google\n\n You've opted to allow users to authenticate via Google.  If you haven't already, you'll need to go to https://developers.google.com/identity and create an App ID.\n\n Enter your Google Web Client ID for your OAuth flow: [your ID goes here]\n Enter your Google Web Client Secret for your OAuth flow:  [your key goes here]\nSuccessfully added resource authentify61179086 locally\n\nSome next steps:\n\"amplify push\" will build all your local backend resources and provision it in the cloud\n\"amplify publish\" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud\n```\n\n#### Run: `amplify function add`\n\n```console\n$ amplify function add\nUsing service: Lambda, provided by: awscloudformation\n? Provide a friendly name for your resource to be used as a label for this category in the project: authentifyPreSignUp\n? Provide the AWS Lambda function name: authentifyPreSignUp\n? Choose the function runtime that you want to use: NodeJS\n? Choose the function template that you want to use: Hello World\n? Do you want to access other resources created in this project from your Lambda function? Yes\n? Select the category auth\nAuth category has a resource called authentify12345678\n? Select the operations you want to permit for authentify12345678 read\n\nYou can access the following resource attributes as environment variables from your Lambda function\nvar environment = process.env.ENV\nvar region = process.env.REGION\nvar authAuthentify12345678UserPoolId = process.env.AUTH_AUTHENTIFY12345678?_USERPOOLID\n\n? Do you want to invoke this function on a recurring schedule? No\n? Do you want to edit the local lambda function now? No\nSuccessfully added resource authentifyPreSignUp locally.\n```\n\nOnce complete paste the code from the repo at `amplify/backend/function/authentifyPreSignUp/src/index.js` into the same location in this project locally.\n\n#### Run: `amplify push`\n\n```console\n$ amplify push\n✔ Successfully pulled backend environment dev from the cloud.\n\nCurrent Environment: dev\n\n| Category | Resource name       | Operation | Provider plugin   |\n| -------- | ------------------- | --------- | ----------------- |\n| Auth     | authentify12345678  | Create    | awscloudformation |\n| Function | authentifyPreSignUp | Create    | awscloudformation |\n? Are you sure you want to continue? (Y/n)\n⠧ Updating resources in the cloud. This may take a few minutes...\n\nLots of stuff happens, and then...\n\nHosted UI Endpoint: ... \u003c== your endpoint here\nTest Your Hosted UI Endpoint: ... \u003c== your enpoint test URL here\n```\n\n#### Configure Pre sign-up lambda trigger\n\nNote that if you have multiple environments (e.g. \"dev\", \"prod\", etc.), you'll need to do this for each one.\n\n##### Enable Pre sign-up trigger in Cognito User Pool\n\n1. Open Cognito in AWS Console.\n2. Click on Manage User Pools\n3. Click on the User Pool that looks like `authentifya6e66045_userpool_12345678-dev`\n4. On the left, under \"General Settings\", click on Triggers\n5. In the top left section, labeled Pre sign-up, select the function that looks like `amplifyPreSignUp-dev` from the Lambda Function drop down menu.\n6. Click Save Changes\n\n##### Edit execution policy for IAM role\n\nIn order for the Pre sign-in function to link federated accounts to existing user accounts, you'll need to update the execution policy of the lambda role associated with this project\n\n1. Open IAM Management in AWS Console\n2. Click Roles in the left menu\n3. Search for authentify \u003c== this can be different if you've changed the name in the above steps\n4. Find the role that looks like `authentifyLambdaRole6637e7e0-dev` and click on it.\n5. On the Permissions tab, expand the item in the table named, `amplify-lambda-execution-policy`\n6. Click the Edit Policy button\n7. Click the JSON tab\n8. Add the entry `\"cognito-idp:AdminLinkProviderForUser,\"` inside the `Action` array\n9. Click the Review Policy Button\n10. Click Save changes (you can safely ignore the yellow warning) near Summary\n\n🎉 At this point you have your own working dev setup of Authentify! Run `npm start` in the project root directory to see it in action.\n\n### Edit current configuration method (not sure how to do this quite yet)\n\nYou could probably do this, but it seems like more hassle than it's worth, because Amplify makes things so easy.\n\n## Notes\n\n### Apple\n\n1. The icon on the Apple authentication pages will only show if you have an iOS app in the App Store. Dave Moore says so [here](https://stackoverflow.com/questions/58475605/how-can-i-set-a-logo-in-the-sign-in-with-apple-consent-screen). I _believe_ the reason behind this is that web apps are considered supplemental to iOS apps in the Apple mindset. Thus there is no reason to add Sign in with Apple to a website that does not have an iOS app for which it is supplementing.\n\n### Amplify\n\n1. Be sure to exclude amplify/team-provider-info.json from public repositories, .esp when doing federated sign-in with Cognito. This file will expose your secret keys from the third party auth providers.\n2. At present, the **Amplify CLI** does not handle adding Apple as an authentication provider. The set for this is all manual. Luckily, since we've added Google as an auth provider, Amplify does do most of the legwork for us, like creating the **User Pool** and **Identity Pool**.\n\n### Windows\n\n1. Running npm on Windows requires the modifcation of **package.json** scripts in order for them to work:\n\n- add `.cmd` to the script names\n- change the start script so that it will run the project using HTTP\n\n```json\n  \"scripts\": {\n    \"build\": \"react-scripts.cmd build\",\n    \"eject\": \"react-scripts.cmd eject\",\n    \"levelUp\": \"standard-version.cmd\",\n    \"levelUpDry\": \"standard-version.cmd --dry-run\",\n    \"levelUpMajor\": \"standard-version.cmd --release-as major\",\n    \"levelUpMajorDry\": \"standard-version.cmd --release-as major --dry-run\",\n    \"levelUpMinor\": \"standard-version.cmd --release-as minor\",\n    \"levelUpMinorDry\": \"standard-version.cmd --release-as minor --dry-run\",\n    \"levelUpPatch\": \"standard-version.cmd --release-as patch\",\n    \"levelUpPatchDry\": \"standard-version.cmd --release-as patch --dry-run\",\n    \"pushDevelop\": \"git push --follow-tags origin develop\",\n    \"pushMaster\": \"git push --follow-tags origin master\",\n    \"start-nossl\": \"react-scripts.cmd start\",\n    \"start\": \"set HTTPS=true\u0026\u0026set PORT=3000\u0026\u0026react-scripts.cmd start\",\n    \"test\": \"react-scripts test\"\n  },\n```\n\n## Known Bugs 🐛\n\n### \"Already found an entry for username\" error on first federated sign-in\n\nThe first time a user signs in with Apple or Google after succesfully signing up Cognito will throw an error.\n\nThe second time around, this error does not occur. No app refresh is required for the second attempt to work.\n\n### Federated sign-in changes Cognito user email\n\nIt is not uncommon for users to have multiple email addresses. For example, their Apple email may differ from their Google email, and each of those may differ from the email they use to sign up with Cognito.\n\nIn an attempt to handle this scenario, Authentify uses custom user attributes in the Cognito User Pool: `custom:appleEmail` and `custom:googleEmail`.\n\nThere is some logic in the Pre sign-up lambda function to do the following:\n\nIf the provider email matches an existing Cognito user's email or the custom attribute for the provider, the external provider account will be created and linked to the existing Cognito user account\n\nFor example, say the Cognito user's email is ada@example.com and their `custom:googleEmail` is set to ada@gmail.com. When they click `Sign in with Google` and authenticate using the email address ada@gmail.com, the Cognito account and the Google external account will be linked.\n\nThe bug is that, due to attribute mapping, the Cognito user's email will be overwritten with the Google email address.\n\nTODO: Describe how email gets overwritten when `adminLinkProviderForUser` is run.\n\nThis means that when another user, attempts to sign up with ada@example.com, using a different username (e.g. AdaX), they will not be prevented from doing so.\n\n#### Workaround\n\nThe above is not an ideal situation, and neither is this work-around.\n\nTo get around the aforementioned problem in Authentify, I bascially cheat the system. It's not quite the Kobayashi Maru, but it does kinda work to a point.\n\nIn brief, when the user first signs up, I populate the `preferred_username` attribute in the User Pool with the user's original Cognito email address. And when the Pre sign-up function runs, I check all four attributes (email, custom:appleEmail, custom:googleEmail, and preferred_username) before allowing/preventing the creation of the external provider account.\n\nThis is obviously hacky, but it works. What doesn't work is this: custom attributes are not searchable.\n\nWhy does that matter? Because, in the Pre sign-up lambda function, if we want to do what Authentify actually does, we need to check the custom attributes of `custom:appleEmail` and `custom:googleEmail`. And because we cannot search or use the [ListUsers](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_ListUsers.html) API with a filter using custom attributes, we need to list **all** users and work with them.\n\nThis is not at all good practice, .esp when the number of users grows. I've only left Authentify the way it is from demonstration purposes.\n\nTODO: write about how \"Sign in without Password\" is affected by this.\n\nIn real life, I believe another solution would be required, such as only allowing \"Sign in with...\" users that have external provider accounts with emails that match the email address that was used to sign up with to sign in.\n\n## References\n\n- [A sample implementation of Passwordless E-Mail Auth in Amazon Cognito](https://github.com/aws-samples/amazon-cognito-passwordless-email-auth)\n- [Authorizing Amazon Cognito to Send Amazon SES Email on Your Behalf (from a Custom FROM Email Address)](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-settings-ses-authorization-to-send-email.html)\n- [AWS Amplify Javascript Authentication (official)](https://aws-amplify.github.io/docs/js/authentication)\n- [AWS Amplify Javascript Hub (official)](https://aws-amplify.github.io/docs/js/hub)\n- [AWS Cognito Developerguide (official)](https://docs.aws.amazon.com/cognito/latest/developerguide/what-is-amazon-cognito.html)\n- [Customizing User Pool Workflows with Lambda Triggers](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html)\n- [https://docs.aws.amazon.com/ses/latest/DeveloperGuide/request-production-access.html](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/request-production-access.html)\n- [Implementing passwordless email authentication with Amazon Cognito](https://aws.amazon.com/blogs/mobile/implementing-passwordless-email-authentication-with-amazon-cognito/)\n- [Making setInterval Declarative with React Hooks by Dan Abramov](https://overreacted.io/making-setinterval-declarative-with-react-hooks/)\n- [Send with SES](https://docs.sendwithses.com/)\n- [Social Provider Setup Docs (Amazon, Facebook, Google) by Amplify](https://aws-amplify.github.io/docs/js/cognito-hosted-ui-federated-identity#social-provider-setup)\n- [The Complete React Native Guide to User Authentication with the Amplify Framework by Nader Dabit](https://dev.to/aws/the-complete-react-native-guide-to-user-authentication-with-the-amplify-framework-ib2)\n- [Working with Email Templates in Amazon SES](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/ses-examples-creating-template.html)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkimfucious%2Fauthentify","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkimfucious%2Fauthentify","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkimfucious%2Fauthentify/lists"}