2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

aws amplifyの認証にamplifyの外で取得した情報を用いたくて頑張った話

Posted at

aws amplifyで構築しているシステムがありますが、これはとあるシステム(php)のサブシステム的な扱いとなっており、認証自体はそのとあるシステムで行いたいという話があります。

環境

とあるシステム

サブシステム(aws amplify)

やりたいこと

  • とあるシステムで認証済みの状態でサブシステムに遷移してもログイン状態を保持していたい。
  • ただしシステム間は疎結合

やろうと思うこと

  • とあるシステムでの認証時にcognitoへのログインを同時におこない、cognitoのアクセストークンを取得
  • 取得したトークンはcookie(ドメイン x path x SSLでセキュリティ担保)に持つ

それではやってみよう

前置きはおしまい。
今回、 とあるシステム に関してはスルーします。トークンはある前提でamplify側だけの検証です。
ではamplify側の構築からやっていきます。導入するものは authapi ですね。

aws amplify初期設定

知ってる前提、というか自分の検証目的メインの記事なので細かい説明などは行いませんが、簡単にamplifyが何かというと、

firebaseのaws版 です

・・いや、それだとあんまりか。

cognitoやstorageといったaws周りの仕組みをフロントエンドのフレームワークで使い倒せる仕組み

といった所がニアリーイコールですかね。正確には違う所も多いけど。簡単なことをやる分には簡単にやれちゃいます。

基本、この通りにやるとモック的なシステムは構築できます。
https://docs.amplify.aws/start/q/integration/vue
前提として下記が必要

Node.js v10.x or later
npm v5.x or later
git v2.14.1 or later

(※nodeとかがglobalに入ってたりするとsudoが必要なことが多いかも)

$ npm install -g @aws-amplify/cli
$ npm install -g @vue/cli
$ vue create myppap

Vue CLI v4.4.6
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Where do you prefer placing config for Babel, ESLint, etc.? In package.json
? Save this as a preset for future projects? No

$ cd myppap
$ amplify init

Note: It is recommended to run this command from the root of your app directory
? Enter a name for the project myppap
? Enter a name for the environment dev
? Choose your default editor: Visual Studio Code
? Choose the type of app that you're building javascript
Please tell us about your project
? What javascript framework are you using vue
? Source Directory Path:  src
? Distribution Directory Path: dist
? Build Command:  npm run-script build
? Start Command: npm run-script serve
Using default provider  awscloudformation
AWS access credentials can not be found.
? Setup new user No

For more information on AWS Profiles, see:
https://docs.aws.amazon.com/cli/latest/userguide/cli-multiple-profiles.html

? accessKeyId:  ********************
? secretAccessKey:  ****************************************
? region:  ap-northeast-1
Adding backend environment dev to AWS Amplify Console app: xxxxxxxx

$ npm install aws-amplify @aws-amplify/ui-vue aws-amplify-vue
$ cat - << EOS > src/main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import Amplify from 'aws-amplify';

import '@aws-amplify/ui-vue';
import aws_exports from './aws-exports';

Amplify.configure(aws_exports);

new Vue({
          el: '#app',
          router,
          components: { App },
          template: '<App/>'
})
EOS
$ npm run serve

とりあえず、最小限な状態のamplifyはこれで動く。

vuetop.png

次にauthを追加してpublish
https://docs.amplify.aws/start/getting-started/auth/q/integration/vue

$ amplify add auth

Using service: Cognito, provided by: awscloudformation

 The current configured provider is Amazon Cognito.

 Do you want to use the default authentication and security configuration? Default configuration
 Warning: you will not be able to edit these selections.
 How do you want users to be able to sign in? Username
 Do you want to configure advanced settings? No, I am done.
Successfully added resource myppap70d7df9c locally

$ amplify push

これでcognitoのリソースは作られる。
次に普通にsign inをできるようにはしておく。
App.vueのテンプレを下記に。(ほぼtutorial通り)
AmplifyのデフォルトのUIが表示されます。
(※どちらかというとHome.vueに書くべきだったけど・・ここは見逃して。。)

App.vue
<template>
  <div class="home">
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link>
    <amplify-authenticator>
      <!-- The rest of your app code -->
      <amplify-sign-out></amplify-sign-out>
    </amplify-authenticator>
    <router-view/>
  </div>
