Статьи

Интеграция Amazon Cognito с одностраничным приложением (Vue.js)

В этой статье мы рассмотрим аутентификацию одностраничного приложения (созданного с использованием Vue.js) в Amazon Cognito с использованием протокола OAuth. В нашей предыдущей статье мы интегрировали серверное приложение с Amazon Cognito.

Строительные леса одностраничного приложения

Мы будем использовать vue-cli для создания пустого приложения Vuejs. Vue CLI можно установить, следуя инструкциям здесь .

Давайте создадим пустое приложение под названием aws-cognito-spa-demo , выполнив следующую команду:

1
vue create aws-cognito-spa-demo

Вам будет предложено выбрать плагины

Amazon Cognito

После того, как приложение было создано, вы можете перейти в этот каталог и выполнить команду для запуска приложения.

1
2
3
cd aws-cognito-spa-demo
npm instal
npm run serve

Приложение будет запущено по адресу http: // localhost: 8080.

Установка дополнительных зависимостей

Мы установим необходимые пакеты узлов, которые мы будем использовать для приложения:

1
2
3
4
npm install --save amazon-cognito-auth-js
npm install --save amazon-cognito-identity-js
npm install --save vue-router
npm install --save axios

Создание нового клиента приложения в Amazon Cognito

Мы создадим новый клиент приложения под названием test-spa-client из консоли Amazon Cognito, как показано ниже:

Amazon Cognito

Обновите настройки для созданного клиента, перейдя в «Настройки клиента приложения», указав значения URL-адреса обратного вызова, URL-адреса выхода из системы, разрешенного потока OAUth и областей OAuth:

Amazon Cognito

Мы используем Implicit Grant в качестве потока OAuth для приложений SPA.

Создание переменных среды

Мы будем хранить настройки, связанные с Amazon Cognito, в файлах свойств, а Vue CLI сделает их доступными среди переменных среды во время выполнения приложения. Подробнее об определении переменных среды в приложениях Vue JS можно найти здесь .

Мы будем хранить общие настройки приложения, такие как URI перенаправления cognito, URI .env файле .env и некоторые локальные настройки в .env.local. Файлы .env. *. Local и .env.local игнорируются в git. Таким образом, вы не фиксируете локальные настройки для контроля версий.

1
2
3
4
# In .env
VUE_APP_COGNITO_REDIRECT_URI=http://localhost:8080/login/oauth2/code/cognito
VUE_APP_COGNITO_REDIRECT_URI_SIGNOUT=http://localhost:8080/logout
VUE_APP_APP_URL=http://localhost:8080

Тогда следующее в .env.local:

1
2
3
VUE_APP_COGNITO_USERPOOL_ID=<cognito userpool id>
VUE_APP_COGNITO_APP_DOMAIN=<cognito app domain>
VUE_APP_COGNITO_CLIENT_ID=<app client id>

Создание пользовательского информационного магазина

Мы будем использовать глобальный объект JSON для хранения зарегистрированной пользовательской информации. Это альтернативный подход к использованию Vuex . Давайте создадим объект JSON в src/app/user-info-store.js :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var state = {
  cognitoInfo: {},
  loggedIn: false,
  loadingState: true,
  errorLoadingState: false
}
 
function setLoggedIn(newValue) {
  state.loggedIn = newValue;
}
 
function setLoggedOut() {
  state.loggedIn = false;
  state.cognitoInfo = {};
}
 
function setCognitoInfo(newValue){
  state.cognitoInfo = newValue;
}
 
export default {
  state: state,
  setLoggedIn: setLoggedIn,
  setLoggedOut: setLoggedOut,
  setCognitoInfo: setCognitoInfo
}

Оболочка для Amazon Cognito API

