概要
目次
- 概要
- Amazon Cognito とは
- OAuth と OpenID Connect とは
- 今回やったことは?
- 作業手順
- 1. Cognito でユーザープールの作成
- 2. アプリクライアントの作成と設定
- 3. AWS Cognito で試しにサインアップしてみる
- 4. 外部のソーシャル ID と連携させる
- まとめ
- 参考資料
なお、本記事で作成した OpenID Provider は GitLabのリポジトリ にあげております。
Amazon Cognito とは
- ウェブアプリケーションやモバイルアプリケーションの認証・認可・ユーザー管理をサポートしてくれるサービスです。
- 認証:その利用者が本人であるかを確認することを言います。ID とパスワードによって行うのが一般的です。
- 認可:認証されたユーザがあるシステム資源に対するアクセスを判断することを言います。認証されたユーザの権限に応じて、読み取りのみ/読み書きOK/管理者権限などを割り当てます。
- Cognito には大きく2つの機能があります。
- ユーザープール
- ID プール
- ユーザープールと ID プールは、別々にすることも一緒にすることも可能です。
OAuth と OpenID Connect とは
- OAuth は、認可の規格です。
- OpenID Connectは、OAuthのフローをベースにした認証の規格です。
- 認証・認可の仕組みの詳細については、下記の参考資料をご覧ください。
今回やったことは?
作業手順
大まかな流れは以下の通りです。
- Cognito でユーザープールを作成
- アプリクライアントの作成と設定
- AWS Cognito で試しにサインアップしてみる
- 外部ソーシャル ID と連携させる
1. Cognito でユーザープールの作成
- AWS マネジメントコンソールから「Cognito」を選択し、下の画面から「ユーザープールの作成」を選択します。
- 右上の「ユーザープールを作成する」より、新規にユーザープールを作成します。
- 適当な名前を設定し、「デフォルトを確認する」をクリックして次に進みます。
- 左サイドバーより「属性」を選び、ユーザー情報として必要な属性を設定します。「どの標準属性が必要ですか?」の下から、ユーザ登録時に必須の属性を指定します。指定し終えたら「次のステップ」を押して進みます。
- ここでの注意点は、必須の標準属性はユーザープール作成後、後から変更できないことです。最初に、アカウントのポリシーを決めておく必要があります。
- また、ここで選択した必須属性は、連携する外部のソーシャル ID からユーザ ID を連携させる際にも、取得できることが前提条件となります。
- 今回は、ユーザ ID としてメールアドレスを指定し、必須属性を指定しないというポリシーで進めます。
- 「ポリシー」の画面でパスワードポリシーを設定します。
- 今回は、緩い設定ですが、特殊文字・大文字を必須としない設定にしました。
- ここまでで一旦設定を終え、左サイドバーより「確認」を選択します。下の「プールの作成」を押し、ユーザープールを作成します。
2. アプリクライアントの作成と設定
- 続いて、作成したユーザプールを利用するアプリケーションを登録します。
- 左サイドバーより「アプリクライアント」を選択し、適当なアプリ名をつけて作成します。
- 今回は、「クライアントシークレットを生成」のチェックを外します。
- 「クライアントアプリの作成」を押すと、次画面でアプリクライアント ID が発行されます。
- 次に、左サイドバーより「アプリクライアントの設定」を選択します。ここで、先程作成したクライアントアプリと実際の Web アプリケーションとの紐付けを行います。
- 設定内容は以下のようにしておきます。終わったら、「変更の保存」を押して、内容を保存します。
- 有効な ID プロバイダ:「Cognito User Pool」にチェック
- コールバック URL:(暫定的に)https://localhost:3001
- サインアウト URL:(暫定的に)https://localhost:3001
- 許可されている OAuth フロー:「Authorization code grant」にチェック
- 許可されている OAuth スコープ:「email」と「openid」にチェック
- 次に、AWS Cognito ドメイン名を決定します。
- これは、外部ソーシャル ID のサーバが Cognito とやり取りするために使用されます。また、あらかじめ用意してくれるサインアップ/サインインの画面を呼び出すための URL でもあります。
- 他のCognito利用者と重複できないので、誰も使っていそうもない値を設定します。
3. AWS Cognito で試しにサインアップしてみる
- 実際に立ち上げた Web アプリにサインアップしてみます。
- 今回は、ngrok を利用して、ローカルで立ち上げたアプリを外部公開します。
- ngrok の詳しい使い方は、ngrokが便利すぎる - Qiita をご覧ください。
- 立ち上げるアプリは、 Node.js を用いて、クエリ文字列を表示するだけの簡単なものとします。
- 下記のようなコマンドで環境を整えた後、
index.js
を作成します。- コマンド:
- 下記のようなコマンドで環境を整えた後、
npm init npm install express -–save npm install cors -–save mkdir public
index.js
の内容(クリックで展開):
var express = require('express'); var app = express(); var cors = require('cors'); app.use(cors()); app.use(express.static('public')); app.get('/', (req, res) =>{ res.send('Welcome to Express!<br>' + 'QueryString:' + JSON.stringify(req.query) ); }); app.listen(3000, () =>{ console.log('HTTP Server(3000) is running.'); });
- アプリを立ち上げ、ngrok で公開します。
node index.js ngrok http 3000
https://XXXXXX.ngrok.io
というアドレスでアプリケーションが外部公開されてますので、これを利用します。- Cognito のユーザープール管理画面に戻り、「アプリクライアントの設定」でとりあえず埋めていた「コールバック URL」と「サインアウト URL」を上で発行された
[](https://XXXXXX.ngrok.io)
に書き換えます。 - 「アプリクライアントの設定」の画面にある「ホストされた UI の起動」リンクを押すか、ブラウザに以下の URL を入力して、Cognito が用意してくれるデフォルトのサインアップ画面を開きます。
https://<ドメイン名>.auth.ap-northeast-1.amazoncognito.com/login?response_type=code&client_id=<アプリクライアントID>&redirect_uri=<リダイレクト先URL>
まだユーザ登録していないため、「Need an account? Sign up」からサインアップ画面に行きメールアドレスとパスワードを入力して「Sign up」を押します。すると、入力したメールアドレスに認証コードを通知するメールが届くので、次画面でそれを入力します。
認証が無事終わり、次画面に遷移します。Cognito の管理画面より「ユーザーとグループ」より、ユーザが作成されていることも確認できます。
4. 外部のソーシャル ID と連携させる
- ここまで来てようやく外部のアカウントと連携できるようになります。
(1) Google アカウントの連携を有効にする
- Google アカウントとの連携の設定のためには、開発者アカウントが必要ですので、ない場合は作成します。
- Google Cloud Platform を開き、新しいプロジェクトが無い場合は作成します。
- プロジェクトを作成したら、左サイドバーの「API とサービス」を選択します。
- 「OAuth 同意画面」より、User Type を選択します。
- 今回は、組織外の人でも使える「外部」としました。
- また、「承認済みドメイン」に
amazoncognito.com
を追加しておきます。
- 次に、「認証情報」より、認証情報の作成を行います。作成対象として OAuth クライアント ID を選択し、次の画面で次のように入力します。
- アプリケーションの種類:ウェブアプリケーション
- 名前:任意(たとえば、Cognito Test)
- 認証済みのリダイレクトURI:
https://<ドメイン名>.auth.<リージョン>.amazoncognito.com/oauth2/idpresponse
- すると、「クライアント ID」と「クライアントシークレット」が生成されるため、メモっておきます。
- このクライアント ID とクライアントシークレットを Cognito に設定します。
- ユーザープールの設定画面より「ID プロバイダー」を選択し、先程のクライアント ID とクライアントシークレットを入力する。
- 承認スコープは「profile openid email」とする。
- Google から取得したメールアドレスをユーザプールに取り込むよう設定します。「属性マッピング」から Google の email にチェックを入れ、「ユーザプール属性」を Email とします。
- 左サイドバーより「アプリクライアントの設定」で「有効な ID プロバイダー」に Google が追加されているので、チェックする。
- ここまで設定した後、再度サインイン画面
https://<ドメイン名>.auth.<リージョン>.amazoncognito.com/login?response_type=code&client_id=<アプリクライアントID>&redirect_uri=<リダイレクト先URL>
を開くと、Google でログインするためのボタンが追加されています。 - Google での認証が済むと、次画面に遷移されます。また、Google アカウントとの連携の場合は、認証コードの入力なしに、ユーザ(「Google_…」で始まるユーザ)が作成されていることも確認できます。
(2) LINE のアカウント連携を有効にする
- 続いて、LINE アカウントとの連携です。こちらも、LINE のデベロッパーアカウントが必要なので、持っていない人は作成する必要があります。
- デベロッパーアカウントでログインをしたら、プロバイダーを作成し、その中に連携で用いるチャンネルを作成します。
- チャンネル作成時に設定するポイントは以下です。
- Channel Type:「LINE Login」にする
- App Types:「Web App」にチェックを入れる
- チャンネル作成後、Basic Settings のタブで、OpenID Connect を有効にします。
- チャンネル作成後、「Channel ID」と「Channel Seacret」が発行されますので、これを控えておきます。
- チャンネル作成時に設定するポイントは以下です。
- LINE Login の設定画面で「Callback URL」を
https://<ドメイン名>.auth.<リージョン>.amazoncognito.com/oauth2/idpresponse
に設定します。 - 再び、Cognito のユーザープールの設定画面に戻り、「ID プロバイダー」から LINE の設定を追加します。
- 「OpenID Connect」を選択し、以下のように設定を入力します。
- プロバイダ名:任意(「LINE」とでもしておきます)
- クライアント ID:LINE のチャンネル設定のところで確認した「Channel ID」の値
- クライアントのシークレット:LINE のチャンネル設定のところで確認した「Channel Secret」の値
- 属性のリクエストメソッド:「GET」を選択
- 承認スコープ:「profile email openid」を入力
- 発行者:「 https://access.line.me 」を入力
- この後、「検出の実行」ボタンを押します。失敗した旨のメッセージが出て入力欄が表示されるので、更に以下を入力します。
- 認証エンドポイント:https://access.line.me/oauth2/v2.1/authorize
- トークンエンドポイント:https://api.line.me/oauth2/v2.1/token
- ユーザ情報エンドポイント:https://api.line.me/v2/profile
- Jwks uri:不明 (なので、適当に入力しておく)
- 「OpenID Connect」を選択し、以下のように設定を入力します。
- あとは、Google の設定のときと同様に OIDC 属性の設定で Email を追加し、アプリクライアントの設定から LINE を追加します。
- デフォルトのサインイン画面
https://<ドメイン名>.auth.<リージョン>.amazoncognito.com/login?response_type=code&client_id=<アプリクライアントID>&redirect_uri=<リダイレクト先URL>
を開くと、LINE でのサインインボタンが追加されています。- ボタンを押すと、LINE でのログイン画面とアクセス許可を求められる画面に遷移します。OK して無事リダイレクト先画面に行った後、ユーザープールに「LINE_」で始まるユーザが追加されていることがわかります。
(3) Slack のアカウントと連携させる
- Slack のワークスペースのユーザのアカウントと連携させるには、一工夫必要です。最初にも述べたとおり、Slack のアカウントは OAuth2.0 の API の利用は可能であるものの、OpenID Connect に準拠していないためです。
- そこで、Slack と Cognito を中継するサーバ(OpenID Provider)を立ち上げて、認証処理のやり取りを行います。
① OpenID Provider の実装
- 今回実装した OpenID Provider の処理の流れは以下のようなイメージです。
- 前回利用した、aws-serverless-express を利用して、AWS Lambda + API Gateway で実装しました。
- ライブラリの追加
npm init npm install express -–save npm install request -–save npm install body-parser -–save npm install --save aws-serverless-express
OpenID Provider の
index.js
の実装(クリックで展開)
const request = require('request') const express = require('express'); const bodyParser = require('body-parser'); const isProd = () => { return !!process.env.AWS_REGION } const app = express(); app.use(bodyParser.urlencoded({ extended: true })); if (isProd()) { console.log('Environment: Production'); const awsServerlessExpressMiddleware = require("aws-serverless-express/middleware"); app.use(awsServerlessExpressMiddleware.eventContext()); } // slack が認可コードを送ってくる先の URL を設定 // 実際には、一旦デプロイした後、 // API Gateway のエンドポイント(https://xxxxxx.us-east-2.amazonaws.com/prod/) // を確認してから記載 const slack_redirect_url = 'https://xxxxxx.us-east-2.amazonaws.com/prod/slackendpoint' // Slackの認可コード受け取りのためのエンドポイント app.get('/slackendpoint', (req, res) => { // 認可コードの取得 const code = req.query["code"]; const url = `https://linkode-oidc-sample.auth.us-east-2.amazoncognito.com/oauth2/idpresponse?state=${req.query.state}&code=${code}&scope=openid` res.redirect(url); }); // Cognito からの認証要求を受け付けるエンドポイント app.get('/authorization', (req, res) => { console.log("auth", req) const redirect_uri = req.query.redirect_uri const state = req.query.state; const client_id = req.query.client_id; const url = `https://slack.com/oauth/authorize?client_id=${client_id}&scope=identify&redirect_uri=${slack_redirect_url}&state=${state}&target_uri=${redirect_uri}`; res.redirect(url); }); // Cognito からのアクセストークンの要求を受け付けるエンドポイント app.post('/token', (req, res, next) => { console.log("token", req); const client_id = req.body.client_id; const client_secret = req.body.client_secret; request({ url: "https://slack.com/api/oauth.access", method: "POST", form: { client_id: client_id, client_secret: client_secret, code: req.body.code, redirect_uri: slack_redirect_url } }, (error, response, body) => { res.setHeader('Content-Type', 'application/json'); const param = JSON.parse(body); const access_token = param['access_token'] // アクセストークン const j = { access_token: access_token, expires_in: 3600, "token_type": "Bearer", } res.send(j) }) }); // Cognito からのユーザー情報の要求を受け付けるエンドポイント app.get('/userinfo', (req, res) => { res.setHeader('Content-Type', 'application/json'); const access_token = req.headers.authorization.split(' ')[1]; console.log("userinfo", req) // ユーザIDを取得するためのリクエスト request("https://slack.com/api/auth.test", { method: "POST", form: { token: access_token } }, (error, response, body) => { const user = JSON.parse(body); console.log(user); // アクセストークンを使ってユーザ情報をリクエスト request("https://slack.com/api/users.info ", { method: 'POST', form: { token: access_token, user: user.user_id } }, (error, response, body) => { const params = JSON.parse(body).user; params.sub = params.id params.email = params.profile.email console.log(params) res.send(params) }) }) }) if (isProd()) { module.exports = app; } else { app.listen(4000, () => console.log(`Listening on: 4000`)); }
- その他、必要なスクリプトや設定ファイルをコピーし、内容を適宜変更する点は、下記の記事を参照:
②Slack アプリの設定
- Slack API: Applications | Slack から、新たに Slack アプリを立ち上げます。
- Basic Information に記載されている「Client ID」と「Client Secret」は後ほど利用するため、控えておきます。
- Slack 側からコールバックを受ける OpenID Provider のURL を指定します。①で立ち上げたサーバの ngrok で公開された URL を記載します。
- 記載する箇所は、左サイドバーより「OAuth & Permissions」を選び、「redirect URLs」のところです。
- 次に、スコープを設定します。同じ「OAuth & Permissions」の画面の下の「User Token Scopes」から、次の 2 つを許可します。
- users:read
- users:read.email
③Cognito の設定
LINE のときと同様に、ID プロバイダーの追加を行います。Cognito のユーザープール管理画面から「ID プロバイダー」の「OpenID Connect」を以下の値を入力して追加します。
- プロバイダ名:任意(「Slack」とでもしておきます)
- クライアント ID:Slack の「Client ID」
- クライアントシークレット:Slack の「Client Secret」
- 属性リクエストのメソッド:「GET」を指定
- 承認スコープ:「openid email」
- 発行者:適当で良い(ここでは https://slack.com としておく)
- 認証エンドポイント:https://xxxxxx.us-east-2.amazonaws.com/prod/authorization
- トークンエンドポイント:https://xxxxxx.us-east-2.amazonaws.com/prod/token
- ユーザー情報エンドポイント:https://xxxxxx.us-east-2.amazonaws.com/prod/userinfo
- Jwks url:任意(ここでは、https://xxxxxx.us-east-2.amazonaws.com/prod/jwks としておく)
属性マッピング、クライアントアプリの設定も忘れずにやります。
- 再度、サインイン画面を開くと、「Slack」が追加されています。
- ここから、Slack の認証の画面に飛び、同意すると Web アプリのページに飛びます。そして、ユーザープールに「Slack_」がついたユーザが追加されていることもわかります。
まとめ
- Amazon Cognito を用いて、ユーザー管理基盤であるユーザーープールを作成しました。
- 独自のサインアップ・サインインのみならず、外部ソーシャル ID との連携もできました。
参考資料
- The OAuth 2.0 Authorization Framework
- OAuth 2.0 の公式ドキュメントです。
- OpenID Connect | OpenID
- OpenID Connect の公式ドキュメントです。
- OAuth 2.0 + OpenID Connect のフルスクラッチ実装者が知見を語る - Qiita
- OAuth2.0とOpenID Connect の概要 - Qiita
- 上記 2 つは、OAuth と OpenID Connect の違いや概要について参照しました。
- 一番分かりやすい OpenID Connect の説明 - Qiita
- 多分わかりやすいOpenID Connect – SIOS Tech. Lab
- 上記 2 つは、Open ID Connect について参考にしました。
- AWS CognitoにGoogleとYahooとLINEアカウントを連携させる - Qiita
- Google と LINE のアカウントの連携方法について参照しました
- Amazon CognitoとSlackで無理矢理OpenID Connect - Qiita
- こちらを参考に、OpenID Provider を用意して、Slack アカウントと連携させました。
- OIDC ユーザープール IdP 認証フロー
- Amazon Cognito ユーザープール の Auth API リファレンス