4
2

More than 1 year has passed since last update.

golangで動かして学ぶCognitoユーザプール、IDプール

Posted at

Cognitoをgolnagから操作してみる

イマイチ理解しきれてなかったので、ラッパーライブラリを作りつつ、勉強してみました。
元ネタはhttps://dev.classmethod.jp/articles/get-aws-temporary-security-credentials-with-cognito-id-pool-by-aws-cli
で、AWS CLIベースの記事だったのをgolangに書き起こしつつ理解するって形を取りました。

作ったラッパーは
https://gitlab.com/kuritayu/cognito
におきました。

ユーザプール、IDプールの違い

  1. ユーザプール: ユーザ情報、ログイン情報の管理(認証担当)
  2. IDプール: ユーザに応じた一時クレデンシャルキーの発行(認可担当)

パターン別のクレデンシャルキーの発行

  1. 認証済・非認証に応じたクレデンシャルキーの発行(IAM Role)
  2. ルールベースのクレデンシャルキーの発行
  3. ユーザプールに設定したIAM Roleでクレデンシャルキーを発行

環境構築

上記クラスメソッドさんの記事に詳細に記載されているため、割愛

認証部品(ユーザプール操作)

初期化

セッション情報、プールID、アプリクライアントIDを引数に、構造体を構築します。

    region := "region名"
    poolId := "ユーザプールID"
    clientId := "アプリクライアントID"
    sess := session.Must(
        session.NewSession(&aws.Config{
            Region: aws.String(region),
        }))
    userPool := New(sess, poolId, clientId)

ユーザ作成

saveメソッドを呼び出すだけです。
今回のユーザプールでは必須属性としてメールアドレス、カスタム属性として会社名を入れてみました。

func TestUserPool_Save(t *testing.T) {
    err := userPool.Save("ユーザ名", "メールアドレス", "会社名")
    assert.NoError(t, err)
}

saveメソッドでは、AttributeTypeにemailやカスタム属性の会社名を設定しています。

func (p UserPool) Save(name string, email string, company string) error {

    var attrs []*cognitoidentityprovider.AttributeType
    emailAttr := p.userAttribute(cognitoidentityprovider.UsernameAttributeTypeEmail, email)
    attrs = append(attrs, emailAttr)
    companyAttr := p.userAttribute("custom:company", company)
    attrs = append(attrs, companyAttr)

    input := &cognitoidentityprovider.AdminCreateUserInput{
        UserPoolId:     aws.String(p.PoolId),
        Username:       aws.String(name),
        UserAttributes: attrs,
    }

    _, err := p.client.AdminCreateUser(input)
    if err != nil {
        return err
    }

    return nil
}

ユーザ情報取得

ユーザ名を引数にとり、構造体で返します。

func TestUserPool_FindOne(t *testing.T) {
    out, err := userPool.FindOne("ユーザ名")
    assert.NoError(t, err)
}

FindOneメソッドですが、UserAttributesが配列で入っていて厄介です。
ループで回して構造体に入れていますが、他にいい方法があるのかも。

func (p UserPool) FindOne(name string) (*UserInfo, error) {

    input := &cognitoidentityprovider.AdminGetUserInput{
        UserPoolId: aws.String(p.PoolId),
        Username:   aws.String(name),
    }

    out, err := p.client.AdminGetUser(input)
    if err != nil {
        return nil, err
    }

    userInfo := &UserInfo{
        Name: name,
    }
    for _, a := range out.UserAttributes {
        switch aws.StringValue(a.Name) {
        case "sub":
            userInfo.Uuid = aws.StringValue(a.Value)
        case "email":
            userInfo.Email = aws.StringValue(a.Value)
        case "custom:company":
            userInfo.Company = aws.StringValue(a.Value)
        }
    }

    return userInfo, nil
}

認証

こちらもシンプルにID/Passwordを引数にわたすだけです。IDトークンが返ってきます。(IDプールで使うので)

func TestUserPool_Authenticate(t *testing.T) {
    out, err := userPool.Authenticate("ユーザ名", "パスワード")
    assert.NoError(t, err)
}

