環境について
Cognitoへのアクセス準備
Cognitoへ接続するためには
aws_access_key_id
aws_secret_access_key
がそれぞれ必要なので、AWS SDK for Goを見て下記のファイルを作成します。
[default]
aws_access_key_id = xxxxxxxxxxxxxxxxxxxx
aws_secret_access_key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
それぞれのKeyについてはIAMでCognitoへのアクセス権限を持ったユーザーを作成すると発行される。
上記で用意したDockerfileにコンテナ内へコピーするコマンドを追加
〜略〜
# AWS COGNITOの環境変数設定
RUN mkdir /root/.aws
COPY ./credentials /root/.aws/
あとはCognitoの設定等必要ですが、
ここら辺を参考にしました。
他にもいろいろ記事があると思うのでいろいろ見てみてください。
その他アクセスに必要なもの
UserPoolId
:ユーザープールを作成時に発行
ClientId
:ユーザープール作成時にアプリクライアントを作成すると発行
Region
:リージョンを東京とかにしていると必要
アクセスキーやIDは漏洩すると大変なので扱いは注意しましょう!
コーディング
変数を定義
それぞれのメソッドで使用するのでグローバル変数に用意しました。
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で直接追加しています。)
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
}
初回認証時はパスワードの再設定を要求されるのでToken
はnull
でレスポンスされます。
その代わり、Sessionに一時トークンがレスポンスされるので、これを使って再設定を行います。
再設定は以下のように用意しました。
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(¶ms)
if err != nil {
log.Print("No Changed Password")
log.Fatal(err)
}
log.Print(res)
return nil
}
ユーザープール作成時に必須属性を追加している場合は、初回パスワード設定の際に合わせて設定する必要があります。
userAttributes.{属性名}
で追加する必要があります。
コントローラー、モデル等を実装したら、これで一通り実装は終わりです。
まとめ
Sessionで渡される一時トークンは有効期限があるので、期限を過ぎるとエラーがレスポンスされます。
今回はまとめてエラーを処理していますが、実運用する場合はトークン切れ等の処理が必要と思います。
間違いや、もっとこうした方がいい等ありましたらご指摘ください。