├── .gitignore
├── README.md
├── assets
├── auth-group-selection.PNG
├── authenticator-example.PNG
└── hello_user.PNG
├── awsmobilejs
├── .awsmobile
│ └── info
│ │ ├── aws-info.json
│ │ └── project-info.json
└── backend
│ └── mobile-hub-project.yml
├── package.json
├── public
├── favicon.ico
├── index.html
└── manifest.json
├── src
├── App.css
├── App.js
├── App.test.js
├── aws-exports.js
├── index.css
├── index.js
├── logo.svg
└── registerServiceWorker.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
23 | #awsmobilejs
24 | awsmobilejs/.awsmobile/backend-build
25 | awsmobilejs/\#current-backend-info
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > Note: This repository is outdated and uses an obsolete version of amplify. Please see the docs at https://aws-amplify.github.io/docs/js/authentication for the updated CLI.
2 |
3 | Get going with `yarn install && yarn start`
4 |
5 | # Barebones Authentication with AWS-Amplify
6 |
7 | This is meant to demonstrate how to create an application AWS-Amplify with a focus on Authentication.
8 |
9 | Some pages will require authentication, other will not require it.
10 |
11 | Please note that I will be assuming familiarity with react and react-router. I will be assuming that you may be fairly new to AWS and the Amplify library, so we will touch on those elements in more detail.
12 |
13 | ## Application and AWS Setup
14 |
15 | 1. First, let's create an app. We'll be using create-react-app for this walkthrough. If you don't have it already you can install it by running `yarn global add create-react-app` in a terminal window. Once `create-react-app` is installed, run the following command to create a new application.
16 |
17 | `create-react-app amplify-auth-examples`
18 |
19 | `cd amplify-auth-examples`
20 |
21 | 2. Next, we want to initialize our project using the `awsmobile` CLI. If you don't already have it, you can install by running `npm install -g awsmobile` in a terminal window.
22 |
23 | Before we go forward, we need to setup a user to act as the admin for our project so the CLI can access the things it needs (e.g. permissions to provision AWS services for us on our behalf). Head over to [AWS IAM Management](https://console.aws.amazon.com/iam/home?region=us-east-1#/home) and login with your AWS credentials.
24 |
25 | 3. Next, click on `Users` on the left-hand menu. Click the `Add User` button at the top. Enter a username, like `amplify-auth-examples-admin` and select the checkbox next to `Programmatic access` for the `Access Type`. Click `Next`.
26 |
27 | 4. On the next screen, click the `Create group` button beneath the `Add user to group` heading. For the `group name`, enter `AuthAdministrator` or something similar and select the checkbox next to the `AdministratorAccess` item from the list. Click `Create group` at the bottom of the screen to confirm and close the dialog. Click the `Next:Review` button at the bottom.
28 |
29 | 5. On the next screen, click the `Create user` button.
30 |
31 | 6. You should see a success message at the top of the page confirming that our user was created successfully. At this point, you can download the CSV using the button provided, or copy the `Access key ID` and `Secret access key` elsewhere. We will need them in just a minute.
32 |
33 | 7. Back in your terminal, run the following command in the terminal to initialize `awsmobile`:
34 |
35 | `awsmobile init`
36 |
37 | This will prompt you multiple times, and you can press enter to accept the defaults for each of the first four prompts. After these prompts, it will say `missing aws account settings`. It will ask if you want to configure aws account settings. Type `Y` to accept.
38 |
39 | This is where the `Access key ID` and `Secret access key` come into play. Paste the `Access key ID` and `Secret access key` when it promps for it. (If you don't have them or you mess up, you can just press `Ctrl + C` (`Cmd` on OSX) to exit and try again.) Use the arrow keys to select `us-east-1` for the region and press enter. It will ask what name you would like to use for the project. Press enter to accept the default or choose your own. It will take a second to initialize your project and leave you back at the command-line.
40 |
41 | 8. Next, we need to initailize `user-signin` to allow us to interact with AWS Cognito and provide authentication functionality for our users. Run the following commands in the terminal.
42 |
43 | `awsmobile user-signin enable`
44 |
45 | Press enter to accept the default.
46 |
47 | `awsmobile push`
48 |
49 | This command will sync your local configuration changes to your project in [Mobile Hub](https://console.aws.amazon.com/mobilehub/home?region=us-east-1#/).
50 |
51 | Just to make sure everything is working, let's test the app by running `yarn start` at the command line. The application should start and you should see the default `create-react-app` splash page.
52 |
53 | ## Home Page & Header Links
54 |
55 | Next, we need to install `react-router`, `react-router-dom`, and the `aws-amplify-react` library.
56 |
57 | `yarn add react-router react-router-dom aws-amplify-react`
58 |
59 | These get our libraries out of the way. We'll be focusing on `App.js` from now on. Open that file and delete everything inside the `div` tag with the `className` of `App`. In its place, just put an `
` tag with the words `Home Page`. You can delete the `logo` import as well. Your `App.js` should look like this:
60 |
61 | ```javascript
62 | import React, { Component } from 'react';
63 | import './App.css';
64 |
65 | class App extends Component {
66 | render() {
67 | return (
68 |
69 |
Amplify Routes Example
70 |
71 | );
72 | }
73 | }
74 |
75 | export default App;
76 | ```
77 |
78 | We're not going to worry about creating separate files. I'll leave that as an exercise for the reader. :P
79 |
80 | Next, we'll add a simple routes structure to demonstrate having different pages. Since I'm assuming some familiarity with `react-router`, I'm not going to go as in depth on these. If you need a primer, you can check out [this article](https://medium.com/@mwarger/one-router-to-rule-them-all-c073f5d66361) or the [official docs](https://reacttraining.com/react-router/).
81 |
82 | Add the `Link` and `Router` imports from `react-router-dom` to the imports section on the page.
83 |
84 | `import { Link, BrowserRouter as Router } from 'react-router-dom';`
85 |
86 | Beneath the imports, add the following functional component.
87 |
88 | ```javascript
89 | const HeaderLinks = props => (
90 |
91 |
92 | Home
93 |
94 |
95 | Create Account/Login
96 |
97 |
98 | Secret Page
99 |
100 |
101 |
102 | About Page (we don't care if you're logged in or not)
103 |
104 |
105 |
106 | );
107 | ```
108 |
109 | Add the declaration for this component beneat the `h1` in our `App` declaration. We also need to add our `Router` component so `react-router` can compose the `Link` component properly.
110 |
111 | ```javascript
112 | class App extends Component {
113 | render() {
114 | return (
115 |
116 |
Amplify Routes Example
117 |
118 |
119 |
120 | );
121 | }
122 | }
123 |
124 | const AppWithRouter = () => (
125 |
126 |
127 |
128 | );
129 |
130 | export default AppWithRouter;
131 | ```
132 |
133 | ## Routes
134 |
135 | Now let's add some router outlet to our application. This will be the place where our different route components get injected in the page when we click on our header links.
136 |
137 | Create a `Routes` functional component with the following code. Place it right above the `App` declaration.
138 |
139 | ```javascript
140 | class AuthComponent extends Component {
141 | render() {
142 | return
} />
156 |
157 | );
158 | ```
159 |
160 | We will come back to the `AuthComponent` component in a bit to implement our authentication with `aws-amplify`.
161 |
162 | Place the declaration for this beneath the `HeaderLinks` declaration in the `App`.
163 |
164 | ```javascript
165 | class App extends Component {
166 | render() {
167 | return (
168 |
169 |
Amplify Routes Example
170 |
171 |
172 |
173 |
174 |
175 | );
176 | }
177 | }
178 | ```
179 |
180 | If you run your app now, you should see that clicking on the links load the different pieces of content we have defined for each of our routes. However, we want the secret page to only be accessible to logged in users, and if someone tries to access it, they should be redirected to the `auth` page to login or create an account. Let's implement this functionality.
181 |
182 | ## Protected Routes
183 |
184 | We're going to implement a `ProtectedRoute` component that prevents the user from accessing content unless they're logged in. This will follow the same structure as a `Route` from `react-router`; we will just be passing some additional information to it.
185 |
186 | 1. Right above the `AuthComponent` component declaration, create a `ProtectedRoute` component with the same `Route` as our secret route.
187 |
188 | ```javascript
189 | const ProtectedRoute = props => (
190 |
Keep it secret! Keep it safe!
}
194 | />
195 | );
196 | ```
197 |
198 | 2. Because we will be using this in place of our `Route`, the elements that our specific to our component will be passed in. This means that we will replace them with the props of the component.
199 |
200 | ```javascript
201 | const ProtectedRoute = props => (
202 |
203 | );
204 | ```
205 |
206 | 3. Replace the `Route` declaration for the secret page in our `Routes` list and you should see that everything is working the same way as before.
207 |
208 | ```javascript
209 | const Routes = () => (
210 |
211 |
Home
} />
212 |
213 |
Keep it secret! Keep it safe!
}
217 | />
218 |
About Content
} />
219 |
220 | );
221 | ```
222 |
223 | 4. We have successfully replaced our `Route` with our `ProtectedRoute`! Now we can work to implement the logic to redirect if the user is not logged in. This will take one more change to our `App` component and the `Routes` component. Above the `render` method of the `App` component, declare the following state object so we can track whether the user is logged in.
224 |
225 | ```
226 | state = {
227 | authState: {
228 | isLoggedIn: false
229 | }
230 | };
231 | ```
232 |
233 | 5. Let's pass this to our `Routes` component in our `App` as an `authState` prop.
234 |
235 | ```javascript
236 | ...
237 |
238 |
Amplify Routes Example
239 |
240 |
241 |
242 |
243 |
244 | ...
245 | ```
246 |
247 | 6. Next, update our `Routes` declaration to accept these new props and pass it down to our `ProtectedRoute`.
248 |
249 | ```javascript
250 | const Routes = ({ authState }) => (
251 |
252 |
Home
} />
253 |
254 |
Keep it secret! Keep it safe!
}
258 | props={authState}
259 | />
260 |
About Content
} />
261 |
262 | );
263 | ```
264 |
265 | 7. Now we need to implement our redirect. If the user is logged in, we want to render the component we passed in. If the user is not logged in, we want to redirect them to the auth page and store the redirect URL so we can let them to continue to the intended page after they have logged in.
266 |
267 | Add the `Redirect` import to our `react-router-dom` import at the top of `App.js`.
268 |
269 | `import { Link, BrowserRouter as Router, Switch, Route, Redirect } from 'react-router-dom';`
270 |
271 | 8. Update the ProtectedRoute declaration to handle our new state that's passed in.
272 |
273 | ```javascript
274 | const ProtectedRoute = props => (
275 |
279 | props.props.isLoggedIn ? (
280 |
281 | ) : (
282 |
287 | )
288 | }
289 | />
290 | );
291 | ```
292 |
293 | If you try your app now, you should see that clicking the Secret Page link should change the URL and content to render the `auth` route, while keeping the redirect set to the `secret` route. Change the `isLoggedIn` state to true to test what happens if the user `isLoggedIn`. This time, you should pass straight to the `secret` route without any redirect.
294 |
295 | 9. Finally, we need to clean up from our component evolution. It works as is, but we can use destructuring to clarify our meaning and make it look prettier. Let's start by descturing out the props that we care about.
296 |
297 | ```javascript
298 | const ProtectedRoute = ({ render: C, props: childProps, ...rest }) => (
299 | ...
300 | ```
301 |
302 | Here, we have taken the `render` prop that contains our `div`, and rename it to `C`. This is the Component that we eventually want to render. We rename `props` to `childProps` because we're going to pass it to the child component that we will eventually render (in this case, `C`). Finally, we take all the other props and assign them to the `rest` variable using the spread operator.
303 |
304 | The following snippet is the entire component. It uses the `C` declaration to create a component and spread the `rProps` from the `render` prop and the `childProps` from our parent component so that our final rendered component has everything that we passed down to begin with. The redirect is the same as before.
305 |
306 | ```javascript
307 | const ProtectedRoute = ({ render: C, props: childProps, ...rest }) => (
308 |
311 | childProps.isLoggedIn ? (
312 |
313 | ) : (
314 |
319 | )
320 | }
321 | />
322 | );
323 | ```
324 |
325 | This gives us a good foundation to implement our `AuthComponent` component using `aws-amplify`. Let's continue.
326 |
327 | ## Authentication
328 |
329 | Let's change our focus to the AuthComponent component we stubbed our earlier. It should look like this.
330 |
331 | ```javascript
332 | class AuthComponent extends Component {
333 | render() {
334 | return
Auth Section
;
335 | }
336 | }
337 | ```
338 |
339 | That's fairly simple. Let's add the basic `aws-amplify` `Authenticator` component. We have to add it to the imports at the top of the page.
340 |
341 | `import { Authenticator } from 'aws-amplify-react';`
342 |
343 | Replace our `Auth Section` text in the `AuthComponent` with the `Authenticator` declaration.
344 |
345 | ```javascript
346 | class AuthComponent extends Component {
347 | render() {
348 | return (
349 |
350 |
351 |
352 | );
353 | }
354 | }
355 | ```
356 |
357 | You should see the component on the page when you go to the auth route.
358 |
359 | 
360 |
361 | The nice part about Amplify is that this is all there is to it. We now have Authentication available to us in a simple way. However, we are diverging from the more common use case of wrapping our entire app with this component. This will enable us to have certain sections of the app where the user need not be logged in to interact with our application. We want to be able to communicate our authenticated state to the rest of the app. This is where the amplify library can also help us. It provides a hook for handling the change of state that occurs with this component. It's called `onStateChange`. Let's implement that now.
362 |
363 | Inside your `AuthComponent` component declaration, create a method called `handleStateChange`. This is what will take in the state of the component and allow us to check against it. The code for this method is below.
364 |
365 | ```javascript
366 | class AuthComponent extends Component {
367 | handleStateChange = state => {
368 | if (state === 'signedIn') {
369 | // handle state change
370 | }
371 | };
372 | render() {
373 | return (
374 |
375 |
376 |
377 | );
378 | }
379 | }
380 | ```
381 |
382 | Update the `Authenticator` to use this method.
383 |
384 | ```javascript
385 | class AuthComponent extends Component {
386 | handleStateChange = state => {
387 | if (state === 'signedIn') {
388 | // handle state change
389 | }
390 | };
391 | render() {
392 | return (
393 |
394 |
395 |
396 | );
397 | }
398 | }
399 | ```
400 |
401 | This should be all we need to enable the creation of a user. However, if you try, you will get an error that says `No userPool`. This is because we have not yet told the amplify library to use the configuration we setup earlier. Let's do that now.
402 |
403 | Import the `Amplify` and `Auth` libraries from `aws-amplify`.
404 |
405 | `import Amplify, { Auth } from 'aws-amplify';`
406 |
407 | We need to also import the aws_exports configuration that was created for us when we ran `awsmobile init`.
408 |
409 | `import aws_exports from './aws-exports';`
410 |
411 | Now, we can use that configuration file to configure the `Amplify` library. Add the following line just below the imports at the top of `App.js`.
412 |
413 | `Amplify.configure(aws_exports);`
414 |
415 | With this out of the way, head over to the auth page and use the Authenticator to create a user. The default settings have multi-factor authentication (MFA) turned on, so you'll go through a couple text messages to sign up and login, and if successful, you should see a greeting like this.
416 |
417 | 
418 |
419 | Success!
420 |
421 | Let's add a console log statement to our `handleStateChange` function to see what this component is doing.
422 |
423 | ```javascript
424 | ...
425 | handleStateChange = state => {
426 | console.log(state);
427 | if (state === 'signedIn') {
428 | // handle state change
429 | }
430 | };
431 | ```
432 |
433 | Try logging in, and watch the console. You should see `confirmSignIn` and `signedIn` events that are passed to our `handleStateChange` function.
434 |
435 | Now that we can get access to our state, let's implement the hook that allows us to tell the rest of the app when we are logged in.
436 |
437 | Recall from earlier that we have our `isLoggedIn` state available at the application level. This is what we can pass to our components to let us know if the user is logged in. We need to be able to update this piece of state, so we need to pass a function to our `AuthComponent` to let us do this.
438 |
439 | 1. In the App component, create a function called `handleUserSignIn`.
440 |
441 | ```javascript
442 | handleUserSignIn = () => {
443 | this.setState({ authState: { isLoggedIn: true } });
444 | };
445 | ```
446 |
447 | 2. Replace the render method with the following snippet. This will collect our multiple props into a childProps object that we can pass into our routes.
448 |
449 | ```javascript
450 | render() {
451 | const childProps = {
452 | isLoggedIn: this.state.authState.isLoggedIn,
453 | onUserSignIn: this.handleUserSignIn
454 | };
455 |
456 | return (
457 |
458 |
Amplify Routes Example
459 |
460 |
461 |
462 |
463 |
464 | );
465 | }
466 | ```
467 |
468 | 3. Before we can pass our function to our auth route, we need to create a route that accepts props. This will be very similar to our authenticated route, but we won't care whether the user is logged in or not. Create a new functional component called `ProppedRoute` right beneat the `ProtectedRoute`.
469 |
470 | ```javascript
471 | const ProppedRoute = ({ render: C, props: childProps, ...rest }) => (
472 | } />
473 | );
474 | ```
475 |
476 | 4. Update the `Routes` component to accept the new `childProps` prop and pass it to the auth route, which we have changed to be our new `ProppedRoute` component.
477 |
478 | ```javascript
479 | const Routes = ({ childProps }) => (
480 |
481 |
Home
} />
482 |
488 |
Keep it secret! Keep it safe!
}
492 | props={childProps}
493 | />
494 |
About Content
} />
495 |
496 | );
497 | ```
498 |
499 | 5. Now that we have a function we can use to alert the app that we're signed in, let's wire it up. In the `AuthComponent`, replace the `//handle state change` comment with a call to the `onUserSignIn` function prop.
500 |
501 | ```javascript
502 | handleStateChange = state => {
503 | console.log(state);
504 | if (state === 'signedIn') {
505 | this.props.onUserSignIn();
506 | }
507 | };
508 | ```
509 |
510 | 6. To check that it worked, add this snippet beneath the `HeaderLinks` component to let us know the status of the user. Sign out and refresh the app. Head to the auth page and sign in. You should see the text change from 'Not Logged In' to 'User is Logged In', confirming that our state update took place!
511 |
512 | ```javascript
513 |
123 | );
124 | }
125 | }
126 |
127 | const AppWithRouter = () => (
128 |
129 |
130 |
131 | );
132 |
133 | export default AppWithRouter;
134 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/src/aws-exports.js:
--------------------------------------------------------------------------------
1 | // WARNING: DO NOT EDIT. This file is Auto-Generated by AWS Mobile Hub. It will be overwritten.
2 |
3 | // Copyright 2017-2018 Amazon.com, Inc. or its affiliates (Amazon). All Rights Reserved.
4 | // Code generated by AWS Mobile Hub. Amazon gives unlimited permission to
5 | // copy, distribute and modify it.
6 |
7 | // AWS Mobile Hub Project Constants
8 | const awsmobile = {
9 | // this should be replaced with your own config
10 | };
11 |
12 | export default awsmobile;
13 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import registerServiceWorker from './registerServiceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 | registerServiceWorker();
9 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | // In production, we register a service worker to serve assets from local cache.
2 |
3 | // This lets the app load faster on subsequent visits in production, and gives
4 | // it offline capabilities. However, it also means that developers (and users)
5 | // will only see deployed updates on the "N+1" visit to a page, since previously
6 | // cached resources are updated in the background.
7 |
8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
9 | // This link also includes instructions on opting out of this behavior.
10 |
11 | const isLocalhost = Boolean(
12 | window.location.hostname === 'localhost' ||
13 | // [::1] is the IPv6 localhost address.
14 | window.location.hostname === '[::1]' ||
15 | // 127.0.0.1/8 is considered localhost for IPv4.
16 | window.location.hostname.match(
17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
18 | )
19 | );
20 |
21 | export default function register() {
22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
23 | // The URL constructor is available in all browsers that support SW.
24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
25 | if (publicUrl.origin !== window.location.origin) {
26 | // Our service worker won't work if PUBLIC_URL is on a different origin
27 | // from what our page is served on. This might happen if a CDN is used to
28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
29 | return;
30 | }
31 |
32 | window.addEventListener('load', () => {
33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
34 |
35 | if (isLocalhost) {
36 | // This is running on localhost. Lets check if a service worker still exists or not.
37 | checkValidServiceWorker(swUrl);
38 |
39 | // Add some additional logging to localhost, pointing developers to the
40 | // service worker/PWA documentation.
41 | navigator.serviceWorker.ready.then(() => {
42 | console.log(
43 | 'This web app is being served cache-first by a service ' +
44 | 'worker. To learn more, visit https://goo.gl/SC7cgQ'
45 | );
46 | });
47 | } else {
48 | // Is not local host. Just register service worker
49 | registerValidSW(swUrl);
50 | }
51 | });
52 | }
53 | }
54 |
55 | function registerValidSW(swUrl) {
56 | navigator.serviceWorker
57 | .register(swUrl)
58 | .then(registration => {
59 | registration.onupdatefound = () => {
60 | const installingWorker = registration.installing;
61 | installingWorker.onstatechange = () => {
62 | if (installingWorker.state === 'installed') {
63 | if (navigator.serviceWorker.controller) {
64 | // At this point, the old content will have been purged and
65 | // the fresh content will have been added to the cache.
66 | // It's the perfect time to display a "New content is
67 | // available; please refresh." message in your web app.
68 | console.log('New content is available; please refresh.');
69 | } else {
70 | // At this point, everything has been precached.
71 | // It's the perfect time to display a
72 | // "Content is cached for offline use." message.
73 | console.log('Content is cached for offline use.');
74 | }
75 | }
76 | };
77 | };
78 | })
79 | .catch(error => {
80 | console.error('Error during service worker registration:', error);
81 | });
82 | }
83 |
84 | function checkValidServiceWorker(swUrl) {
85 | // Check if the service worker can be found. If it can't reload the page.
86 | fetch(swUrl)
87 | .then(response => {
88 | // Ensure service worker exists, and that we really are getting a JS file.
89 | if (
90 | response.status === 404 ||
91 | response.headers.get('content-type').indexOf('javascript') === -1
92 | ) {
93 | // No service worker found. Probably a different app. Reload the page.
94 | navigator.serviceWorker.ready.then(registration => {
95 | registration.unregister().then(() => {
96 | window.location.reload();
97 | });
98 | });
99 | } else {
100 | // Service worker found. Proceed as normal.
101 | registerValidSW(swUrl);
102 | }
103 | })
104 | .catch(() => {
105 | console.log(
106 | 'No internet connection found. App is running in offline mode.'
107 | );
108 | });
109 | }
110 |
111 | export function unregister() {
112 | if ('serviceWorker' in navigator) {
113 | navigator.serviceWorker.ready.then(registration => {
114 | registration.unregister();
115 | });
116 | }
117 | }
118 |
--------------------------------------------------------------------------------