What's?

Home / AWS Amplify Auth(Cognito)に Line(OIDC)でログインする方法

AWS Amplify Auth(Cognito)に Line(OIDC)でログインする方法
April 27, 2020

DEVELOPMENT

web
js
aws
cognito
amplify
vue

AmplifyのAuth(Cognito)を使い、Vue−cliのプロジェクトで、Line(OIDC)アカウントでログインをしたい場合の設定方法と実装の仕方です。

AmplifyでLINEのログインのドキュメントが皆無でしたが、他のを参考にしつつ、ダメ元やりましたが、しっかり実装できましたので、ご紹介します。

AWS AmplifyのAuthをLine でログインする

前提

  • Amplifyの導入済み
  • Vue-cliでprojを開始しwelecomeページが表示されている
  • ※注意 LineからEmailを取得するには、申請が必要です。そのため今回はCognito側の設定でEmailを必須にしない設定で進めます
  • 参考 ▶ Line Developer

最初に

  • AmplifyでAuthをadd済みの場合、Cognitoのユーザーの登録内容の必須項目等の編集がCognitoの仕様で不可なので、一回削除します。
$ amplify remove auth
$ rm ./aws-exports.js
$ amplify push

Amplify でAuthを追加する

$ amplify add auth
Using service: Cognito, provided by: awscloudformation

Do you want to use the default authentication and security configuration?  (Use arrow keys)

❯ Default configuration <-これ
Default configuration with Social Provider (Federation)
Manual configuration
I want to learn more.
Do you want to use the default authentication and security configuration?  Default configuration

How do you want users to be able to sign in?  (Use arrow keys)

❯ Username <-これ
Email
Phone Number
Email and Phone Number
I want to learn more.
Do you want to use the default authentication and security configuration?  Default configuration

How do you want users to be able to sign in?  Username
Do you want to configure advanced settings?
No, I am done.
❯ Yes, I want to make some additional changes. <-これ
  • Emailではなくて、Nameにする ※Emailのカーソルで、一回spaceキー、Nameでspaceキーを押す
◯ Gender (This attribute is not supported by Login With Amazon.)
◯ Locale (This attribute is not supported by Facebook, Google.)
◯ Given Name (This attribute is not supported by Login With Amazon.)
❯◯ Name
◯ Nickname (This attribute is not supported by Facebook, Google, Login With Amazon.)
◯ Phone Number (This attribute is not supported by Facebook, Login With Amazon.)
◯ Preferred Username (This attribute is not supported by Facebook, Google, Login With Amazon.)
  • これは何も選択しない
Do you want to enable any of the following capabilities?

※間違えたら一回Control+Cで抜けてやり直せる

  • pushして反映する
$ amplify push

Cotnitoのdomainを設定しておく

  • AWS Consoleから、Cognito,ユーザープールで、amplifyから作成されたユーザープールの設定画面に
  • メニューより、ドメイン名を選択し登録する(何でもよい)

Line Developer設定

Line Developers

  • ログイン後
  • プロバイダ作成
  • Line Loginを選択して、チャンネル作成
  • 登録後、上のタブメニューの「アプリ設定」
  • コールバックURLは先程、設定したCognitoのドメインに下記のPathで設定する
https://先程設定したドメイン/oauth2/idpresponse
  • ※ ここのPATHは指定されたものです。ドメインのみ可変です
  • 参考 ▶ AWS Cognito

CognitoにLineプロバイダを登録

  • Congito ユーザープール、メニューのIDプロバイダーを選択
  • OpenID Connectを選択
  • 各項目を設定する
  • 発行者を入力したら、検出されないが「検出の実行」を押して表示された残りの項目にも下記を入力
項目 内容
プロバイダ名 LINE
クライアントID LineのチャンネルID
クライアントのシークレット(オプション) Lineのチャネルシークレット
属性のリクエストメソッド GET
承認スコープ profile mail openid
発行者 https://access.line.me
識別子 (オプション) なし
認証エンドポイント https://access.line.me/oauth2/v2.1/authorize
トークンエンドポイント https://api.line.me/oauth2/v2.1/token
ユーザー情報エンドポイント https://api.line.me/v2/profile
Jwks uri https://api.line.me/oauth2/v2.1/verify

