1
0

More than 3 years have passed since last update.

【Amplify x Next.jsハマりどころ】type Query fieldに@aws_iamディレクティブをつけても、authRoleにpolicyがアタッチされない

Last updated at Posted at 2021-03-13

この記事は「Amplify x Next.jsハマりどころ」シリーズ第2弾です。
もともとはnoteに書いていたのですが、あまり見てもらえないのとコード記述が読みにくいのでqiitaにせっせと移植中です。

ハマりどころ一覧

  1. postConfirmation lambda triggerでstorage(dynamoDB)にアクセスしようとすると、Circular dependency between resourcesでpushできない
  2. type Query fieldに@aws_iamディレクティブをつけても、authRole(またはunauthRole)にポリシーがアタッチされない <- 今回!
  3. type Mutation filedに@authディレクティブをつけるとOnly one resolver is allowed per field. (Service: AWSAppSync; Status Code: 400; Error Code: BadRequestException;)
  4. redirect_urlを環境ごとに分けたいんですが
  5. S3の画像URLを署名なしで取得したいんですが
  6. aws-exports.jsが.gitignoreの対象になっているためビルド時にエラーになるんですが
  7. モノレポでamplifyバックエンドを共有したいんですが
  8. サブドメインでも認証を維持したいんですが

今回はNo.2について書きます。

※このシリーズでは、まずはエラーを再現し、そのエラーを修正するというチュートリアル的な流れで書いていきます。結論だけ知りたい方は結論まで読み飛ばしてください。

Versions

@aws-amplify/cli: 4.43.0

To Reproduce

Amplify api categoryには、様々なディレクティブが用意されていてgraphql schemaに追加の機能を指定することができます。その中に、@functionディレクティブというものがあり、これはlambdaをAWS AppSync APIのresolverに割り当てることを簡単に実現してくれます。例えばこんな感じ。

# schema.graphql

type Query {
 echo(msg: String): String @function(name: "echofunction-${env}")
}
// ./amplify/backend/function/echofunction/src/index.js

exports.handler = async (event) => {
 return event.arguments.msg
}
// ./page/index.js
import React from  'react'
import { Amplify, API } from 'aws-amplify'
import awsExports from '../src/aws-exports'
import { echo } from '../src/graphql/queries'

Amplify.configure({ ...awsExports, ssr: true })

export default Home() {
  const [msg, setMsg] = useState('')
  useEffect(() => {
    (async () => {
      try {
        const response = await API.graphql({
          query: echo,
          variables: {
            msg: 'hello',
          },
        })
        console.log(response.data.echo)
        setMsg(response.data.echo)
      } catch (error) {
        console.log(error)
      }
    })()
  }, [])

  return (<div>{ msg } < /div>)
}

ところでAmplify apiをcallする際には、必ず

  • API_KEY
  • AWS_IAM
  • OPENID_CONNECT
  • AMAZON_COGNITO_USER_POOLS

という4つのauthorization typesのどれかで認可される必要があります。amplify api add/updateでdefault authorization typeを指定することができ、通常この認可モードでapiが呼ばれます。一般的にはdefaultをAWS_IAM、セカンダリとしてAMAZON_COGNITO_USER_POOLSを使用する場合が多いのではないかと思います。

さて、default authorization type をAWS_IAMにしていた場合、上のAPI.graphql()は401レスポンスを返します。

{
 "errors" : [ {
   "errorType" : "UnauthorizedException",
   "message" : "Permission denied"
 } ]
}

これは、graphql.schemaで記述したトップレベルのtype Queryフィールドに、AWS_IAM認可で実行できるようにする@aws_iamディレクティブをamplifyは自動で付与してくれないためです(ちなみに@aws_iamはamplifyではなくAWS AppSyncで用意されたディレクティブです)。なので以下のようにgraphql.schemaを書き換えてみます。

type Query { 
  echo(msg: String): String @function(name: "echofunction-${env}") @aws_iam
}

これでめでたしめでたしかと思いますが、実はまだclient側では引き続きUnauthorizedExceptionが発生します。

clientからのIAM認可モードでのapiは、amplifyが自動で作成してくれるauthRole(およびunauthRole)を通して呼び出されます。このroleにAppSync APIを実行するpolicyが割り当てられているのですが、@aws_iamディレクティブを使用したフィールドに関しては自動的にpolicyに追加されません。そのため、authRole(またはunauthRole)にQuery.echoを実行する権限がないということになります。

こいつの倒し方

@aws_iamではなく@authディレクティブを使用します。@authディレクティブはAmplifyで追加されたディレクティブで、policyの追加作業を良しなにやってくれます。

type Query { 
  echo(msg: String): String @function(name: "echofunction-${env}")
    @auth(rules: [
      { allow: private, provider: iam },
      { allow: public, provider: iam }
    ])
}

最後に

いやいや、最初から@authディレクティブ使えよ!ってオチなんですが、実は@authディレクティブを@modelと一緒にしか使えない時代があったんです。なので、上の方法でcliに怒られた場合、@aws-amplify/cliのバージョンを上げてみてください。

https://github.com/aws-amplify/amplify-cli/issues/2701
https://github.com/aws-amplify/amplify-cli/pull/3590

1
0
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
1
0