finished tutorial
This commit is contained in:
256
tutorial.md
256
tutorial.md
@@ -1,3 +1,13 @@
|
||||
# AWS amplify
|
||||
|
||||
## Example repo
|
||||
|
||||
Use the following repo as an example/baseline:
|
||||
|
||||
<https://git.panaetius.co.uk/web-development/medium-aws-tutorial-1>
|
||||
|
||||
## Installaing amplify
|
||||
|
||||
Install amplify globally
|
||||
|
||||
`yarn global add @aws-amplify/cli`
|
||||
@@ -6,6 +16,8 @@ Create new amplify app in project
|
||||
|
||||
`amplify init`
|
||||
|
||||
### Amplify commands
|
||||
|
||||
Amplify commands after init
|
||||
|
||||
`amplify status` - will show you what you've added already and if it's locally configured or deployed
|
||||
@@ -14,13 +26,18 @@ Amplify commands after init
|
||||
`amplify console` - to open the Amplify Console and view your project status
|
||||
`amplify publish` - will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud
|
||||
|
||||
`amplify add api` - to create a backend API and then
|
||||
`amplify publish` - to deploy everything
|
||||
`amplify add api` - to create a backend API and then
|
||||
`amplify publish` - to deploy everything
|
||||
|
||||
Install aws amplify dependencies into project
|
||||
`yarn add aws-amplify aws-amplify-vue`
|
||||
|
||||
## Create auth flow
|
||||
|
||||
Add the Amplify instance to Vue instance in `main.js`
|
||||
|
||||
### Auth.js
|
||||
|
||||
Create a new js file in `./src/utils/auth.js` - write the aws amplify code in here.
|
||||
|
||||
You need to write a function for each auth flow. Signup, GetUser etc.
|
||||
@@ -28,3 +45,238 @@ You need to write a function for each auth flow. Signup, GetUser etc.
|
||||
Remember to use
|
||||
`export {getUser, signUp, confirmSignUp, resendSignUp, signIn, signOut}`
|
||||
at the end so it can be imported later.
|
||||
|
||||
You should write a view for each action, login, signup etc.
|
||||
These views should define the UI, and also call the function in the `auth.js`. E.g if you need to sign in, you should write the UI, write any computed methods to verify the form, and write a submit method that takes the form details and calls the `signIn` function from the `auth.js` file.
|
||||
|
||||
In `./utils/auth.js` you can write the functions needed to actually log in with amplify.
|
||||
|
||||
For example, to signUp you can do:
|
||||
|
||||
```javascript
|
||||
function signUp(username, password) {
|
||||
return Auth.signUp({
|
||||
username,
|
||||
password,
|
||||
attributes: {
|
||||
email: username
|
||||
}
|
||||
})
|
||||
.then(data => {
|
||||
AmplifyEventBus.$emit("localUser", data.user);
|
||||
if (data.userConfirmed === false) {
|
||||
AmplifyEventBus.$emit("authState", "confirmSignUp");
|
||||
} else {
|
||||
AmplifyEventBus.$emit("authState", "signIn");
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### router/index.js
|
||||
|
||||
In the `./router/index.js` you should write the router flow.
|
||||
|
||||
#### Define routes
|
||||
|
||||
Each route should set whether they need authentication or not.
|
||||
|
||||
```javascript
|
||||
{
|
||||
path: "/",
|
||||
name: "Home",
|
||||
component: Home,
|
||||
meta: { requiresAuth: true }
|
||||
},
|
||||
```
|
||||
|
||||
#### Additional functionality
|
||||
|
||||
In this file, you can write functions that control the behaviour. For example, you can force anyone logged in to go to the home page only:
|
||||
|
||||
```javascript
|
||||
getUser().then(user => {
|
||||
if (user) {
|
||||
router.push({ path: "/" });
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### Respond to events
|
||||
|
||||
AWS Amplify has the ability to natively `emit` events in your auth flow functions. When you emit these, you can use `AmplifyEventBus` to do something.
|
||||
|
||||
We can push people to different routes based on the sign in flow.
|
||||
|
||||
An example emit:
|
||||
|
||||
```javascript
|
||||
AmplifyEventBus.$emit("authState", "signedIn");
|
||||
```
|
||||
|
||||
An example listener in the router:
|
||||
|
||||
```javascript
|
||||
AmplifyEventBus.$on("authState", async state => {
|
||||
const pushPathes = {
|
||||
signedOut: () => {
|
||||
router.push({ path: "/signIn" });
|
||||
},
|
||||
signUp: () => {
|
||||
router.push({ path: "/signUp" });
|
||||
},
|
||||
confirmSignUp: () => {
|
||||
router.push({ path: "/signUpConfirm" });
|
||||
},
|
||||
signIn: () => {
|
||||
router.push({ path: "/signIn" });
|
||||
},
|
||||
signedIn: () => {
|
||||
router.push({ path: "/" });
|
||||
}
|
||||
};
|
||||
if (typeof pushPathes[state] === "function") {
|
||||
pushPathes[state]();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Managing sign in/out + displaying name
|
||||
|
||||
### Create the Vuex store
|
||||
|
||||
#### state
|
||||
|
||||
Your state should contain 3 items:
|
||||
|
||||
- authorized:bool = flag if logged in or not.
|
||||
- user: str = user object.
|
||||
- userEmail: str = username or email.
|
||||
|
||||
#### mutations
|
||||
|
||||
You should define two mutations (to update this state):
|
||||
|
||||
```javascript
|
||||
mutations: {
|
||||
user(state, user) {
|
||||
state.authorized =
|
||||
!!user && user.attributes && user.attributes.email_verified;
|
||||
state.user = user;
|
||||
},
|
||||
userEmail(state, userEmail) {
|
||||
state.userEmail = userEmail;
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
- When you set the user state you should check to make sure the user object you pass in exists and set the authorized flag at the same time.
|
||||
|
||||
#### actions
|
||||
|
||||
You should define an action that fetches the user. This should call the `getUser` method of whatever auth system you're using and pass this in to the method.
|
||||
|
||||
This `getUser` dispatch should either get the user and set the store state, or set it to null if no user exists. This way, any interaction with `getUser` will make sure the store is up to date.
|
||||
|
||||
```javascript
|
||||
actions: {
|
||||
async fetchUser({ commit }) {
|
||||
try {
|
||||
const user = await Auth.currentAuthenticatedUser();
|
||||
const expires =
|
||||
user.getSignInUserSession().getIdToken().payload.exp -
|
||||
Math.floor(new Date().getTime() / 1000);
|
||||
console.log(`Token expires in ${expires} seconds.`);
|
||||
commit("user", user);
|
||||
commit("userEmail", user.attributes.email);
|
||||
} catch (err) {
|
||||
commit("user", null);
|
||||
commit("userEmail", null);
|
||||
}
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
#### getters
|
||||
|
||||
You should define getters to access the state. These getters will cache - so they will only update if one of the things used to calculate the state updates.
|
||||
|
||||
```javascript
|
||||
getters: {
|
||||
userEmail: state => {
|
||||
return state.userEmail;
|
||||
},
|
||||
authorized: state => {
|
||||
return state.authorized;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Triggering state for apps
|
||||
|
||||
In addition to getting the user to determine router actions, you should get the user in the main component so you can set the state.
|
||||
|
||||
In the main view (parent of all components `App.vue`) you should define a `created` method. This method should:
|
||||
|
||||
- Fetch the user.
|
||||
- Change the state to reflect this.
|
||||
|
||||
```javascript
|
||||
async created() {
|
||||
try {
|
||||
await this.$store.dispatch("fetchUser");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
console.log(this.email);
|
||||
console.log(this.$store.state.userEmail);
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
This needs to be done right at the start to make sure this triggers a state change before anything else.
|
||||
|
||||
In the views for signing in/out/registering etc you should make sure that when you call the auth methods you wrote, that you also update the state at this time.
|
||||
This way, any component in the app can use the state to determine if someone is logged in, and display/toggle components dynamically.
|
||||
|
||||
A signin method for a button might look like:
|
||||
|
||||
```javascript
|
||||
async submit() {
|
||||
if (this.$refs.form.validate()) {
|
||||
console.log(
|
||||
`SIGN IN username: ${this.username}, password: ${this.password}`
|
||||
);
|
||||
try {
|
||||
await signIn(this.username, this.password);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
} finally {
|
||||
this.$store.dispatch("fetchUser");
|
||||
}
|
||||
console.log("Signed in");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A sign out button might look like:
|
||||
|
||||
```javascript
|
||||
async dosignOut() {
|
||||
try {
|
||||
await signOut();
|
||||
} finally {
|
||||
this.$store.dispatch("fetchUser");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Remember:
|
||||
|
||||
- Call `this.$store.dispatch("fetchUser)` when you sign in - do it in a try/finally block to make sure the state gets updated after the ajax call.
|
||||
- Call `this.$store.dispatch("fetchUser)` when you sign out do it in a try/finally bock to make sure the state gets updated after the ajax call.
|
||||
|
||||
Reference in New Issue
Block a user