Authenticateメソッドは、凝ったことはしていません。

func (p UserPool) Authenticate(name string, password string) (string, error) {

    input := &cognitoidentityprovider.AdminInitiateAuthInput{
        AuthFlow: aws.String("ADMIN_USER_PASSWORD_AUTH"),
        AuthParameters: map[string]*string{
            "USERNAME": aws.String(name),
            "PASSWORD": aws.String(password),
        },
        ClientId:   aws.String(p.clientId),
        UserPoolId: aws.String(p.PoolId),
    }

    out, err := p.client.AdminInitiateAuth(input)
    if err != nil {
        return "", err
    }

    return aws.StringValue(out.AuthenticationResult.IdToken), nil
}

認可部品(IDプール操作)

初期化

セッション情報、プールIDを引数に、構造体を構築します。

    region := "リージョン名"
    poolId := "IDプール名"
    sess := session.Must(
        session.NewSession(&aws.Config{
            Region: aws.String(region),
        }))
    idPool := New(sess, poolId)

Identity ID取得

認証済、非認証で2パターン用意しました。
ユーザプールから取得したIDトークンを使うかどうか、の違いですね。

func TestIdPool_GetIdWithoutAuth(t *testing.T) {
    out, err := idPool.GetIdWithoutAuth()
    assert.NoError(t, err)
}

func TestIdPool_GetIdWithAuth(t *testing.T) {
    userPool := SetUpForUserPool()
    idToken, err := userPool.Authenticate("ユーザ名", "パスワード")
    assert.NoError(t, err)

    identityId, err := idPool.GetIdWithAuth(idToken, userPool.PoolId)
    assert.NoError(t, err)
}

メソッドとしては、インプットのパラメータが違うだけです。
投げるリクエストは共通化しています。

func (i IdPool) GetIdWithoutAuth() (string, error) {

    input := &cognitoidentity.GetIdInput{
        IdentityPoolId: aws.String(i.poolId),
    }

    return i.doGetId(input)
}


func (i IdPool) GetIdWithAuth(token string, userPoolId string) (string, error) {

    param := make(map[string]*string)
    provider := fmt.Sprintf("cognito-idp.%v.amazonaws.com/%v", aws.StringValue(i.client.Config.Region), userPoolId)
    param[provider] = aws.String(token)

    input := &cognitoidentity.GetIdInput{
        IdentityPoolId: aws.String(i.poolId),
        Logins:         param,
    }

    return i.doGetId(input)
}

func (i IdPool) doGetId(input *cognitoidentity.GetIdInput) (string, error) {

    out, err := i.client.GetId(input)
    if err != nil {
        return "", err
    }

    return aws.StringValue(out.IdentityId), nil
}


認可(クレデンシャルキー発行)

取得したクレデンシャルキーをstsでチェックし、割り当てられていることまで確認しました。

func TestIdPool_GetCredentialWithoutAuth(t *testing.T) {
    idPool := SetUpForIdPool()
    identityId, _ := idPool.GetIdWithoutAuth()
    out, err := idPool.GetCredentialWithoutAuth(identityId)
    assert.NoError(t, err)

    sts, err := SetUpSTS(out.accessKey, out.secretKey, out.sessionToken)
    assert.NoError(t, err)

    log.Print(sts)
}

func TestIdPool_GetCredentialWithAuth(t *testing.T) {
    userPool := SetUpForUserPool()
    idToken, err := userPool.Authenticate("ユーザ名", "パスワード")
    assert.NoError(t, err)

    idPool := SetUpForIdPool()
    identityId, err := idPool.GetIdWithAuth(idToken, userPool.PoolId)
    assert.NoError(t, err)

    out, err := idPool.GetCredentialWithAuth(identityId, idToken, userPool.PoolId)
    assert.NoError(t, err)

    sts, err := SetUpSTS(out.accessKey, out.secretKey, out.sessionToken)
    assert.NoError(t, err)

    log.Print(sts)

}
4
2
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
4
2