0
0

More than 1 year has passed since last update.

golangとAWS Cognitoで認証機能を実装してみた

Posted at

環境について

Cognitoへのアクセス準備

Cognitoへ接続するためには
aws_access_key_id
aws_secret_access_key
がそれぞれ必要なので、AWS SDK for Goを見て下記のファイルを作成します。

credentials
[default]
aws_access_key_id = xxxxxxxxxxxxxxxxxxxx
aws_secret_access_key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

それぞれのKeyについてはIAMでCognitoへのアクセス権限を持ったユーザーを作成すると発行される。

上記で用意したDockerfileにコンテナ内へコピーするコマンドを追加

Dokerfile
〜略〜
# AWS COGNITOの環境変数設定
RUN mkdir /root/.aws
COPY ./credentials /root/.aws/

あとはCognitoの設定等必要ですが、
ここら辺を参考にしました。
他にもいろいろ記事があると思うのでいろいろ見てみてください。

その他アクセスに必要なもの

UserPoolId:ユーザープールを作成時に発行
ClientId:ユーザープール作成時にアプリクライアントを作成すると発行
Region:リージョンを東京とかにしていると必要

アクセスキーやIDは漏洩すると大変なので扱いは注意しましょう!

コーディング

変数を定義

それぞれのメソッドで使用するのでグローバル変数に用意しました。

service.go
var clientId = "xxxxxxxxxxxxxxxxxxxxxxxxx"//用意したClientId
var userPoolId = "ap-northeast-1_xxxxxxxxx"//用意したユーザープールID
var sess = session.Must(session.NewSessionWithOptions(
	session.Options{
		SharedConfigState: session.SharedConfigEnable,
		Config: aws.Config{
			CredentialsChainVerboseErrors: aws.Bool(true),
			Region:                        aws.String("ap-northeast-1"),//ここでRegionを使用
		},
	}))

上記のCredentialsChainVerboseErrors: aws.Bool(true)の部分は
SDKを実行した時に、下記のようなエラーが出る場合があります。

NoCredentialProviders: no valid providers in chain. Deprecated. 
    For verbose messaging see aws.Config.CredentialsChainVerboseErrors

このままだと、エラー詳細まで確認できないのでConfigで設定しておくと、エラー詳細を確認できるようになります。

err: NoCredentialProviders: no valid providers in chain
caused by: EnvAccessKeyNotFound: failed to find credentials in the environment.
SharedCredsLoad: failed to load profile, .
EC2RoleRequestError: no EC2 instance role found
caused by: RequestError: send request failed
caused by: Get http://169.254.169.254/latest/meta-data/iam/security-credentials/: dial tcp 169.254.169.254:80: connect: invalid argument

アクセスキーが間違ってたり設定されていないと上記のようなエラーが発生します。

認証処理

Cognitoに追加されているユーザーの認証処理を下記のようにします。
(ユーザー追加はCognitoで直接追加しています。)

service.go
func (Service) CognitoAuth(ut model.UserToken) (model.UserToken, error) {
	//認証パラメータ生成
	params := &cognitoidentityprovider.AdminInitiateAuthInput{
		AuthFlow: aws.String(cognitoidentityprovider.AuthFlowTypeAdminNoSrpAuth),
		AuthParameters: map[string]*string{
			"USERNAME": aws.String(ut.Id),
			"PASSWORD": aws.String(ut.Password),
		},
		ClientId:   aws.String(clientId),
		UserPoolId: aws.String(userPoolId),
	}

	//Sessionの生成
	client := cognitoidentityprovider.New(sess)

	//認証実行
	res, err := client.AdminInitiateAuth(params)
	if err != nil {
		log.Print("AdminAuth Error")
		log.Fatal(err)
	}
	if res == nil {
		log.Fatal("failed to login")
	}
	if res.Session != nil {
		ut.Session = res.Session
		ut.AuthState = model.NewPassword
		return ut, nil
	}
	if res.AuthenticationResult == nil || res.AuthenticationResult.IdToken == nil {
		log.Fatal("failed to login")
	}

	ut.IdToken = res.AuthenticationResult.IdToken
	ut.AccessToken = res.AuthenticationResult.AccessToken
	ut.RefreshToken = res.AuthenticationResult.RefreshToken
	ut.AuthState = model.Authticated

	return ut, nil
}

初回認証時はパスワードの再設定を要求されるのでTokennullでレスポンスされます。
その代わり、Sessionに一時トークンがレスポンスされるので、これを使って再設定を行います。

再設定は以下のように用意しました。

service.go
func (Service) SetNewPassword(ut model.UserToken) error {

	params := cognitoidentityprovider.AdminRespondToAuthChallengeInput{
		ChallengeName: aws.String("NEW_PASSWORD_REQUIRED"),
		ChallengeResponses: map[string]*string{
			"NEW_PASSWORD":               aws.String(*ut.NewPassword),
			"USERNAME":                   aws.String(ut.Id),
			"userAttributes.name":        aws.String(*ut.Name),
			"userAttributes.family_name": aws.String(*ut.FamilyName),
		},
		ClientId:   aws.String(clientId),
		Session:    ut.Session,
		UserPoolId: aws.String(userPoolId),
	}

	client := cognitoidentityprovider.New(sess)

	res, err := client.AdminRespondToAuthChallenge(&params)
	if err != nil {
		log.Print("No Changed Password")
		log.Fatal(err)
	}
	log.Print(res)
	return nil
}

ユーザープール作成時に必須属性を追加している場合は、初回パスワード設定の際に合わせて設定する必要があります。
userAttributes.{属性名}で追加する必要があります。

コントローラー、モデル等を実装したら、これで一通り実装は終わりです。

まとめ

Sessionで渡される一時トークンは有効期限があるので、期限を過ぎるとエラーがレスポンスされます。
今回はまとめてエラーを処理していますが、実運用する場合はトークン切れ等の処理が必要と思います。

間違いや、もっとこうした方がいい等ありましたらご指摘ください。

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