</template>

次に src/router/router.js に下記の感じにする。
単純に、signin済みなら/aboutに、違えばトップにという動作をするだけ。

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

import { Amplify, Auth } from 'aws-amplify'
import * as AmplifyModules from 'aws-amplify'
import { AmplifyEventBus, AmplifyPlugin } from 'aws-amplify-vue'

Vue.use(AmplifyPlugin, AmplifyModules)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/About',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackchunkname: "about" */ '../views/About.vue'),
    meta: { requiresauth: true}
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

router.beforeEach(async (to, from, next) => {
  if (to.matched.some(record => record.meta.requiresauth)) {
    try {
      await Auth.currentAuthenticatedUser()
      console.log('in');
      next();
    } catch (err) {
      console.log('out');
      next({ path: '/', query: { redirect: to.fullPath }});
    }
  }
});

AmplifyEventBus.$on('authState', info => {
  if (info === 'signedIn') {
    console.log('/about');
    router.push({name: 'About'});
  } else {
    console.log('/');
    router.push({name: 'Home'});
    window.location.href = process.env.BASE_URL;
  }
});

export default router

あと src/views/About.vue には下記の一行を適当に入れておく。

<amplify-sign-out/>

これでrun serveするとこうなる。

vuetop.png

さっそく、 Create acount しておきましょう。
とりあえず、 TestUser:Test1234 にしてしまった。あとで消すのを忘れないようにしないと。

vuetop.png

そんなわけでsign inはできました。
だいぶ簡素化して書いちゃってますが、このあたりの情報はぐぐればいっぱいでてきますので。

cliで認証

さて長い前置きは終わり。
今回はcognitoのユーザープールに対してサインインして、返ってきたアクセストークン(1時間有効)をつかって、amplify上の認証が必要なページを閲覧したいという内容。
で、このアクセストークンを得るところは今回CLIでやってしまう。
コマンド一発ですけどね。なお、awscliは必須です。

admin-initiate-auth
--user-pool-id
--client-id
--auth-flow

user-pool-idとclient-idはsrc/aws-exports.jsに書かれてるやつを使えばOK。
あとcognitoのconsoleを開き、管理 API のユーザー名パスワード認証を有効にする も有効にしないといけない。

aws cognito-idp admin-initiate-auth --user-pool-id ap-northeast-1_7xxxxxxxx --client-id vunghxxxxxxxxxxxxxxxxxx --auth-flow ADMIN_NO_SRP_AUTH  --auth-parameters "USERNAME=TestUser,PASSWORD=Test1234"

で、いろいろと返ってくるけど、ほしいのはIdTokenと、AccessToken、RefreshToken。

aws cognito-idp admin-initiate-auth --user-pool-id ap-northeast-1_7xxxxxxxx --client-id vunghxxxxxxxxxxxxxxxxxx --auth-flow ADMIN_NO_SRP_AUTH  --auth-parameters "USERNAME=TestUser,PASSWORD=Test1234" | jq .AuthenticationResult.IdToken

これで認証はできるしTokenは取れた。
ちなみに、このIdTokenとかにユーザー名とかが入ってたりする。

$ echo ${ACCESSTOKEN} | cut -d'.' -f 2 | base64 -d
{"sub":"11d9f7b0-xxxx-40c6-8ca1-xxxxxxx","event_id":"d604176f-xxxx-4e37-a1d6-xxxxxxxxx","token_use":"access","scope":"aws.cognito.signin.user.admin","auth_time":1595671549,"iss":"https:\/\/cognito-idp.ap-northeast-1.amazonaws.com\/ap-northeast-xxxxxxxx","exp":1595675149,"iat":1595671549,"jti":"c04785f1-575a-48e3-abb8-xxxxxxxx","client_id":"vunxxxxxxxxx","username":"TestUser"}

IdTokenの方にはemailなんかもあった。

Cognitoを手動で組み込む。

で、Tokenを使って /about のページを開きたい。

参考: https://blog.u-chan-chi.com/post/vue-cognito/

$ npm install --save amazon-cognito-identity-js aws-sdk

・・っていろいろやってみてユーザー情報取ったりはできたものの・・AmplifyのAuthと連携するのがうまくいかなかった。。

そんなわけで!