Давайте создадим оболочку src/app/auth.js для Amazon Cognito API, которая облегчит такие операции, как создание объекта CognitoAuth , login, logout:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/* eslint-disable */
import {CognitoAuth, StorageHelper} from 'amazon-cognito-auth-js';
import IndexRouter from '../router/index';
import UserInfoStore from './user-info-store';
import UserInfoApi from './user-info-api';
 
 
const CLIENT_ID = process.env.VUE_APP_COGNITO_CLIENT_ID;
const APP_DOMAIN = process.env.VUE_APP_COGNITO_APP_DOMAIN;
const REDIRECT_URI = process.env.VUE_APP_COGNITO_REDIRECT_URI;
const USERPOOL_ID = process.env.VUE_APP_COGNITO_USERPOOL_ID;
const REDIRECT_URI_SIGNOUT = process.env.VUE_APP_COGNITO_REDIRECT_URI_SIGNOUT;
const APP_URL = process.env.VUE_APP_APP_URL;
 
var authData = {
    ClientId : CLIENT_ID, // Your client id here
    AppWebDomain : APP_DOMAIN,
    TokenScopesArray : ['openid', 'email'],
    RedirectUriSignIn : REDIRECT_URI,
    RedirectUriSignOut : REDIRECT_URI_SIGNOUT,
    UserPoolId : USERPOOL_ID,
}
 
var auth = new CognitoAuth(authData);
auth.userhandler = {
    onSuccess: function(result) {
        console.log("On Success result", result);
        UserInfoStore.setLoggedIn(true);
        UserInfoApi.getUserInfo().then(response => {
            IndexRouter.push('/');
        });
         
         
    },
    onFailure: function(err) {
        UserInfoStore.setLoggedOut();
        IndexRouter.go({ path: '/error', query: { message: 'Login failed due to ' + err } });
    }
};
 
function getUserInfoStorageKey(){
    var keyPrefix = 'CognitoIdentityServiceProvider.' + auth.getClientId();
    var tokenUserName = auth.signInUserSession.getAccessToken().getUsername();
    var userInfoKey = keyPrefix + '.' + tokenUserName + '.userInfo';
    return userInfoKey;
}
 
var storageHelper = new StorageHelper();
var storage = storageHelper.getStorage();
export default{
    auth: auth,
    login(){
        auth.getSession();
    },
    logout(){
        if (auth.isUserSignedIn()) {
            var userInfoKey = this.getUserInfoStorageKey();
            auth.signOut();
 
            storage.removeItem(userInfoKey);
        }
    },
    getUserInfoStorageKey,
 
}

Получение информации о пользователе от Amazon Cognito

После аутентификации мы можем использовать токен доступа для получения информации о пользователе, вошедшем в систему. Для этого нам нужно будет выполнить GET-запрос к конечной точке: https://<app domain>/oauth2/userInfo . Мы создали служебный метод getUserInfo() в src/app/user-info.js как показано ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
import axios from 'axios';
import auth from './auth';
 
 
export default
    getUserInfo(){
        var jwtToken = auth.auth.getSignInUserSession().getAccessToken().jwtToken;
        const USERINFO_URL = 'https://'+auth.auth.getAppWebDomain() + '/oauth2/userInfo';
        var requestData = {
            headers: {
                'Authorization': 'Bearer '+ jwtToken
            }
        }
        return axios.get(USERINFO_URL, requestData).then(response => {
            return response.data;
        });
    }
}

Этот API был использован в оболочке Cognito, написанной в разделе выше.

Создание компонентов Vue

Давайте создадим несколько компонентов Vue для:

  • отображая вошедшую в систему информацию о пользователе
  • показывает выход из системы
  • компонент обработки ошибок

Мы будем использовать Vue Router для отображения URL-пути к компонентам Vue. Определения компонентов показаны ниже:

Home компонент

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<template>
    <div class="row">
        <div class="col">
            <h3>Welcome, </h3>
            <div class="alert alert-info">
                {{userInfo}}
            </div>
            <router-link to="/logout">
                Logout
            </router-link>
        </div>
    </div>
</template>
<script>
import UserInfoStore from '../app/user-info-store';
export default {
    name: 'Home',
    data: function() {
        return{
            userInfo: UserInfoStore.state.cognitoInfo
        }
    }
}
</script>
<style>
</style>

Компонент LogoutSuccess :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<template>
<div class="row">
    <div class="col">
        <h2>Logged Out successfully</h2>
        <router-link to="/login">Login</router-link>
    </div>
