はじめに
Amazon Cognitoはユーザ認証・認可機能を提供するサービスです。ユーザ認証はCognitoの機能で行うことも、GoogleやFacebookなどのソーシャルプロバイダーと連携して行うこともできます。
認証が完了したユーザにリソースへのアクセス権を与えるのが認可で、Cognitoを使うとユーザ認証から認証完了後にAWSリソースへのアクセス権の割り当てるところまでをワンストップ、且つ簡単に実装できます。ここではその一例としてAWS S3にファイルアップロードできるアプリ(ログイン機能付き)を作成してみたいと思います。
手順
- Cognitoユーザプールを作成する
- CognitoIDプールを作成する
- S3を作成する
- IAMロールを編集する
- Webアプリを作成する
- ログインユーザを登録する
Cognitoユーザプールを作成する
Cognitoユーザプールとは前述のユーザ認証機能で使用するデータプールになります。ユーザプールを作成すると、認証情報(ID/パスワード)が登録できるようになります。ユーザプールはAWSコンソールから数ステップで作成できます。
ユーザプールの作成方法
- AWSコンソールにログインし、Amazon Cognitoのサービスを検索し、メニュー画面から「ユーザプールの管理」を選択します。
- ユーザプールの管理画面の右上にある「ユーザプールを作成する」ボタンを押すと、作成画面が現れるため、プール名に適当な名前をつけて、「デフォルトを確認する」から設定を行います。
- デフォルト設定が表示されるので、そのままで「プールの作成」ボタンを押します。
- プールが作成されるので、左側のメニューから全般設定-アプリクライアントを選択します。
- 「アプリクライアントを追加」を選択すると、アプリクライアントの設定画面が表示されます。アプリクライアント名に適当な名前を付けてから、クライアントシークレットを生成のチェックボックスを外した上で「アプリクライアントの作成」を押します。
- 次の画面で表示される「アプリクライアント ID」をメモっておきます。
これでユーザプールが作成できました。
CognitoIDプールを作成する
CognitoIDプールとはユーザ認可機能の方で使用するデータプールになります。認証済みのユーザーに対して、任意のIAMロールを割り当てることでIAMロールで定義されたリソースへのアクセスが可能になります。認証プロバイダーとして、上記で作成したユーザプールを紐付けることで、ユーザプールで認証を行い、IDプールで認可するという連携が可能になります。
IDプールの作成方法
- AWSコンソールからAmazon Cognitoのサービスを検索し、メニュー画面から「IDプールの管理」を選択します。
- IDプール作成画面が表示されるので、適当なIDプール名を付与し、認証IDプロバイダーのCognitoタブのところに上で作成したユーザプールIDとアプリクライアントIDを入力した上で、「プールの作成」ボタンを押します。
- 「Identify the IAM roles to use with your new identity pool」という画面が現れます。ここでは未認証/認証済みのユーザに割り当てるIAMロールを定義します。IAMロール名はそれぞれCognito_(IDプール名)Unauth_Role/Cognito_(IDプール名)Auth_Roleという名前が割り当てられます。ここでは何も変えずに「許可」ボタンを押します。
これでIDプールが作成できました。
S3を作成する
ローカルファイルのアップロード先となるS3バケットを作成します。
S3の作成方法
- AWSコンソールからS3のサービスを検索し、メニュー画面の右上の方にある「バケットを作成」ボタンを押します。
- 適当なバケット名を付けて、他はデフォルトのままで「バケットを作成」ボタンを押します。
ちなみにデフォルトではCORSが許可されていないため、外部(別ドメイン)からバケットにファイルアップロードしようとすると失敗します。このため作成したバケットにCORS設定を行います。
CORSの設定方法
- 作成したバケットの「アクセス許可」タブの一番下にある「Cross-Origin Resource Sharing (CORS)」の編集ボタンを押します。
- 開いたエディタに以下の内容をペーストして「変更の保存」ボタンを押します。
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"HEAD",
"GET",
"PUT",
"POST",
"DELETE"
],
"AllowedOrigins": [
"*"
],
"ExposeHeaders": [
"ETag"
]
}
]
これでS3バケットが作成できました。
IAMロールを編集する
IDプール作成時に割り当てられたIAMロールについては、デフォルトでは何の権限も付与されていません。このため、認証済みロール(Cognito_(IDプール名)Auth_Role)に対して、S3バケットへのアクセス権を付与します。これを行うことで、ユーザ認証が完了するとS3バケットへのアクセスが可能となります。
IAMロールの編集方法
- AWSコンソールからIAMのサービスを検索し、左側のメニューバーにある「ロール」を選択します。
- 「Cognito_(IDプール名)Auth_Role」で検索を行い、表示結果を選択します。
- 許可タブの右上にある「許可を追加」ボタンを押して、更に表示される「ポリシーをアタッチ」を選択します。
- 「S3」で検索を行い、表示結果から「AmazonS3FullAccess」のチェックボックスにチェックを入れて、「ポリシーをアタッチ」ボタンを押します。
これで認証済みIAMロールへの権限付与ができました。
Webアプリを作成する
AWS側の準備ができたので、Webアプリ(React)を作成します。
アプリはCreateReactAppを使って構築します。
npx create-react-app my-app
cd my-app
AWS SDKを使うため事前にインストールしておきます。
npm i aws-sdk
npm i amazon-cognito-identity-js
なおSDKの設定については下記を参照してください。
https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/configuring-the-jssdk.html
次にmy-appディレクトリに作成されたファイル群のうち、以下のファイルについて内容を書き換えます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>Cognito demo app</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
import './App.css';
import { useState } from 'react'
var AWS = require("aws-sdk");
var AmazonCognitoIdentity = require('amazon-cognito-identity-js');
function App() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [s3Client, setS3Client] = useState('')
const [loginFlg, setLoginFlg] = useState(false)
const [uploadFile, setUploadFile] = useState('')
// AWS関連のパラメータ設定
const Region = '(AWSリージョン名)'
const IdentityPoolId = '(CognitoIDプールのID)'
const poolData = {
UserPoolId: '(CognitoユーザプールのID)'
ClientId: '(ユーザプールのアプリクライアントID)',
};
const CognitoId = 'cognito-idp.(リージョン名).amazonaws.com/(ユーザプールID)'
const BucketName = '(S3バケット名)'
// ログイン用の関数
const login = () => {
try {
// 認証情報の設定
const authenticationData = {
Username: email,
Password: password,
}
const authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(
authenticationData
)
const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData)
const userData = {
Username: email,
Pool: userPool,
}
const cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData)
// ユーザ認証
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function(result) {
// 認証成功の場合、認可情報をSDKに設定
const idToken = result.getIdToken().getJwtToken()
AWS.config.region = Region
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: IdentityPoolId,
Logins: {
[CognitoId]: idToken,
},
})
// 認可情報をSDKに反映
AWS.config.credentials.refresh(error => {
if (error) {
alert('Login failure!')
console.error(error);
} else {
const s3 = new AWS.S3({
apiVersion: "2006-03-01",
params: { Bucket: BucketName }
});
setS3Client(s3)
setLoginFlg(true)
alert('Successfully login!')
}
});
},
onFailure: function(err) {
alert('Login failure!')
console.log(err.message || JSON.stringify(err));
},
});
} catch(e) {
alert('Login failure!')
console.log(e)
}
}
// ログアウト用の関数
const logout = () => {
try {
// 認証情報の設定
const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData)
const userData = {
Username: email,
Pool: userPool,
}
const cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData)
// ログアウト
cognitoUser.signOut();
setS3Client('')
setLoginFlg(false)
alert('Successfully logout!')
} catch(e) {
alert('Logout failure!')
console.log(e)
}
}
// ファイルアップロード用の関数
const fileUpload = async() => {
try {
const upload = new AWS.S3.ManagedUpload({
params: {
Bucket: BucketName,
Key: uploadFile.name,
Body: uploadFile
}
})
var promise = upload.promise();
promise.then(
function(data) {
alert("Successfully uploading file!");
setUploadFile('')
},
function(err) {
console.log(err)
return alert("There was an error uploading file!");
}
);
} catch(e) {
console.log(e)
}
}
if(!loginFlg){
return (
<div className="App py-4">
<div className="container">
<h1 className='mb-5'>Login</h1>
<div className="mb-3">
<label htmlFor="email" className="form-label">Email address</label>
<input value={email} onChange={(e) => setEmail(e.target.value) } type="email" className="form-control" id="email" placeholder="name@example.com"/>
</div>
<div className="mb-3">
<label htmlFor="password" className="form-label">Password</label>
<input value={password} onChange={(e) => setPassword(e.target.value)} type="password" className="form-control" id="password"/>
</div>
<button type="button" className="btn btn-primary" onClick={login}>ログイン</button>
</div>
</div>
);
} else {
return (
<div className="App py-4">
<div className="container">
<h1 className='mb-5'>ファイルアップロード</h1>
<div className="mb-3">
<input onChange={(e) => setUploadFile(e.target.files[0])} className="form-control" type="file" id="formFile"/>
</div>
<button type="button" className="btn btn-primary" onClick={fileUpload}>アップロード</button>
<button type="button" className="btn btn-secondary mx-2" onClick={logout}>ログアウト</button>
</div>
</div>
);
}
}
export default App;
ローカルサーバを起動します。
npm start
http://localhost:3000/にアクセスして、以下の画面が表示されれば成功です。
ログイン情報を登録する
最後にCognitoユーザプールにログイン情報(Eメール・パスワード)を登録します。
登録方法
- AWSコンソールから先ほど作成したCognitoユーザプールを選択する。
- 左側のメニューバーから全般設定-ユーザとグループを選択する。
- 「ユーザ」タブから「ユーザの作成」ボタンを押す。
- ユーザ名と仮パスワードを入力した後、「Eメールを検証済みにしますか?」以外のチェックを外して「ユーザの作成」を押す。
アカウントのステータス変更
これでログイン情報が作成されますが、初期状態ではアカウントのステータスが「FORCE_CHANGE_PASSWORD」(初期パスワードの変更が必要)となっており、このままでは使えません。
本来初期パスワード変更画面をアプリ側で作って対応する必要がありますが、以下のようにAWS CLIから「CONFIRMED」にステータスを変える方法もありますので、こちらでは割愛します。
これでユーザ登録が完了しました。Webアプリからログインできるようになっているはずですので、お試しください!
参照
AmazonCognitoデベロッパーガイド
AWS SDK for Javascryptデベロッパーガイド
amazon-cognito-identity-js