283 lines
7.5 KiB
Markdown
283 lines
7.5 KiB
Markdown
# 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`
|
|
|
|
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
|
|
`amplify add <category>` - will allow you to add features like user login or a backend API
|
|
`amplify push` - will build all your local backend resources and provision it in the cloud
|
|
`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
|
|
|
|
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.
|
|
|
|
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.
|