AWS AppSyncでCognito User Pools認証を利用する

「AppSyncでGraphQL簡単に使える」ってことだったので、最近仲良くなろうと頑張ってます。 SaaSとかだとAPIキーではなくCognito User PoolsでAPIの認証かけたいよねってなるので、トライした […]

広告ここから
広告ここまで

目次

    「AppSyncでGraphQL簡単に使える」ってことだったので、最近仲良くなろうと頑張ってます。

    SaaSとかだとAPIキーではなくCognito User PoolsでAPIの認証かけたいよねってなるので、トライした覚書です。

    まとめ

    • GraphQL APIのSettingsでAuthorization typeをCognito UserPoolsに変えればOK
    • defaultActionをDENYにするとスキーマ毎に制御できる
    • マネコンでテストするときにCognito User PoolsのClient IDいるから要注意

    AWS CDKでさっさと作る

    最近AWS CDKが型定義ファイルとIDEの入力補完でパラメーター書けるので、新しいサービス立ち上げるときによく使ってます。

    スタック作ってFailしてRollbackして・・・のループに入る前に、TypeScriptがガンガンバリデートしてくれるので割と助かってます。

    ということで簡単なサンプルコード(lib/aws-cdk-stack.ts)

    import cdk = require('@aws-cdk/cdk');
    import Appsync = require('@aws-cdk/aws-appsync')
    
    export class AwsCdkStack extends cdk.Stack {
      constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
        super(scope, id, props);
    
        // APIを作る
        const api = new Appsync.CfnGraphQLApi(this, 'GraphQLAPI', {
          authenticationType: 'AMAZON_COGNITO_USER_POOLS',
          name: id,
          userPoolConfig: {
            appIdClientRegex: 'XXXXXXXXX',
            awsRegion: 'us-east-1',
            defaultAction: 'DENY',
            userPoolId: 'us-east-1_XXXXXXx'
          }
        })
        // schemaを定義する(今回は省略)
        const schema = AppSyncSchema.create(this, api)
    
        // datasourceを定義する(今回は省略)
        const dataSource = AppSyncDatasourcess.create(this, id, api)
    
        // resolverを定義する(今回は省略)
        const resolvers = AppSyncResolvers.create(this, api)
    
        // CFNの依存を組む
        resolvers.map((resolver: Appsync.CfnResolver) => {
          resolver.node.addDependency(dataSource);
          resolver.node.addDependency(schema);
        })
      }
    }
    

    schema / datasource / resolverも定義しないとですが、そこを書き出すと長いので省略しました。

    設定のポイント

    設定時のポイントとしては、以下の点があります。

    • 認証方式は1API 1タイプなので、API tokenなどとの併用はできない。
      • schema / resolverとかのコードを共有にして、もう1つAPIつくると良さそう。
    • defaultActionをDENYにした場合、schema毎に許可する設定が必要
    • アプリクライアントも決め打ちしたいなら、appIdClientRegexで指定

    複数の認証方式に対応させる

    CDKやCloudFormationで複数のAPIを作るのがよいでしょう。

    import cdk = require('@aws-cdk/cdk');
    import Appsync = require('@aws-cdk/aws-appsync')
    
    export class AwsCdkStack extends cdk.Stack {
      constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
        super(scope, id, props);
    
        // APIを作る
        const api = new Appsync.CfnGraphQLApi(this, 'GraphQLAPI', {
          authenticationType: 'AMAZON_COGNITO_USER_POOLS',
          name: id,
          userPoolConfig: {
            appIdClientRegex: 'XXXXXXXXX',
            awsRegion: 'us-east-1',
            defaultAction: 'DENY',
            userPoolId: 'us-east-1_XXXXXXx'
          }
        })
        const apiWithKey = new Appsync.CfnGraphQLApi(this, 'GraphQLAPI', {
          authenticationType: 'API_KEY',
          name: id,
        })
          
        new Appsync.CfnApiKey(this, 'apiKey', {
          apiId: apiWithKey.graphQlApiApiId,
        })
    
        // schemaを定義する(今回は省略)
        const schema = AppSyncSchema.create(this, api)
        const schema1 = AppSyncSchema.create(this, apiWithKey)
        
        // datasourceを定義する(今回は省略)
        const dataSource = AppSyncDatasourcess.create(this, id, api)
        const dataSource1 = AppSyncDatasourcess.create(this, id, apiWithKey)
    
        // resolverを定義する(今回は省略)
        const resolvers = AppSyncResolvers.create(this, api)
        const resolvers1 = AppSyncResolvers.create(this, apiWithKey)
    
        // CFNの依存を組む
        resolvers.map((resolver: Appsync.CfnResolver) => {
          resolver.node.addDependency(dataSource);
          resolver.node.addDependency(schema);
        })
        resolvers1.map((resolver: Appsync.CfnResolver) => {
          resolver.node.addDependency(dataSource1);
          resolver.node.addDependency(schema1);
        })
      }
    }

    schema単位での制御

    @aws_authを使うことでschema別に制御できます。ドキュメントをざっと見た範囲では、Cognito User Poolsのgroupで割り振りできる様子です。

    type Query {
      getItem(UUID: String!, track_month: Int!): Item
    		@aws_auth(cognito_groups: ["admin", "editor", "reader"])
      queryStatistics(
    		user_name: String!,
    		track_month: Int!,
    	): StatisticsConnection
    		@aws_auth(cognito_groups: ["admin"])
    }

    上のサンプルだと、getItemはadmin / editor / readerのいずれかに所属するユーザーがアクセス可能で、queryStatisticsはadminのみアクセスできます。

    これとdefaultActionをDENYにする設定を入れることで、ホワイトリスト的に制御できます。ただしすべてのschemaに明示的に設定する必要がありますので、トレードオフであることは要注意です。

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark