7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS Cognito と 認可コードフロー

Last updated at Posted at 2024-03-16

Cognitoと認可コードフロー

認可コードフローを実装する機会がありました。そう言えば以前にCognitoをなんとなく触ったなーというのもあり、Cognitoの理解を深めるためにも、Cognitoで認可コードフローを試してみようと思い、その際の完全個人的メモ(途中)です。

Amazon Cognito はウェブアプリとモバイルアプリ用のアイデンティティプラットフォームです。これは、ユーザーディレクトリ、認証サーバー、OAuth 2.0 アクセストークンと AWS 認証情報の承認サービスです。Amazon Cognito を使用すると、組み込みのユーザーディレクトリ、エンタープライズディレクトリ、Google や Facebook などのコンシューマー ID プロバイダーからユーザーを認証および承認できます。

OIDC(OpenID Connect)

  • OAuth 2.0プロトコルに基づいた認証レイヤー
  • ユーザーの認証情報とID情報を安全に交換するための標準規格
  • OIDC IDプロバイダーは、このOpenID Connect規格を使用してユーザーの認証を行い、認証されたユーザー情報をアプリケーションやサービス(クライアント)に提供するシステムやサービス

IDプロバイダ(Identity Provider、略してIdP)

  • ユーザー認証とユーザーのデジタルアイデンティティ情報を管理するサービスまたはシステム
  • IDプロバイダは、ユーザーが誰であるか(身元)を証明し、
  • そのユーザーがアクセスを試みているサービスやアプリケーション(サービスプロバイダ)に対して、その証明を提供
  • このプロセスは、シングルサインオン(SSO)やフェデレーション認証の一環として頻繁に行われます

スコープ

  • aws.cognito.signin.user.admin
    • Amazon Cognito ユーザー プールをAPIで操作する

エンドポイント

流れ

①認可リクエスト(/oauth2/authorize)
https://kinoko-test-oauth.auth.ap-northeast-1.amazoncognito.com/oauth2/authorize?client_id=6rroupfknqnntpkoe94n3u0kan&response_type=code&scope=email+openid&redirect_uri=http://localhost:8080

認可リクエストを投げるとCognitoの場合は以下のログインエンドポイント(/login)にリダイレクトされる
https://kinoko-test-oauth.auth.ap-northeast-1.amazoncognito.com/login?client_id=6rroupfknqnntpkoe94n3u0kan&response_type=code&scope=email+openid&redirect_uri=https://localhost:8080

②認可サーバー(CognitoやGoogleなど)にサインイン
コールバックURIにリダイレクトされ、認可コードが発行される
https://localhost:8080/?code=ba359276-39af-4532-8729-43d579b5bca1

③認可コードをつけて、トークンエンドポイントへリクエスト
(/oauth2/token)

④アクセストークンが発行される

⑤リソースサーバーにアクセス

ユーザープールの作成

image.png

image.png

認証済みユーザー

外部プロバイダーとあるのが、Cognitoプール以外(フェデレーテッドアイデンティティプロバイダー)でサインインしたユーザーになります。

image.png

フェデレーテッドアイデンティティプロバイダーの設定

image.png

Cognitoの認証画面

image.png

{
  "data": {
    "sub": "18b43b25-b462-428d-b1d2-5bd56b897645",
    "email_verified": "true",
    "name": "ki",
    "email": "mashandroom.kinoko@gmail.com",
    "username": "kinoko2"
  }
}

image.png

image.png

ID プロバイダーにGoogleを指定してみた

image.png

image.png

image.png

GoogleのOAuth2で指定可能なスコープは以下の通りです。

image.png

スクリーンショット 2024-03-16 20.12.59.png

GoogleのクライアントIDとクライアントシークレットを登録することで、CognitoからGoogle OAuthへ紐づけられるイメージですね。

image.png

IDプロバイダーにGoogleを追加することで、Google認証を選択できるようになります(ボタンが表示されます)。

image.png

以下の通り、SignIn画面にGoogleでのログインが追加されます。

image.png

Continue with Google を押すと以下にリクエストが投げられます。

https://kinoko-google-oauth.auth.ap-northeast-1.amazoncognito.com/login?client_id=5k5ihs3ucupfcbh17btapbffp2&response_type=code&scope=email+openid+profile&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauth
https://kinoko-google-oauth.auth.ap-northeast-1.amazoncognito.com/login Cognitoログインエンドポイント
client_id クライアントID
response_type 認可コードフロー
scope email+openid+profile
redirect_uri 認可後のリダイレクト先

以下の設定をもとに、上記リクエストURIが生成されています。

image.png

属性マッピング

Googleの属性をCognitoのどの属性にマッピングするか?を指定します。

image.png

アクセエストークンを取得後、 /oauth2/userInfo にリクエストを投げユーザー情報を取得したものを見ると、マッピングした属性が含まれていることがわかります。

{
  "data": {
    "sub": "xxxxx",
    "identities": "[{\"userId\":\"zzzzzz\",\"providerName\":\"Google\",\"providerType\":\"Google\",\"issuer\":null,\"primary\":true,\"dateCreated\":1710586402334}]",
    "email_verified": "false",
    "name": "キノコ",
    "given_name": "キノコ",
    "family_name": "マッシュ&ルーム",
    "email": "mashandroom.kinoko@gmail.com",
    "username": "google_yyyyyyyy"
  }
}

コールバックを受けるバックエンド

①〜④は、Cognitoの設定(図)を参照し、変更してください。

Node.js
const express = require("express");
const axios = require("axios");
const querystring = require("querystring");

const app = express();
const port = 3000;

const cognitoClientId = ""; // ①
const cognitoClientSecret = ""; // ②
const redirectUri = "http://localhost:3000/auth"; // ③
const cognitoDomain = ""; // ④

app.get("/auth", async (req: any, res: any) => {
  const code = req.query.code;
  console.log(code);

  const authString = `${cognitoClientId}:${cognitoClientSecret}`;
  const authHeader = `Basic ${Buffer.from(authString).toString("base64")}`;

  try {
    const response = await axios.post(
      `https://${cognitoDomain}/oauth2/token`,
      querystring.stringify({
        grant_type: "authorization_code",
        client_id: cognitoClientId,
        redirect_uri: redirectUri,
        code: code,
      }),
      {
        headers: {
          "Content-Type": "application/x-www-form-urlencoded",
          Authorization: authHeader,
        },
      }
    );

    const accessToken = response.data.access_token;

    const userInfoResponse = await axios.get(
      `https://${cognitoDomain}/oauth2/userInfo`,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );

    res.json({ data: userInfoResponse.data });
  } catch (error) {
    res.send("認証に失敗しました");
  }
});

app.listen(port, () => {
  console.log(`running on http://localhost:${port}.`);
});

image.png

image.png

image.png

ハマった

設定をしたつもりが、なぜかredirect_uri_mismatchが出てしまいました。
Google側の設定(承認済みの JavaScript 生成元,承認済みのリダイレクト URI)やCognitoの設定を何度も見直し、リダイレクトURLも正しくされているのに・・

image.png

↑ に出ている「エラーの詳細」は、以前にクリックしてドキュメントにリンクされるだけだと思っていて、なぜか押さずに数時間経過・・・いよいよわからん!と思って押してみたら、以下の通りでした・・・やっぱメッセージ確認するべきですね。

image.png

ということで、Google側の設定に、 https://kinoko-google-oauth.auth.ap-northeast-1.amazoncognito.com/oauth2/idpresponse を追加したところ正しく認証されリダイレクトされました。

最終的にGoogle側の設定は以下の通りです。

image.png

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?