Lineログインに属性をマッピング

  • OIDCの追加から

    • OIDC 属性: email と sub追加
    • ユーザープール属性: email -> Emailと sub -> Name OIDCの追加

アプリクライアントにIDプロバイダを設定する

  • Cognitoページの「アプリクライアント」
  • アプリクライアントが2つあるので両方に設定します

    • ****appclientWeb
    • ****appclient
  • 有効な ID プロバイダのLINEにチェック
  • コールバック URL(local時、状況により変えて下さい)

  • 許可されている OAuth フロー: Authorization code grantにチェック
  • 許可されている OAuth スコープは全部チェック IDプロバイダを設定
  • ※ 指定のURLには最後にスラッシュを忘れずに!

vue material ui を入れて置く

  • 注意:見た目を整える(ボタンやローティングなど)のに、とりあえず使いました。他のframework使うならここはスキップして下さい
  • インストール
$ yarn add vue-material
  • プロジェクトに反映
~
import VueMaterial from 'vue-material'
import 'vue-material/dist/vue-material.min.css'
import 'vue-material/dist/theme/default.css' <-これ入れないと色つかない
Vue.use(VueMaterial)

import App from './App.vue'
~

Vueのコード

  • 必要なパッケージを入れる
$ yarn add aws-amplify-vue vue-router vuex
  • dir構造
- /
  |- src/
  |-- assets
  |-- components
  |-- views
      |-- Login.vue(ラインのログインボタンのページ)<- 追加
      |-- Top.vue(ログイン後のページ) <- 追加
  |-- App.vue
  |-- aws-exports.js <- 自動で追加
  |-- aws-oauth-config.js <- 追加
  |-- main.js
  |-- router.js <- 追加
  |-- sotre.js <- 追加

不足のファイルやDirは作成しておく

  • aws-oauth-config.js
const oauth = {
  domain: 'xxxxxxxxxxxxx.auth.xx-xxxx-x.amazoncognito.com',
  scope: ['profile', 'email', 'openid', 'aws.cognito.signin.user.admin'],
  redirectSignIn: 'http://localhost:8080/login/',
  redirectSignOut: 'http://localhost:8080/',
  responseType: 'code' // or 'token', note that REFRESH token will only be generated when the responseType is code
}

export default oauth
  • App.vue
<template>
  <div id="app">
    <router-view /> <-- ルーター
  </div>
</template>

<script>

export default {
  name: 'app',
  methods: {
  }
}
</script>

<style>
#app {
}
</style>
  • views/Login.vue
<template>
  <div class="login">
    <!--ログインコンポーネント-->
    <md-button v-if="signined === false && loading === false" class="md-raised" v-on:click="signIn">Sign in LINE</md-button>
    <md-progress-spinner v-if="loading" :md-diameter="30" :md-stroke="3" md-mode="indeterminate"></md-progress-spinner>
    <p v-if="signined">Sign in しました。</p>
  </div>
</template>

<script>
import router from '../router.js'
import Amplify, { Auth, Hub } from 'aws-amplify'
import awsconfig from '../aws-exports'
Amplify.configure(awsconfig)

import oauth from '../aws-oauth-config'

Auth.configure({ oauth })

export default {
  name: 'login',
  data () {
    return {
      signined: false,
      loading: false,
    }
  },
  created: function () {
    Hub.listen('auth', (data) => {
      this.loading = false
      switch (data.payload.event) {
        case 'signIn':
          this.signined = true
          router.push({path: '/'})
          break
        case 'signIn_failure':
          break
        default:
          break
      }

    })
  },
  methods: {
    signIn: async function() {
      this.loading = true
      let res = await Auth.federatedSignIn({provider: 'LINE'})
      console.log(res)
    }
  }
}
</script>
  • views/Top.vue
<template>
  <div>
    Top
    トップページです
    <amplify-sign-out></amplify-sign-out>
  </div>
</template>

<script>
export default {
  name: 'Top'
}
</script>

<style>
</style>
  • main.js
import Vue from 'vue'

