[React]認証でCognitoを辞めた話

## Cognitoを検討した理由
- Googleなどのシングルサインオンを検討していた
- RDSをできるなら使いたくなかった
- ワンタイムパスワードなど丸投げしたかった


## 使わなくなった理由
- ロックインされる
- ユーザーをRDSなどに移したいときに移すのがめんどくさい
- CognitoUIがダサいし意外と使うのがめんどくさい
- シングルサインオンのアクセストークンをそのまま使う方が楽

## CLoudFormationで構築

下のテンプレートのcallbackとlogoutを正しいのに置き換えてデプロイします。

```yaml
AWSTemplateFormatVersion: '2010-09-09'

Parameters:
  ProjectName:
    Description: 'Name of the project'
    Type: String
    Default: 'myproject'

  Env:
    Description: 'Environment (e.g., prod, dev, staging)'
    Type: String
    AllowedValues:
      - prod
      - dev
      - staging
    Default: 'dev'

  CallbackURL:
    Description: 'Callback URL after successful authentication'
    Type: String
    Default: 'http://localhost:8080/callback'

  LogoutURL:
    Description: 'URL to redirect after logout'
    Type: String
    Default: 'http://localhost:8080/logout'

Resources:
  MyUserPool:
    Type: 'AWS::Cognito::UserPool'
    Properties: 
      UserPoolName: !Sub '${ProjectName}-${Env}-UserPool'
      AutoVerifiedAttributes: 
        - email
      EmailVerificationMessage: 'Your verification code is {####}.'
      EmailVerificationSubject: 'Your verification code'
      MfaConfiguration: 'OFF'
      Policies:
        PasswordPolicy:
          MinimumLength: 8
          RequireUppercase: true
          RequireLowercase: true
          RequireNumbers: true
          RequireSymbols: true
      AdminCreateUserConfig:
        AllowAdminCreateUserOnly: false


  MyUserPoolClient:
    Type: 'AWS::Cognito::UserPoolClient'
    Properties:
      UserPoolId: 
        Ref: 'MyUserPool'
      ClientName: !Sub '${ProjectName}-${Env}-UserPoolClient'
      GenerateSecret: false
      SupportedIdentityProviders:
        - 'COGNITO'
      ExplicitAuthFlows:
        - 'ALLOW_USER_SRP_AUTH'
        - 'ALLOW_REFRESH_TOKEN_AUTH'
      CallbackURLs:
        - !Ref CallbackURL
      LogoutURLs:
        - !Ref LogoutURL
      AllowedOAuthFlows:
        - 'code'
      AllowedOAuthFlowsUserPoolClient: true
      AllowedOAuthScopes:
        - 'openid'

  MyUserPoolDomain:
    Type: 'AWS::Cognito::UserPoolDomain'
    Properties:
      UserPoolId:
        Ref: 'MyUserPool'
      Domain: !Sub '${ProjectName}-${Env}-userpooldomain'

Outputs:
  UserPoolId:
    Description: "The ID of the created Cognito User Pool"
    Value: 
      Ref: "MyUserPool"

  UserPoolClientId:
    Description: "The ID of the created Cognito User Pool Client"
    Value: 
      Ref: "MyUserPoolClient"

  UserPoolDomain:
    Description: "The domain of the created Cognito User Pool"
    Value: 
      !Sub "${ProjectName}-${Env}-userpooldomain.auth.${AWS::Region}.amazoncognito.com"

  CallbackURL:
    Description: "Callback URL after successful authentication"
    Value:
      !Ref "CallbackURL"

  LogoutURL:
    Description: "URL to redirect after logout"
    Value:
      !Ref "LogoutURL"
```

## Reactでログインページに飛ぶ

### 環境変数を設定

必要なパラメーターはすべて先ほどアップロードしたCloudformationのOutputから取れます。
.envファイルはreactのルートディレクトリ入れてください。

```:.env
REACT_APP_COGNITO_DOMAIN=
REACT_APP_COGNITO_CLIENT_ID=
REACT_APP_COGNITO_CALLBACK_URL=
```

reactで環境変数を読み込めるようにします

```typescript:/src/utils/cognitoConfigs.ts
export const getCognitoLoginUrl = (): string => {
    const domain = process.env.REACT_APP_COGNITO_DOMAIN ?? 'your-default-domain';
    const clientId = process.env.REACT_APP_COGNITO_CLIENT_ID ?? 'your-default-client-id';
    const callbackUrl = process.env.REACT_APP_COGNITO_CALLBACK_URL ?? 'your-default-callback-url';
    const redirectUri = encodeURIComponent(callbackUrl);
    const scope = encodeURIComponent('openid'); // ここで必要なスコープを追加する

    // URLを生成し、response_typeを'code'に設定する
    return `https://${domain}/login?response_type=code&client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}`;
};

```

これでgetCognitoLoginUrlという関数でurlを作成してアクセスをするだけでCognitoのHostUIに行くことができます。

## Callbackのページを作る

ここでは敢えてコードで書くことでもないので解説だけにします。
Reactの環境変数で指定したURLにSignInが成功するとcodeというクエリーパラメーターと共にリダイレクトされます。

そのコードをクライアントシークレットを設定していない場合はcognitoのあるエンドポイントにリクエストを送るとアクセストークンを取得することできます。