32
20

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+IAMで未認証ユーザでもAppSyncを使えるようにする

Posted at

背景

AWS Amplify CLIでバックエンドの自動構築をしようとすると、APIの認証タイプを選ぶとき(Choose an authorization type for the API)に「API KEY」か「Amazon Cognito User Pool」しか選べません。

? Please select from one of the below mentioned services GraphQL
? Choose an authorization type for the API 
❯ API key 
  Amazon Cognito User Pool 

「Amazon Cognito User Pool」を選択すると認証していないユーザはAppSyncを利用できません。
未認証ユーザがAppSyncを使えるようにするためにはAPIの認証タイプにIAMを選択する必要があります。
Amplify CLIを使わずに各リソースを手動で設定することで、認証ユーザと未認証ユーザの両方がAppSyncを使えるようなバックエンド環境の構築を目指します。(ついでにAmplify CLIの裏での挙動を理解したいと思います。)

構成

AWSリソース(バックエンド)

  • Cognito ユーザープール: アプリケーションを利用するユーザの認証に使います。
  • Cognito IDプール: 認証したユーザがどのAWSリソースにアクセスできるのかを管理します。また認証していないユーザのアクセス制限も行います。
    • 今回はAWS AppSyncのアクセス管理を行います。
  • AppSync: GraphQLを用いたDynamoDBへのアクセスのためのAPIとして使います。
    • AuthItemとUnauthItemの2つのタイプを作成し、認証したユーザーは両方にアクセス可能で、未認証ユーザーはUnauthItemのみにアクセスできるようにします。
  • DynamoDB: データソースとして利用します。

フロントエンド

  • AWS Amplify
  • Vue.js

構成図

こんな感じのことを実現したい

認証したユーザ 認証していないユーザ

環境

バックエンド(AWS)

AppSync

認証したユーザのみがアクセスできるAPI(AuthItem)と全てのユーザがアクセスできるAPI(UnauthItem)を作成します。
自動的にクエリとDynamoDBでAuthItem tableとUnauthItem tableが生成されます。

ユーザープール

一人ユーザーを作成しておきます。
パスワードルールや必須情報などは今回はこだわりません。
ただクライアントアプリケーションとしてVue.jsを利用するのでシークレットキーの生成はオフにします。

IDプール

認証されていない ID を有効にしたいので、「認証されていない ID に対してアクセスを有効にする」にチェックをして「プールを作成」します。

ロールの設定

IDプールには「認証されたロール」と「認証されていないロール」が保存されており、ユーザーの承認リクエストのたびにユーザの認証状態に応じてどちらかのロールが自動的に利用されます。
つまり、「認証されたロール」にAuthItemに関するAPIへのアクセス権限が、「認証されていないロール」にはUnauthItemに関するAPIへのアクセス権限を与えます。

ロールにアタッチするポリシー

IAM → ポリシー → ポリシーの作成、から以下のJSONでポリシーを作成します

認証されたロールのためのポリシー

{
 "Version": "2012-10-17",
  "Statement": [
   {
    "Effect": "Allow",
    "Action": [
     "appsync:GraphQL"
     ],
    "Resource": [
     "arn:aws:appsync:[your region]:[account id]:apis/[AppSync API ID]/types/Query/fields/getAuthItems",
     "arn:aws:appsync:[your region]:[account id]:apis/[AppSync API ID]/types/Query/fields/getUnauthItems"
    ]
   }
  ]
}
  • Actionに"appsync:GraphQL"を設定します
  • Resourceにアクセスを許可するAWSリソースをarn形式で追加します
    • /types以降に許可するqueriesを指定できます。(上の例はAuthItemの取得)
      • 例えば認証したユーザにAuthItemの作成を許可したい場合は/types/Mutation/fields/CreateAuthItem
    • 参考: AppSyncのAWS_IAM認証

このポリシーがアタッチされたロールをIDプールの「認証されたロール」に設定します。

