2021-08-22

AWS Cognitoでユーザー情報をAWS Lambda(Golang)から取得する方法

blogimg

目的

APIの処理をAPIを叩いてきたユーザー名によって変えたいということがありました。

そこで、Cognitoでオーソライズを行っているLambda関数にて、API Gatewayの認証情報をLambda(Golang)の中で取得する方法を調べました。

結論

以下のコードでユーザー名を取得できます。

func handler(ctx context.Context, apiGWEvent events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {

	ユーザー名取得
	diaryGetter := apiGWEvent.RequestContext.Authorizer["claims"].(map[string]interface{})["cognito:username"].(string)

  /// ...略
}

割と途中のコードとかが謎ですので、以下に解説を載せておきます。(これより単純に取得できる方法がありましたらご教授いただけると幸いです。)

解説

(前提として、LambdaのオーソライザーにCognitoのユーザープールが設定されていることを確認)

まず、events.APIGatewayProxyRequestにはどのようなhttpリクエストを受け取ったのかが格納されます。

ドキュメントを参照すると、

type APIGatewayProxyRequest struct {
  Resource            string            `json:"resource"` // The resource path defined in API Gateway
  Path              string            `json:"path"`   // The url path for the caller
  HTTPMethod           string            `json:"httpMethod"`
  Headers             map[string]string       `json:"headers"`
  MultiValueHeaders        map[string][]string      `json:"multiValueHeaders"`
  QueryStringParameters      map[string]string       `json:"queryStringParameters"`
  MultiValueQueryStringParameters map[string][]string      `json:"multiValueQueryStringParameters"`
  PathParameters         map[string]string       `json:"pathParameters"`
  StageVariables         map[string]string       `json:"stageVariables"`
  RequestContext         APIGatewayProxyRequestContext `json:"requestContext"`
  Body              string            `json:"body"`
  IsBase64Encoded         bool             `json:"isBase64Encoded,omitempty"`
}

となっております。

参考:[GoDoc package aws/aws-lambda-go/events]

参考:[AWS Lambda+API Gateway+DynamoDBでCRUD APIを作るのをGolangでやってみた]

こちらの、RequestContext構造体には、Authorizer情報が`map[string]interface{}`型で入っています。

僕が作成したアプリのdev環境用のCognitoの情報を取得してみると、以下の様になっています。

map[claims:map[aud:tekitoutekitou auth_time:12345678 cognito:username:テスト太郎 event_id:tekitou-d37c-41c9-be67-tekitou exp:Tue May 11 14:18:46 UTC 2021 iat:Tue May 11 13:18:46 UTC 2021 iss:https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-tekitou sub:tekitou-c7e9-4649-ab16-tekitou token_use:id]]

こちらから、claimsキーの中の、cognito:usernameキーがユーザーネームであることがわかります。

最初この構造を見たとき、

diaryGetter := apiGWEvent.RequestContext.Authorizer["claims"]["cognito:username"]

みたいな感じでとれるんじゃないの?

と思ったのですが、これをやろうとすると、

invalid operation: cannot index apiGWEvent.RequestContext.Authorizer["claims"] (map index expression of type interface{})

となってしまいます。

interface型の知識が欠如しておりました。

> どんな型の値でも受け取れるinterface{}ですが、interface{}型の引数で受け渡された値は、元の型の情報が欠落しています。

(元の型の値を操作するための関数等を実行できません)

引用:https://blog.y-yuki.net/entry/2017/05/08/000000

元の型の値を操作できないため、辞書型のキーを取り出すという動作ができないっぽいです。

なので、型アサーションをして、取り出す必要がありました。さらにこの`diaryGetter`はstring型として扱いたいので、最後も型アサーションをする必要があったということです。

まず、

apiGWEvent.RequestContext.Authorizer["claims"]

を型アサーションで`map[string]interface{}`型にして、mapのキーを取り出すという感じです。