強引にやったりました。

tokenとかはほんとはセッションに埋め込む予定だけど、今回は単純にスクリプトに埋め

const IdToken = 'eyxxxxxxxx'
const AccessToken = 'eyxxxxxxxx'
const RefreshToken = 'eyxxxxxxxx'
const userName = 'TestUser';

そしてこれをlocalStorageにぶっこんでしまう!

import aws_exports from '../aws-exports';
const userName = 'TestUser';
const cognitoClientId = aws_exports.aws_user_pools_web_client_id
const makeKey = (name) => `CognitoIdentityServiceProvider.${cognitoClientId}.${userName}.${name}`;
localStorage.setItem(makeKey('accessToken'), AccessToken);
localStorage.setItem(makeKey('idToken'), IdToken);
localStorage.setItem(makeKey('refreshToken'), RefreshToken);
localStorage.setItem(makeKey('clockDrift'), 1);
localStorage.setItem(`CognitoIdentityServiceProvider.${cognitoClientId}.LastAuthUser`, userName);

なお、このへんから拾った。
https://stackoverflow.com/questions/61644513/setting-aws-amplify-user-session-manually

vuetop1.png

なんかログインはできた。
めっちゃ裏口すね。。
なんか正攻法あったらだれか教えてください。。
というかこれやるなら別アプリ(同じドメイン)で直接localStorageに入れてしまえばいいのかもしれない。

api(appsync)を叩く

最後にこのインチキ臭い方法で入り込んだ状態でapi叩けるか確認しておきますね。

$ amplify add api

Scanning for plugins...
Plugin scan successful
? Please select from one of the below mentioned services: GraphQL
? Provide API name: myppap
? Choose the default authorization type for the API Amazon Cognito User Pool
Use a Cognito user pool configured as a part of this project.
? Do you want to configure advanced settings for the GraphQL API No, I am done.
? Do you have an annotated GraphQL schema? No
? Do you want a guided schema creation? Yes
? What best describes your project: Single object with fields (e.g., “Todo” with ID, name, description)
? Do you want to edit the schema now? Yes
Please edit the file in your editor: /mnt/c/Users/masra/myppap/amplify/backend/api/myppap/schema.graphql
? Press enter to continue


The following types do not have '@auth' enabled. Consider using @auth with @model
         - Todo
Learn more about @auth here: https://docs.amplify.aws/cli/graphql-transformer/directives#auth


GraphQL schema compiled successfully.

そしてamplify pushしてしまう。(mockとかも使えるみたいではあるけど)

$ amplify push

? Do you want to generate code for your newly created GraphQL API Yes
? Choose the code generation language target javascript
? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.js
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes
? Enter maximum statement depth [increase from default if your schema is deeply nested] 2

そしてこのあたり を参考(というかほぼコピペ)にさせていただきつつ、About.vueにTodoアプリのコードを差し込みます。

src/views/About.vue
<template>
  <div class="about">
    <h1>This is an about page</h1>
    <amplify-sign-out/>

    <div class="createTodo">
      <input v-model="name" name="name" />
      <input v-model="description" name="description" />
      <button v-on:click="createTodo()">Create Todo</button>
    </div>
    <template v-for="todo in todos">
      <div>
        <h3>{{todo.name}}</h3>
        <p>{{todo.description}}</p>
      </div>
    </template>
  </div>
</template>

<script>
import Vue from 'vue'
import { API, graphqlOperation} from "aws-amplify"
import { listTodos } from "../graphql/queries"
import { createTodo } from "../graphql/mutations"

export default {
  name: 'Todo',
  data () {
    return {
      todos: [],
      name: "",
      description: ""
    }
  },
  mounted: async function () {
    let todos = await API.graphql(graphqlOperation(listTodos))
    this.todos = todos.data.listTodos.items
  },
  methods: {
    createTodo: async function () {
      if ((this.name === "") || (this.description === "")) return
      const todo = {name: this.name, description: this.description}
      try {
        const todos = [...this.todos, todo]
        this.todos = todos
        this.name = "";
        this.description = "";
        await API.graphql(graphqlOperation(createTodo, {input: todo}))
      } catch (error) {
      }
    }
  },
}
</script>

vuetop1.png

はい、でけたでけたー。わーい。わぁー。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?