# Migrating a Legacy App to Cloud Native — Part 7

Photo by [Zan](https://unsplash.com/@zanilic?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)

This is part 7 in a series documenting my journey migrating my progressive web app, called SqAC, to AWS cloud native. If you haven’t been following it before now, here are the previous posts:

* [Part 1: Background](https://adam.fanello.net/migrating-a-legacy-app-to-cloud-native-part-1-68a1adbb95d5)
* [Part 2: Requirements & Architecture](https://adam.fanello.net/migrating-a-legacy-app-to-cloud-native-part-2-533dfebd38fb)
* [Part 3: Authentication](https://adam.fanello.net/migrating-a-legacy-app-to-cloud-native-part-3-4bb187fea485)
* [Part 4: Add Cloud Storage](https://adam.fanello.net/migrating-a-legacy-app-to-cloud-native-part-4-2741585e4953)
* [Part 5: Use Cloud Storage](https://adam.fanello.net/migrating-a-legacy-app-to-cloud-native-part-5-34696c6f0f43)
* [Part 6: AppSync API and S3 Trigger](https://adam.fanello.net/migrating-a-legacy-app-to-cloud-native-part-6-280cd65a0937)

Now in part 7, I’ll enhance user accounts first with Google Sign-in (federation), and then with local (guest) accounts for users to try out the app first.

But first…

### Updated Dependencies

This series isn’t about Angular, so I won’t go into details. However I found that to update to the latest Amplify libraries, I had to upgrade Angular from 6.0 to 8.0. At the same time, I updated various other dependencies.

Just about a week before AWS deprecated Node.js v8 in Lambdas, Amplify finally added support for Node.js v10 and so that update has been performed as well.

Pull Requests of code changes:

*   [Part 7 — Update Dependencies](https://github.com/kernwig/sqac-amplify/pull/8)
*   [Part 7 — Amplify Update for Node.js 10](https://github.com/kernwig/sqac-amplify/pull/15)

### Add Google Sign-In

The new cloud native version of SqAC gained user authentication via Cognito back in [part 3](https://adamfanello.medium.com/migrating-a-legacy-app-to-cloud-native-part-3-4bb187fea485). At the time, I didn’t add any federated sign-ins. The legacy app supported Google and Facebook sign-in, but I found that Facebook sign-in wasn’t popular. With Cognito accounts available in this new app, there’s no true *need* for any federated accounts, but personally I like using my Google account for authentication because it’s easier than tracking another account. Besides, this is another feature of Amplify to explore!

Amplify’s documentation to setup Google is [here](https://aws-amplify.github.io/docs/js/cognito-hosted-ui-federated-identity#google-sign-in-instructions-1). Yes, I actually used a bit of Google Cloud Platform for my AWS native app. Breath. It’s okay.

> $ **amplify auth update**  
Scanning for plugins...  
Plugin scan successful  
Please note that certain attributes may not be overwritten if you choose to use defaults settings.

> You have configured resources that might depend on this Cognito resource.  Updating this Cognito resource could have unintended side effects.

> Using service: Cognito, provided by: **awscloudformation**  
> What do you want to do? **Update OAuth social providers**  
> Select the identity providers you want to configure for your user pool: **Google**  
    
> 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](https://developers.google.com/identity) and create   
an App ID.   
   
> Enter your Google Web Client ID for your OAuth flow:  **<hidden>**  
> Enter your Google Web Client Secret for your OAuth flow:  **<hidden>**  
> Successfully updated resource sqacauth locally

> Some next steps:  
"amplify push" will build all your local backend resources and provision it in the cloud  
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud

> $ **amplify push**  
✔ Successfully pulled backend environment dev from the cloud.

An `amplify push` later, and Cognito is setup to use Google Sign-In. Once again, Amplify makes authentication easy! 🥳

In my Angular application’s account page, I inject `AmplifyService` as `amplifySvc` and added the function:

```typescript
signInWithGoogle() {  
   this.amplifySvc.auth().federatedSignIn({provider: 'Google'});  
}
```

A button on the page calls this function. This takes me to the familiar Google account selection page, and then navigates back to my application. There were a few problems though:

1.  It doesn’t recognize that I’m signed in until I navigated to the account page. Simply rendering the `<amplify-authenticator>` component triggered the sign-in to be recognized, but I don't want that on the app landing page.
2.  My name is “Unknown”.
3.  No photograph of myself was received.

We’ll get to problem #1 in a moment. The second two problems caught me by surprise because the legacy SqAC application also supported Google sign-in, through [Passport](http://www.passportjs.org/). In the Cognito AWS Console, I was able to select attribute mapping for name and picture (email and sub were already checked). This resolved receiving my name and photograph. This was easy to do, but I already knew where to look because I have experience with OAuth and Cognito. Someone looking to use Amplify without AWS experience would be lost.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1650409319847/CH3JHfDIo.png)

Problem #1 remains: sign-in isn’t recognized right away. Upon sign-in with Google, the app reloads and I see the Cognito user information in browser local storage. Yet, `Auth.authStateChange$` reports a state of `“signedOut”`. Explicitly requesting the current auth user also returns “No current user”. If I reload the page, then the state becomes `“signedIn”` and all is well. This seems like a bug. 🐞 I added information to the existing [Amplify-js Issue #4621](https://github.com/aws-amplify/amplify-js/issues/4621). After some messages exchanged and some experimentation, here’s a copy of my comment after I resolved this issue:

> Okay, found two fixes based on your comments [@Amplifiyer](https://github.com/Amplifiyer), both involving using the `Hub` to listen for signIn. I first used this as a direct analogue to monitoring `Auth.authStateChange$`. The `Hub` told me when a federated user signed in/out, and `authStateChange$` for a user pool user. I still wanted to land on my accounts page though. So I changed it to navigate to the account page on `Hub signIn` event. Doing that caused `Auth.authStateChange$` to trigger when the auth component rendered, so I just rely on that. Here's the code change: [kernwig/sqac-amplify@8de758e](https://github.com/kernwig/sqac-amplify/commit/8de758e5dcc8c0f6ef7ce729aa872acf0d96abc2)

> Note: The `Hub` doesn't play nice with Angular, thus the need to use `NgZone` directly.

[Amplify Hub](https://aws-amplify.github.io/docs/js/hub) is an in-browser publish-subscribe capability that is used by Amplify and available for us to use too. In Angular, services and RxJS provide for similar behavior and so I hadn’t used Hub before this.

### Auth Secrets

Upon going to commit these changes to github, I noticed that my Google Web Client Secret was stored in `amplify/team-provider-info.json`. Whether or not to commit this file is discussed in [this new section](https://aws-amplify.github.io/docs/cli-toolchain/quickstart#environments-and-teams) of the Amplify documentation. I ended up removing the file from source control.

### Secure Hosting & Authentication

To take federated login live, I needed HTTPS hosting. (OAuth doesn’t allow http callback URLs.) Thus I had to update my hosting configuration to include [CloudFront](https://aws.amazon.com/cloudfront/), the AWS Content Delivery Network:

> $ **amplify update hosting**  
? Specify the section to configure **CloudFront**  
CloudFront is NOT in the current hosting  
? Add CloudFront to hosting **Yes**  
? default object to return from origin **index.html**  
? Default TTL for the default cache behavior **86400**  
? Max TTL for the default cache behavior **31536000**  
? Min TTL for the default cache behavior **60**  
? Configure Custom Error Responses **No**  
? Specify the section to configure **Publish**  
You can configure the publish command to ignore certain directories or files.  
Use glob patterns as in the .gitignore file.  
? Please select the configuration action on the publish ignore. **exit**  
? Specify the section to configure **exit**  
$ **amplify publish**

This deployed SqAC to [https://d3l0j9nusq7n6r.cloudfront.net](https://d3l0j9nusq7n6r.cloudfront.net). I had to add this to the app’s [credentials at Google](https://console.cloud.google.com/apis/credentials), and then to my app:

> $ **amplify update auth**  
Please note that certain attributes may not be overwritten if you choose to use defaults settings.

> You have configured resources that might depend on this Cognito resource.  Updating this Cognito resource could have unintended side effects.

> Using service: Cognito, provided by: **awscloudformation**  
 What do you want to do? Add/Edit signin and signout redirect URIs  
 Which redirect signin URIs do you want to edit? (Press <space> to select, <a> to toggle all, <i> to invert selection)  
 Do you want to add redirect signin URIs? **Yes**  
 Enter your new redirect signin URI: [**https://d3l0j9nusq7n6r.cloudfront.net/**](https://d3l0j9nusq7n6r.cloudfront.net/)  
? Do you want to add another redirect signin URI **No**  
 Which redirect signout URIs do you want to edit? (Press <space> to select, <a> to toggle all, <i> to invert selection)  
 Do you want to add redirect signout URIs? **Yes**  
 Enter your new redirect signout URI: [**https://d3l0j9nusq7n6r.cloudfront.net/**](https://d3l0j9nusq7n6r.cloudfront.net/)  
? Do you want to add another redirect signout URI **No**  
Successfully updated resource sqacauth locally

> $ **amplify push**

But wait, this doesn’t work! 😱The Javascript library isn’t smart enough to choose between this CloudFront URL or the localhost one I already had. The `aws-exports.js` file contains both URLs comma-separated, but at runtime just the first one (localhost, since it was there first) is used. This utterly fails when running the app from CloudFront. See the [issue report here](https://github.com/aws-amplify/amplify-cli/issues/2792). How this gap continues to exist, I do not understand. Surely nearly all front-end developers work first locally (localhost) before deploying to the cloud (CloudFront)? I added this *hack* to the Angular `main.ts` to handle the problem:

```typescript
import awsconfig from './aws-exports';

// Choose an OAuth config based on environment
const redirectSignInOptions = awsconfig.oauth.redirectSignIn.split(',');
const redirect = environment.production
   ? redirectSignInOptions.find(s => s.startsWith('https'))  
   : redirectSignInOptions.find(s => s.includes('localhost'));  
awsconfig.oauth.redirectSignIn = redirect;  
awsconfig.oauth.redirectSignOut = redirect;
```

Here’s another surprise: by default, `amplify publish` does not invalidate the CloudFront cache. 🙄 That means after we publish and reload in browser, we will not see our changes! (Unless we wait a day - not even clearing the browser cache will help.) To actually publish and test try out the changes, we must explicitly pass an option: `amplify publish --invalidateCloudFront`. Yes, this is documented [here](https://aws-amplify.github.io/docs/cli-toolchain/quickstart#workflow-1) and of course I won’t remember this next time I publish and so I added a script to my `package.json` for it.

Pull Request: [Part 7 — Add Google Auth Provider](https://github.com/kernwig/sqac-amplify/pull/13)

### Guest Access

One of my requirements when migrating to AWS was to add guest access. I’ve had some complaints from folks who wanted to try out my app, but were turned off by having to authenticate with Google or Facebook in order to do anything. One way of appeasing this concern was to use a Cognito User Pool, so that users don’t need to link a social account. More impactful though is to allow basic functionality without creating an account at all. In SqAC, I called this a “local account” and provided some warnings about data loss without cloud backup. However, this provides a means for someone to play with the app and decide if it is something they want to go farther with before providing an email address.

Most of my work consisted of changes to my client application itself. I was able to load public content from storage (AWS S3) without difficulty thanks to the work I did in [part 4](https://adamfanello.medium.com/migrating-a-legacy-app-to-cloud-native-part-4-2741585e4953). User data is stored in the browser’s [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB). When there’s a cloud account, this data is copied up to S3 as well; for local accounts this step is skipped. Everything was going great until I hit the last feature to test: search via AppSync. This failed with an exception thrown saying “no current user”. 🤔 This surprised me — I figured not including an `@auth` on my GraphQL type meant “no auth” and thus no need for a user. 🤷‍♂️ Nope. A search brought me to some background and ultimately I stumbled right to the mass of [Amplify documentation](https://aws-amplify.github.io/docs/cli-toolchain/graphql#public-authorization) that tells us how to do public authentication. It *says*...

> 👉***Note: Don’t do this! Read on!*** 👈  
First, use API KEY authentication type:

> $ **amplify update api**  
? Please select from one of the below mentioned services: **GraphQL**  
? Choose the default authorization type for the API **API key**  
? Enter a description for the API key: **Public access**  
? After how many days from now the API key should expire (1-365): **7**  
? Do you want to configure advanced settings for the GraphQL API **No, I am done**.

> The following types do not have '@auth' enabled. Consider using @auth with @model  
  - Collection  
Learn more about @auth here: [https://aws-amplify.github.io/docs/cli-toolchain/graphql#auth](https://aws-amplify.github.io/docs/cli-toolchain/graphql#auth) 

> GraphQL schema compiled successfully.

Second, use auth mode API KEY and `@auth(rules: [{allow: public}])` on the GraphQL type.

I was nervous about the 7 day (default) expiration. Upon an `amplify push`, the CLI showed me a value for the “GraphQL API KEY” and it is set in my `aws-export.js`. My app was able to successfully perform the GraphQL query without creating a Cognito user, but it was clear that this would stop working in a week. I could increase this to 365, but that only delays the problem. Amplify CLI [issue 1450](https://github.com/aws-amplify/amplify-cli/issues/1450) has conversation of people struggling with this.

Back in that [Amplify documentation](https://aws-amplify.github.io/docs/cli-toolchain/graphql#public-authorization), it first says that API KEY must be used for public access, then gives a second example with IAM authentication for public access. Why not try that? 🤷‍♂️

I updated my GraphQL with `@auth(rules: [{allow: public, provider: iam}])` and then on the CLI:

> $ **amplify update api**  
? Please select from one of the below mentioned services: **GraphQL**  
? Choose the default authorization type for the API **IAM**  
? Do you want to configure advanced settings for the GraphQL API **No, I am done.**

> GraphQL schema compiled successfully.

> $ **amplify api gql-compile**  
> $ **amplify push**

A bit of testing with a cloud account and a local (guest) account — and it’s working! 🎉

*Call out to the Amplify team:* [This bit](https://aws-amplify.github.io/docs/cli-toolchain/graphql#public-authorization) of documentation can be made clearer that there are two approaches, and how to choose between them.

### Conclusion

As with everything Amplify (and honestly, doing new things in general) adding Google Sign-In and guest access was harder than it looked. It took some web searching and experimentation, but ultimately was easier than it would have been without Amplify.

### Coming next time…

Cut over! The legacy app is still at [https://sqac.fanello.net/](https://sqac.fanello.net/), and this new one is at [https://d3l0j9nusq7n6r.cloudfront.net](https://d3l0j9nusq7n6r.cloudfront.net). One has a slightly friendly name than the other. 😉

I just paid for another month of DigitalOcean hosting for the legacy app, and hope for it to be the last.

Until next time. 😎

*(This story was originally published* [*here*](http://fanello.net/home/2020/01/19/migrating-a-legacy-app-to-cloud-native-part-7/) *in January 2020.)*
