Amazon Cognitoを使ったのでその覚書ついでに
細かい説明とかはないです
ユーザー管理機能を設定する前に
Amazon Cognitoでユーザーを管理できるようにしますので、とりあえず、Reactのアプリを作成します。
$ npx create-react-app react-aws-cognito --typescript
アプリが作成されたら、srcディレクトリの中に、Cognitoを利用するためのコンフィグファイルを作成します。
const awsConfiguration = {
region: 'ap-northeast-1',
IdentityPoolId: 'ap-northeast-1:**********',
UserPoolId: 'ap-northeast-1_**********',
ClientId: '**********',
}
export default awsConfiguration
**********となっている部分はこれから説明します。
以下もインストールしてください。
$ yarn add amazon-cognito-identity-js
Cognitoをjsで楽にいじられるモジュールです。
https://github.com/aws-amplify/amplify-js/tree/master/packages/amazon-cognito-identity-js
ユーザープールを作成する
Cognitoに入ると上のような画面になるので、まずは「ユーザープールの管理」に入ります。
「ユーザープールを作成する」ボタンがあると思いますので、そこから作成画面へ入っていきます。
こんな具合で設定画面が出てくるので、順に設定します。
ユーザープールはそのままの意味で、ユーザーのみずたまりです。
ユーザーの倉庫のようなものです。
今回はデフォルトで作ります。
デフォルトの場合こんな設定になります。
必須の属性はemailとなり、これはサインアップ時に必須の項目となります(細かく設定するとこの辺りも変えられます)。
パスワードの最小長や多要素認証、また、登録時に送信するEメールやSMSの内容を編集したりもできます。
lambdaと連携させることも可能です
次にIDプールを作ります。
IDプールを作成する
フェデレーテッドアイデンティティから「新しいIDプールの作成」をクリックすると、以下のような画面が表示されます。
IDプール名を入力し、認証プロバイダーのところでCognitoタブを選択して必要な情報を入れます。
ユーザープールID
作成したユーザープールの「全般設定」(ユーザープールから作成したのを選択すると表示されます)にプールIDがあるのでこれを入力してください、
IDプール
作成したユーザープールの「アプリクライアント」からアプリクライアントを作成すると表示される、アプリクライアントIDを設定してください。
今回はJavascriptSDKを利用するのですが、その場合は「クライアントシークレットを生成」のチェックを外してください。JSのSDKでの対応ができていないようですね。。。
https://qiita.com/noobar/items/6615501b035e47792227
入力したら、「プールの作成」を押し、IAMロールを作成します。
ここのロールは、Authenticated UserとUnauthenticated Userそれぞれのアプリケーションの利用可能範囲を定めたものを表します。
Authenticated Userには、アプリケーションに登録したユーザーとして実行可能な範囲のアクションと、そのアクションに伴うAWSリソースへのアクセスを認めるべきです。
反対に、Unauthenticated UserにはAuthenticated Userと比較してより厳しい範囲でのアプリケーションの利用を認めるようにした方が無難です。
IDプールに紐づくIAMロールを作成するので、「許可」を押して次へ行きましょう。
作成が成功したら、「Amazon Cognitoでの作業開始」が出てくるので、ドロップダウンからjavascriptを選びます。
すると、js用のコードが出てきて、その中に、IdentityPoolIdが表記されているので、それをコピーしておきます。
ここまでで今一度、
IdentityPoolId、UserPoolId、 ClientIdを整理しておきましょう(冒頭のコンフィグファイルの項目です)。
IdentityPoolId ・・・ IDプール作った時に出てきたやつ。
UserPoolId ・・・ ユーザープールの「全般設定」に表示されるプールID。
ClientId ・・・ ユーザープールの「アプリクライアント」に表示されるアプリクライアントID。
上記は全てコピーして、冒頭で書いたawsConfiguration.tsに書き込みます。
srcディレクトリ内はこんな感じになります。
.
├── auth(SignUp.tsx, Verification.tsx, SignIn.tsx, SignOut.tsx)
├── App.css
├── App.test.tsx
├── App.tsx
├── awsConfiguration.ts
├── index.css
├── index.tsx
├── logo.svg
├── react-app-env.d.ts
└── serviceWorker.ts
サインアップ
tsxで書いたサインアップのコンポーネントです。
肝はSignUp関数です。
必須の属性として設定されていたemailとパスワードを入力すると、入力したemailあてにメールが届きます(もちろん電話番号で設定するとSMSでも届けられるように設定できます)。
このメールの文言もAWSコンソールで設定できます。
コードの有効期限はデフォルトでは7日になっているようです。
ちなみに、パスワードのポリシーはデフォルトで以下のようになっているので、注意してください。
import React from 'react'
import '../App.css'
import {
CognitoUserPool,
CognitoUserAttribute
} from "amazon-cognito-identity-js"
import awsConfiguration from '../awsConfiguration'
const userPool = new CognitoUserPool({
UserPoolId: awsConfiguration.UserPoolId,
ClientId: awsConfiguration.ClientId,
})
const SignUp: React.FC = () => {
const [email, setEmail] = React.useState<string>('')
const [password, setPassword] = React.useState<string>('')
const changedEmailHandler = (event: any) => setEmail(event.target.value)
const changedPasswordHandler = (event: any) => setPassword(event.target.value)
const signUp = () => {
const attributeList = [
new CognitoUserAttribute({
Name: 'email',
Value: email
})
]
userPool.signUp(email, password, attributeList, [], (err, result) => {
if (err) {
console.error(err)
return
}
setEmail('')
setPassword('')
})
}
return (
<div className="SignUp">
<h1 style={{ textAlign: 'left' }}>SignUp</h1>
<input type="text" placeholder="email" onChange={changedEmailHandler} />
<input type="text" placeholder="password" onChange={changedPasswordHandler} />
<button onClick={signUp}>SignUp</button>
</div>
)
}
export default SignUp
検証
サインアップで受け取った検証コードを使ってサインアップを完了しましょう。
検証コードと必須の属性であるemailを入力する以下のコードを実行すると、アカウントのステータスがUNCONFIRMEDだったのが画像のようにCONFIRMEDに変わります(ユーザープール/全般設定/ユーザーとグループ)。
import React from 'react'
import '../App.css'
import {
CognitoUserPool,
CognitoUser
} from "amazon-cognito-identity-js"
import awsConfiguration from '../awsConfiguration'
const userPool = new CognitoUserPool({
UserPoolId: awsConfiguration.UserPoolId,
ClientId: awsConfiguration.ClientId,
})
const Verification: React.FC = () => {
const [email, setEmail] = React.useState<string>('')
const [verificationCode, setVerificationCode] = React.useState<string>('')
const changedEmailHandler = (event: any) => setEmail(event.target.value)
const changedVerificationCodeHandler = (event: any) => setVerificationCode(event.target.value)
const verifyCode = () => {
const cognitoUser = new CognitoUser({
Username: email,
Pool: userPool
})
cognitoUser.confirmRegistration(verificationCode, true, (err: any) => {
if (err) {
console.log(err)
return
}
console.log('verification succeeded')
setEmail('')
setVerificationCode('')
})
}
return (
<div className="Verification">
<h1>Authenticate</h1>
<input type="text" placeholder="verification code" onChange={changedVerificationCodeHandler} />
<input type="text" placeholder='email' onChange={changedEmailHandler} />
<button onClick={verifyCode}>Authenticate</button>
</div>
)
}
export default Verification
サインイン
それでは検証成功したアカウントでサインインしてみます。
SignIn関数でサインインを行い、認証が成功すればonSuccessを通ってresultを返します。
ここではresultからaccessTokenを取り出してログに出しています。
import React from 'react'
import '../App.css'
import {
CognitoUserPool,
CognitoUser,
AuthenticationDetails
} from "amazon-cognito-identity-js"
import awsConfiguration from '../awsConfiguration'
const userPool = new CognitoUserPool({
UserPoolId: awsConfiguration.UserPoolId,
ClientId: awsConfiguration.ClientId,
})
const SignIn: React.FC = () => {
const [email, setEmail] = React.useState<string>('')
const [password, setPassword] = React.useState<string>('')
const changedEmailHaldler = (e: any) => setEmail(e.target.value)
const changedPasswordHandler = (e: any) => setPassword(e.target.value)
const signIn = () => {
const authenticationDetails = new AuthenticationDetails({
Username : email,
Password : password
})
const cognitoUser = new CognitoUser({
Username: email,
Pool: userPool
})
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: (result) => {
console.log('result: ' + result)
const accessToken = result.getAccessToken().getJwtToken()
console.log('AccessToken: ' + accessToken)
setEmail('')
setPassword('')
},
onFailure: (err) => {
console.error(err)
}
})
}
return (
<div className="SignIn">
<h1>SingIn</h1>
<input type="text" placeholder='email' onChange={changedEmailHaldler}/>
<input type="text" placeholder='password' onChange={changedPasswordHandler}/>
<button onClick={signIn}>Sign In</button>
</div>
)
}
export default SignIn
サインイン中のアカウントを検知する場合は、
const cognitoUser = userPool.getCurrentUser()
if (cognitoUser) {
// sign inしている状態
console.log('signing in')
} else {
// sign inしていない状態
console.log('no signing in')
}
という感じで行い、サインアウト時もこれがtrueの時に処理できるようにします。
サインアウト
サインアウトはこちら。
userPool.getCurrentUser()で現在サインイン中のアカウントを検知し、
サインインされていればcognitoUser.signOut()を、
そうでなければとりあえず念の為localStorageを綺麗にしています(サインインするとlocalStorageにもaccessToken等のデータが入るので)。
import React from 'react'
import '../App.css'
import { CognitoUserPool } from "amazon-cognito-identity-js"
import awsConfiguration from '../awsConfiguration'
const userPool = new CognitoUserPool({
UserPoolId: awsConfiguration.UserPoolId,
ClientId: awsConfiguration.ClientId,
})
const SignOut: React.FC = () => {
const signOut = () => {
const cognitoUser = userPool.getCurrentUser()
if (cognitoUser) {
cognitoUser.signOut()
localStorage.clear()
console.log('signed out')
} else {
localStorage.clear()
console.log('no user signing in')
}
}
return (
<div className="SignOut">
<h1>SignOut</h1>
<button onClick={signOut}>Sign Out</button>
</div>
)
}
export default SignOut
全部まとめて整理するとこんな感じ?
import React from 'react'
import './App.css'
// components
import SignUp from './auth/SignUp'
import Verification from './auth/Verification'
import SignIn from './auth/SignIn'
import SignOut from './auth/SignOut'
import { CognitoUserPool } from "amazon-cognito-identity-js"
import awsConfiguration from './awsConfiguration'
const userPool = new CognitoUserPool({
UserPoolId: awsConfiguration.UserPoolId,
ClientId: awsConfiguration.ClientId,
})
const App: React.FC = () => {
const authentication = () => {
const cognitoUser = userPool.getCurrentUser()
// サインインユーザーがいればアプリのメイン画面へ、
// いなければサインアップ、検証、サインイン画面を表示する。
if (cognitoUser) {
return (
<div className="authorizedMode">
<SignOut />
</div>
)
} else {
return (
<div className="unauthorizedMode">
<SignUp />
<Verification />
<SignIn />
</div>
)
}
}
return (
<div className="App">
<header />
{ authentication() }
</div>
)
}
export default App
グループ(IAMロール)の設定
Cognitoでは特定のIAMロールを持つグループを作成することができ、
そのグループに属するユーザーはそのIAMロールのポリシーに従ってAWSリソースへのアクセスが制限されます。
IAMロールから割り当てたいロールを選択してグループを作成します。
以降、AWSコンソールからグループにユーザーを割り当てることができるようになります。
ですが、これ、毎回手動でAWSコンソールをいじるのも面倒すぎるので、JavascripSDKを利用してやりたいです。
adminAddUserToGroupを利用して実現します。
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html#adminAddUserToGroup-property
import AWS from 'aws-sdk'
const cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider()
const params = {
GroupName: 'STRING_VALUE', /* required */
UserPoolId: 'STRING_VALUE', /* required */
Username: 'STRING_VALUE' /* required */
}
cognitoidentityserviceprovider.adminAddUserToGroup(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
})
上のように書くみたいです(試してませんが)。
検証が成功した後にこの処理を挟めば良いと思います。
Usernameはおそらくメールアドレスを入力すれば問題ないかと思います。
もしかしたら必須の属性設定によるかもしれませんが。。。
基本的には以上でCognitoを利用した認証機能を実装できるはずです
デモ作ってみました。ここで説明したawsConfiguration.tsは入っていません。
https://github.com/yutaro1204/AmazonCognitoWithReact