</div>
</template>
<script>
export default {
    mounted: function(){
         
    }
}
</script>

Компонент ошибки:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
<template>
    <div class="alert alert-danger">
        {{message}}
    </div>
</template>
<script>
export default {
    data: function(){
        return {
            message: ""
        }
    },
    mounted(){
        this.message = this.$route.query.message;
    }
}
</script>

Настройка роутера

Как упоминалось в предыдущем разделе, мы будем использовать Vue Router для сопоставления URL-пути с компонентами Vue. Мы настроим конфигурацию маршрутизатора в router/index.js как показано ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/* eslint-disable */
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home'
import auth from '../app/auth';
import LogoutSuccess from '@/components/LogoutSuccess';
import UserInfoStore from '../app/user-info-store';
import UserInfoApi from '../app/user-info-api';
import ErrorComponent from '@/components/Error';
 
Vue.use(Router)
 
function requireAuth(to, from, next) {
   
  if (!auth.auth.isUserSignedIn()) {
      UserInfoStore.setLoggedIn(false);
      next({
      path: '/login',
      query: { redirect: to.fullPath }
      });
  } else {
    UserInfoApi.getUserInfo().then(response => {
      UserInfoStore.setLoggedIn(true);
      UserInfoStore.setCognitoInfo(response);
      next();
    });
       
  }
}
 
export default new Router({
  mode: 'history',
  base: '/',
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home,
      beforeEnter: requireAuth
    },
    {
      path: '/login', beforeEnter(to, from, next){
        auth.auth.getSession();
      }
    },
    {
      path: '/login/oauth2/code/cognito', beforeEnter(to, from, next){
        var currUrl = window.location.href;
         
        //console.log(currUrl);
        auth.auth.parseCognitoWebResponse(currUrl);
        //next();
      }
    },
    {
      path: '/logout', component: LogoutSuccess,  beforeEnter(to, from, next){
        auth.logout();
        next();
      }
 
    },
    {
      path: '/error', component: ErrorComponent
    }
  ]
})

Мы используем свойство beforeEnter объекта маршрутов для добавления любых предварительных beforeEnter необходимых для визуализации компонента. И в этом свойстве мы проверяем, вошел ли пользователь в систему или не использует созданную нами оболочку Cognito. Таким образом, для путей, которые требуют защиты, мы можем определить свойство beforeEnter .

Созданное приложение по умолчанию имеет компонент App.vue который будет нашим корневым компонентом. Мы используем <router-view/> чтобы указать, что HTML здесь будет основан на компоненте, к которому разрешается путь в конфигурации маршрутизатора.

Итак, наша версия App.vue выглядит так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <div class="contents">
      <router-view/>
    </div>
  </div>
</template>
 
<script>
export default {
  name: 'app'
}
</script>
 
<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Затем мы обновляем src/main.js для ссылки на каталог, который содержит конфигурацию маршрутизатора, как показано ниже:

01
02
03
04
05
06
07
08
09
10
import Vue from 'vue'
import App from './App.vue'
import router from './router'
 
Vue.config.productionTip = false
 
new Vue({
  render: h => h(App),
  router
}).$mount('#app')

Запуск приложения

Вы можете запустить приложение, введя команду: npm run serve . Переход на localhost: 8080 приведет вас к экрану входа в Cognito:

Amazon Cognito

Введите имя пользователя и пароль пользователя, которого вы зарегистрировали в пуле пользователей, или вы даже можете зарегистрироваться для нового пользователя. После входа вы будете перенаправлены обратно в приложение Vue JS:

Amazon Cognito

Ссылка «Выход» приведет к выходу пользователя из системы.

Полный код можно найти в репозитории Github здесь .

Опубликовано на Java Code Geeks с разрешения Мохамеда Санауллы, партнера нашей программы JCG . См. Оригинальную статью здесь: Интеграция Amazon Cognito с одностраничным приложением (Vue.js)

Мнения, высказанные участниками Java Code Geeks, являются их собственными.