認証されていないロールのためのポリシー

{
 "Version": "2012-10-17",
  "Statement": [
   {
    "Effect": "Allow",
    "Action": [
     "appsync:GraphQL"
     ],
    "Resource": [
     "arn:aws:appsync:[your region]:[account id]:apis/[AppSync API ID]/types/Query/fields/getUnauthItems"
    ]
   }
  ]
}

フロントエンド(Vueプロジェクト)

vueプロジェクトを作成し、$ amplify codegen(Amplify codegen)を利用しAppSyncの設定を反映します。

AuthとApiの認証情報の設定

自分が作成したAWSリソースの情報をmain.jsで読み込みます。

main.js
import Amplify from 'aws-amplify';

Amplify.configure({
  "aws_project_region": "XX-XXXXXXXX-X",
  "aws_cognito_identity_pool_id": "XX-XXXXXXXX-X:XXXXXXXXXXXXXXXXXXXXXXXXX",
  "aws_cognito_region": "XX-XXXXXXXX-X",
  "aws_user_pools_id": "XX-XXXXXXXX-X_XXXXXXXX",
  "aws_user_pools_web_client_id": "XXXXXXXXXXXXXXXXXXXXX",
  "oauth": {},
  "aws_appsync_graphqlEndpoint": "https://XXXXXXXXXXXXXXXXXX.appsync-api.XX-XXXXXXXX-X.amazonaws.com/graphql",
  "aws_appsync_region": "XX-XXXXXXXX-X",
  "aws_appsync_authenticationType": "AWS_IAM"
})

"aws_appsync_authenticationType": "AWS_IAM"が今回設定したかった項目ですね

検証

test.js
import * as queries from '@/graphql/queries'
import { Auth, API, graphqlOperation } from 'aws-amplify'

async function test () {
 await Auth.signIn("[test mail address]", "**********") // サインインします
  .then( user => console.log(user)) // CognitoUser {username: "*** ...", ... }

 /* 認証ユーザでAppSyncへアクセス */
 await Auth.currentSession() // JWT Token の取得
  .then( session => console.log(session)) // CognitoUserSesssion {idToken: CognitoIdToken, ... }

 await Auth.currentCredentials() // AWS Credentials の取得
  .then( credentials => console.log(credentials)) //CognitoIdentityCredentials {sessionToken: "**** ...", ...}

 await API.graphql(graphqlOperation(queries.listAuthItems)) // AuthItemの一覧を取得
  .then( items => console.log(items)) // {data: listAuthItems: {items: ...}}

 await API.graphql(graphqlOperation(queries.listUnauthItems)) // UnauthItemの一覧を取得
  .then( items => console.log(items)) // {data: listUnauthItems: {items: ...}}

 await Auth.signOut() // サインアウトします

 /* 未認証ユーザでAppSyncへアクセス */
 try {
  await Auth.currentSession()
  } catch(error) { // 当然 JWT Token の取得はできません
  console.log(error) // No current user
 }

 await Auth.currentCredentials() // サインインしていませんが、AWS Credentials の取得はできます
  .then( credentials => console.log(credentials)) // CognitoIdentityCredentials {sessionToken: "**** ...", ...}

 try {
  await API.graphql(graphqlOperation(queries.listAuthItems))
 } catch(error) { // 権限がないのでAuthItemの取得は失敗します
  console.log(error) // {data: {...}, errors: : Array(1)}
 }

 await API.graphql(graphqlOperation(queries.listUnauthItems)) // 権限があるのでUnauthItemの一覧を取得は成功します
  .then( items => console.log(items)) // {data: listUnauthItems: {items: ...}}
}

感想

最初はAmplify CLIで爆速でアプリケーションを作ってましたが、認証関連でつまったので色々調べてみました。
amplify codegenでcloudformation使わざるを得ない点がちょっとモヤりますが、まだ使いきれてないとこも多いと思うのでドキュメント漁ってみたいと思います。

32
20
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
32
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?