import App from './App.vue'
import router from './router.js'
import store from './store.js'

Vue.config.productionTip = false

new Vue({
  store,
  router,
  render: h => h(App),
}).$mount('#app')
  • route.js
import Vue from 'vue'
import Router from 'vue-router'
import Top from './views/Top.vue'
import Login from './views/Login.vue'

import store from './store.js'

// Amplify読み込み
import { AmplifyEventBus, AmplifyPlugin } from 'aws-amplify-vue'
import * as AmplifyModules from 'aws-amplify'

Vue.use(Router)
Vue.use(AmplifyPlugin, AmplifyModules)

let user;

// ユーザー管理
getUser().then((user) => {
  if (user) {
    router.push({path: '/'});
  }
})

function getUser() {
  return Vue.prototype.$Amplify.Auth.currentAuthenticatedUser().then((data) => {
    if (data && data.signInUserSession) {
      store.commit('setUser', data);
      return data;
    }
  }).catch(() => {
    store.commit('setUser', null);
    return null;
  })
}

// ログイン状態管理
AmplifyEventBus.$on('authState', async (state) => {
  if (state === 'signedOut'){
    user = null;
    store.commit('setUser', null);
    router.push({path: '/login'});
  }
  else if (state === 'signedIn') {
    user = await getUser();
    router.push({path: '/'});
  }
})

// ルーティング設定
const router = new Router({
  mode: 'history',
  routes: [
    {
      // ログインページ
      path: '/login',
      name: 'login',
      component: Login
    },
    {
      // トップページ
      path: '/',
      name: 'top',
      component: Top,
      meta: { requiresAuth: true}
    }
  ]
})

// リダイレクト設定
router.beforeResolve(async (to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    user = await getUser();
    if (!user) {
      return next({
        path: '/login'
      })
    }
    return next()
  }
  return next()
})

export default router
  • store.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    user: null
  },
  mutations: {
    // ユーザー情報保存
    setUser(state, user) {
    state.user = user
    },
  },
  actions: {
  },
  modules: {
  }
})

ハマったところ

  • Callbackを最初違うURLにし、違うページを用意していましたが、返ってきたときに、下記のエラーが出てうまくいきませんでした。リフレッシュをすればいいのかもしれませんが、手っ取り早く、リクエストしたページと同じページ(/login)で受け取るようにしたら上手くいきました

    • invalidrequest_
    • invalidgrant_
  • 参考 ▶ AWS Cognito endpoint
  • Amplify からOIDCの設定を自動で行うことは不可能なので、AWS コンソールからamplifyで自動作成されたユーザープールに設定を追加していく形でしたが、amplify側で認識してくれたのでなんとか実装できました。

まとめ

Amplifyでの、OIDC(Line)の実装サンプルがほとんどなく、ダメ元でトライしましたが、うまくいきました。

参考

うかい / 株式会社UKAI
うかい@代表取締役兼エンジニア株式会社UKAI
Nobuyuki Ukai

株式会社UKAI 代表取締役CEO。建築を専攻していたが、新卒でYahoo!Japanにエンジニアとして入社。その後、転職、脱サラ、フリーランスエンジニアを経て、田舎へ移住し、ゲストハウスの開業、法人成り。ゲストハウス2軒、焼肉店、カフェ、食品製造業を運営しています。詳細はこちらから

🙏よかったらシェアお願いします🙏
WRITTEN BY
うかい / 株式会社UKAI
うかい@代表取締役兼エンジニア株式会社UKAI

Nobuyuki Ukai

株式会社UKAI 代表取締役CEO。建築を専攻していたが、新卒でYahoo!Japanにエンジニアとして入社。その後、転職、脱サラ、フリーランスエンジニアを経て、田舎へ移住し、ゲストハウスの開業、法人成り。ゲストハウス2軒、焼肉店、カフェ、食品製造業を運営しています。詳細はこちらから

CONACT
入力して下さい
Slack からもどうぞ

※お気軽にどうぞ!(6月20日まで有効!お早めに)

COPYRIGHT © 2020 UKAI CO., LTD. ALL RIGHTS RESERVED.