Laravelã§ Amazon Cognito ログインèªè¨¼1ã€ãƒ­ã‚°ã‚¤ãƒ³æ©Ÿèƒ½ã€‘

世ã®ä¸­ã®ãƒ­ã‚°ã‚¤ãƒ³æ©Ÿèƒ½ã®å¤§åŠã‚’å ã‚ã¦ã„ã‚‹OAuthã‚„OIDCã®ä»•組ã¿ã§ã™ãŒã€ã‚ã‹ã£ã¦ã„るよã†ã§ã‚¤ãƒžã‚¤ãƒç†è§£ã—ã¦ã„ãªã„ãªã¨æ€ã£ã¦ãŠã‚Šã¾ã—ãŸã€‚

最近業務ã§ãƒ­ã‚°ã‚¤ãƒ³æ©Ÿèƒ½ã«ã¤ã„ã¦è§¦ã‚Œã‚‹æ©Ÿä¼šãŒã‚りã€ã“れを機ã«å®Ÿéš›ã«å®Ÿè£…ã—ãªãŒã‚‰ç†è§£ã‚’æ·±ã‚よã†ã¨æ€ã„ç«‹ã¡ã¾ã—ã¦æƒ…報を整ç†ã—ã¦ã„ãã¾ã™ã€‚

今回ã¯AWSã®èªè¨¼ã‚µãƒ¼ãƒ“スã§ã‚ã‚‹Amazon Cognitoを利用ã—ã¦å®Ÿè£…を進ã‚ã¦ã„ãã¾ã™ã€‚

■やりãŸã„ã“ã¨

Cognitoを利用ã—ãŸãƒ­ã‚°ã‚¤ãƒ³æ©Ÿèƒ½ã‚’実ç¾ã™ã‚‹

ログイン機能をカスタマイズã™ã‚‹

Cognitoã§ã®ä¼šå“¡ç™»éŒ²æƒ…報を連æºã™ã‚‹

â– å‚考URL

https://qiita.com/tomoeine/items/40a966bf3801633cf90f

Laravelã§ãƒ­ã‚°ã‚¤ãƒ³èªè¨¼ã‚·ãƒªãƒ¼ã‚º

  1. Laravelã§ Amazon Cognito ログインèªè¨¼1ã€ãƒ­ã‚°ã‚¤ãƒ³æ©Ÿèƒ½ã€‘ â†ã€€ã‚¤ãƒžã‚³ã‚³
  2. Laravelã§ Amazon Cognito ログインèªè¨¼2ã€ä¼šå“¡ç™»éŒ²æ©Ÿèƒ½ã€‘

完æˆç³»ã®ç”»é¢ãƒ•ロー

Cognito Laravelログイン画é¢ãƒ•ロー

ã„ãã¤ã‹ã‚„り方ã¯ã‚りã¾ã™ãŒã€ä»Šå›žã¯Amazon Cognitoã®ãƒ­ã‚°ã‚¤ãƒ³ç”»é¢ã‚’利用ã—ã¦å®Ÿç¾ã™ã‚‹æ–¹æ³•ã§ã™ã€‚

Laravelã‹ã‚‰Amazon Cognitoã§ç”¨æ„ã•れã¦ã„ã‚‹èªå¯ã‚¨ãƒ³ãƒ‰ãƒã‚¤ãƒ³ãƒˆã¸ã‚¢ã‚¯ã‚»ã‚¹ã—ã€èªå¯ã‚³ãƒ¼ãƒ‰ã‚’å–å¾—ã—ã¾ã™ã€‚

※ホストã•れãŸWEB UI(AWSå´ã§æº–å‚™ã•れãŸãƒ­ã‚°ã‚¤ãƒ³ç”»é¢ï¼‰ã‚’利用ã—ã¦ã„ã¾ã™ã€‚ã“ã®ç”»é¢ã¯æ—¥æœ¬èªžåŒ–ã§ãã¾ã›ã‚“。
大事ãªã“ã¨ãªã®ã§ã‚‚ã†ä¸€åº¦è¨€ã„ã¾ã™ã€‚日本語化ã§ãã¾ã›ã‚“。

Amazon Cognitoã®è¨­å®š

Cognitoã«ã¯ã€Œãƒ¦ãƒ¼ã‚¶ãƒ—ールã€ã¨ã€ŒIDプールã€ã¨ã„ã†ï¼’ã¤ã®ç®¡ç†ãŒã‚りã¾ã™ã€‚ãれãžã‚Œã®é•ã„ã¯ä¸‹è¨˜ã®é€šã‚Šã§ã™ã€‚今回ã¯ä¸€èˆ¬ãƒ¦ãƒ¼ã‚¶å‘ã‘ãªã®ã§ã€Œãƒ¦ãƒ¼ã‚¶ãƒ—ールã€ã‚’利用ã—ã¾ã™ã€‚

  • ユーザプール:会員サイトãªã©ã§ãƒ¦ãƒ¼ã‚¶ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’管ç†ã™ã‚‹å ´åˆã«åˆ©ç”¨
  • IDプール:組織ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆç®¡ç†ãªã©ã«åˆ©ç”¨

ユーザプールã®è¨­å®š

今回ã¯ã‚¹ãƒ†ãƒƒãƒ—ã«å¾“ã£ã¦ç´°ã‹ã設定ã—ã¦ã„ãã¾ã™ã€‚

デフォルトを確èªã—ãªãŒã‚‰é€²ã‚ã¦é ‚ã„ã¦ã‚‚å•題ã”ã–ã„ã¾ã›ã‚“。

Cognitoユーザプールã®è¨­å®š

ã¾ãšã¯ãƒ¦ãƒ¼ã‚¶ã®ã‚µã‚¤ãƒ³ã‚¤ãƒ³æ–¹æ³•ã§ã™ã€‚今回ã¯ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã§ãƒ­ã‚°ã‚¤ãƒ³ã•ã›ã‚‹ãŸã‚「Eメールã€ã®è¨­å®šã‚’é¸æŠžã—ã¾ã™ã€‚

ä»–ã«ã‚‚ユーザåや電話番å·ã§ã®è¨­å®šã‚‚å¯èƒ½ã§ã™ã€‚

Cognitoユーザプールã®è¨­å®š

次ã«ãƒ‘スワード強度ã¨è‡ªå·±ã‚µã‚¤ãƒ³ã‚¢ãƒƒãƒ—ã®è¨­å®šã§ã™ã€‚

パスワード強度ã¯ãƒ‡ãƒ•ォルトã®ã¾ã¾ã§å•題ã‚りã¾ã›ã‚“。セキュリティãƒãƒªã‚·ãƒ¼ãªã©ãŒã‚ã‚‹å ´åˆã¯ã€ãƒãƒªã‚·ãƒ¼ã«å¾“ã£ã¦è¨­å®šã‚’ã—ã¾ã™ã€‚

自己サインアップã¯ãƒ‡ãƒ•ォルトã§è¨±å¯ã•れã¦ã„ã‚‹ã®ã§ã“ã®ã¾ã¾ç™»éŒ²ã—ã¾ã™ã€‚会員制サイトãªã©ã§ç®¡ç†è€…ã«ã®ã¿ãƒ¦ãƒ¼ã‚¶ä½œæˆã®æ¨©é™ã‚’与ãˆã‚‹å ´åˆã«ã¯è¨­å®šã‚’変更ã—ã¾ã™ã€‚

Cognitoユーザプールã®è¨­å®šã€€ãƒ‘スワード強度ãªã©

多è¦ç´ èªè¨¼ã®è¨­å®šã§ã™ã€‚Cognitoã§ã¯ç°¡å˜ã«å¤šè¦ç´ èªè¨¼ãŒå®Ÿç¾ã§ãã¾ã™ã€‚

今回ã¯ãƒ­ã‚°ã‚¤ãƒ³èªè¨¼ã®ãƒ†ã‚¹ãƒˆã®ã¿ã®ãŸã‚ã€è¨­å®šã¯ã‚ªãƒ•ã«ã—ã¾ã™ã€‚後ã‹ã‚‰è¨­å®šã‚’変ãˆã§å¤šè¦ç´ èªè¨¼ã‚’利用ã™ã‚‹ã“ã¨ã‚‚å¯èƒ½ã§ã™ã€‚

Cognitoユーザプールã®è¨­å®šã€€å¤šè¦ç´ èªè¨¼

ã“ã¡ã‚‰ã¯ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹æ¤œè¨¼ã®ãƒ¡ãƒ¼ãƒ«é€ä¿¡æ™‚ã®è¨­å®šã§ã™ã€‚fromメールアドレスを変更ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚

Amazon SESを経由ã—ã¦ãƒ¡ãƒ¼ãƒ«ã‚’é€ä¿¡ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ãŒã€ä»Šå›žã¯åˆ©ç”¨ã—ã¦ãŠã‚Šã¾ã›ã‚“。

キャプãƒãƒ£ã—ãれã¦ãŠã‚Šã¾ã›ã‚“ãŒã€é€ä¿¡ã™ã‚‹ãƒ¡ãƒ¼ãƒ«æœ¬æ–‡ã®ã‚«ã‚¹ã‚¿ãƒžã‚¤ã‚ºãªã©ã‚‚ã“ã¡ã‚‰ã®è¨­å®šã§å¯èƒ½ã§ã™ã€‚

Cognitoメールアドレスèªè¨¼ã®è¨­å®š

アプリクライアントã®è¨­å®šã§ã™ã€‚アプリクライアントåã¯ãƒ­ã‚°ã‚¤ãƒ³æ©Ÿèƒ½ã‚’実装ã™ã‚‹éš›ã«ã‚³ãƒ¼ãƒ‰ã¨ã—ã¦åˆ©ç”¨ã—ã¾ã™ã€‚

ãã®ä»–ã®è¨­å®šã¯ãƒ‡ãƒ•ォルトã®ã¾ã¾ã§ã‚‚å•題ã”ã–ã„ã¾ã›ã‚“。

Cognitoアプリクライアントã®è¨­å®š

今回ã¯èªè¨¼ãƒ•ローã¨ã—ã¦æ›´æ–°ãƒˆãƒ¼ã‚¯ãƒ³ãƒ™ãƒ¼ã‚¹ã®èªè¨¼ã‚’利用ã—ã¾ã™ã€‚デフォルトã§ãƒã‚§ãƒƒã‚¯æ¸ˆã¿ã§ã™ã€‚

ä»–ã«ã‚‚デフォルトã§ãƒã‚§ãƒƒã‚¯ã•れã¦ã„ã‚‹ã‚‚ã®ãŒã‚りã¾ã™ãŒã€ä»Šå›žã®ãƒ•ローã§ã¯åˆ©ç”¨ã—ãªã„ã®ã§å¤–ã—ã¦ã‚‚å•題ã”ã–ã„ã¾ã›ã‚“。

Cognitoèªè¨¼ãƒ•ローã®è¨­å®š

ã“れã§Cognitoã®è¨­å®šã¯å®Œäº†ã§ã™ã€‚

会員準備

設定ãŒå®Œäº†ã—ãŸã‚‰ã€å…ˆã«æ¤œè¨¼ç”¨ã®ãƒ¦ãƒ¼ã‚¶ã‚’作æˆã—ã¦ãŠãã¾ã™ã€‚ã“ã®ãƒ¦ãƒ¼ã‚¶ã§Laravelã¨Cognitoã®èªè¨¼é€£æºã®æ¤œè¨¼ã‚’行ã„ã¾ã™ã€‚

会員登録ã¯ã€Œãƒ¦ãƒ¼ã‚¶ã¨ã‚°ãƒ«ãƒ¼ãƒ—ã€ã®è¨­å®šã‹ã‚‰è¡Œã„ã¾ã™ã€‚

Cognito会員登録

ユーザã®ä½œæˆã‹ã‚‰é€²ã‚“ã§ã„ãã¨ã€ä¸‹è¨˜ã®ç”»é¢ãŒè¡¨ç¤ºã•れã¾ã™ã€‚今回ã¯ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã¨ãƒ‘スワードã®ã¿ã§ãƒ­ã‚°ã‚¤ãƒ³ã§ãるよã†ã«ã™ã‚‹ãŸã‚ã€é›»è©±ç•ªå·ã®å…¥åŠ›ã¯ä¸è¦ã§ã™ã€‚

ユーザåも利用ã—ãªã„ã®ã§ã¨ã‚Šã‚ãˆãšãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’入れã¦ãŠã‘ã°å¤§ä¸ˆå¤«ã§ã™ã€‚

Cognito会員登録

メールアドレスã®é‡è¤‡ãƒã‚§ãƒƒã‚¯ã¯ãƒ‡ãƒ•ォルトã§å®Ÿæ–½ã•れã¦ã„ã¾ã™

ユーザã®ä½œæˆã‚’押下ã™ã‚‹ã¨ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã«æ¤œè¨¼ã‚³ãƒ¼ãƒ‰ãŒé€ä»˜ã•れã¾ã™ã®ã§ã€ã‚³ãƒ¼ãƒ‰ã‚’入力ã—ã¦èªè¨¼ã‚’行ã„ã¾ã™ã€‚

「Eã‚ーるã‚ã©ã‚Œã™ã‚’検証済ã¿ã«ã—ã¾ã™ã‹ï¼Ÿã€ã®ãƒã‚§ãƒƒã‚¯ã‚’入れã¦ãŠãã¨ã“ã®ä½œæ¥­ã¯ä¸è¦ã«ãªã‚Šã¾ã™ã€‚

Cognito会員登録 メールアドレスèªè¨¼
Cognito会員登録 メールアドレスèªè¨¼ãƒ‘スコード

ã“ã‚Œã§æ¤œè¨¼ç”¨ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®æº–備も完了ã—ã¾ã—ãŸã€‚

ã“ã®å¾Œã®ãƒ­ã‚°ã‚¤ãƒ³æ©Ÿèƒ½ã®æ¤œè¨¼ã§åˆ©ç”¨ã—ã¾ã™ã®ã§ã€ãƒ‘スワードを忘れãªã„よã†ã«æŽ§ãˆã¦ãŠãã¾ã—ょã†ã€‚

Laravelã§ã®ãƒ­ã‚°ã‚¤ãƒ³æ©Ÿèƒ½å®Ÿè£…

Laravelã§ã¯èªè¨¼é–¢é€£ã®æ©Ÿèƒ½ãŒä¸€é€šã‚Šç”¨æ„ã•れã¦ã„ã¾ã™ã€‚ä»Šå›žã®æ¤œè¨¼ã‚‚ã“ã®æ©Ÿèƒ½ã‚’ベースã«åˆ©ç”¨ã—ã¦ã„ãã¾ã™ã€‚

php artisan make:auth

ã“れã§èªè¨¼æ©Ÿèƒ½ã‚’ä½¿ã†æº–å‚™ãŒã§ãã¾ã—ãŸã€‚

Controllerã‚„ç”»é¢viewãªã©ä¸€é€šã‚Šã®ã‚½ãƒ¼ã‚¹ãŒä½œæˆã•れã¦ã„ã¾ã™ã€‚

Laravelã§ã®èªè¨¼æ©Ÿèƒ½ãŠã•らã„

実装を始ã‚ã‚‹ã«Laravelã§ã®ãƒ­ã‚°ã‚¤ãƒ³æ©Ÿèƒ½ã‚’ãŠã•らã„ã„ãŸã—ã¾ã™ã€‚

ãƒã‚¤ãƒ³ãƒˆã¯2点

Guard

èªè¨¼ã«ã‹ã‹ã‚るユースケースやèªè¨¼æ–¹æ³•ãŒå®šç¾©ã•れã¦ã„ã¾ã™ã€‚

èªè¨¼ã®æ–¹æ³•ã”ã¨ã«ã‚¯ãƒ©ã‚¹ãŒæº–å‚™ã•れã¦ãŠã‚Šã€ã‚ˆã使ã†ã®ã¯ SessionGuard ã¨ã„ã†ã‚¯ãƒ©ã‚¹ã§ã™ã€‚

src/Illuminate/Auth/SessionGuard.php

多数ã®å‡¦ç†ãŒè¨˜è¿°ã•れã¦ã„ã¾ã™ãŒã€é‡è¦ãªãƒ¡ã‚½ãƒƒãƒ‰ã ã‘ピックアップã—ã¦è§£èª¬ã—ã¾ã™ã€‚

メソッド解説
attemptèªè¨¼æƒ…報をパラメータã¨ã—ã¦å—ã‘å–りèªè¨¼ã‚’試ã¿ã¾ã™ã€‚èªè¨¼ã«ã‚ˆã‚Šã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’特定ã™ã‚‹å‡¦ç†ã¯å¾Œè¿°ã®ProviderãŒè¡Œã„ã¾ã™ã€‚
èªè¨¼å¯¾è±¡ã®ãƒ¦ãƒ¼ã‚¶ã‚’特定ã—ãŸã‚ã¨ã€èªè¨¼å¾Œå‡¦ç†ã¯ login メソッドを呼ã³å‡ºã—ã¾ã™ã€‚
loginユーザ情報をå—ã‘å–りèªè¨¼å¾Œã®å‡¦ç†ã‚’行ã„ã¾ã™ã€‚具体的ã«ã¯ã‚»ãƒƒã‚·ãƒ§ãƒ³ã‚’張りログイン状態を維æŒã§ãるよã†ã«ã—ã¾ã™ã€‚
userログイン済ã¿ã®ãƒ¦ãƒ¼ã‚¶æƒ…報をå–å¾—ã—ã¾ã™ã€‚Guardインスタンスã‹ã‚‰ãƒ¦ãƒ¼ã‚¶æƒ…å ±ãŒå–å¾—ã§ããªã‹ã£ãŸå ´åˆã¯ã€ã‚»ãƒƒã‚·ãƒ§ãƒ³ã‹ã‚‰ã®å–得を試ã¿ã¾ã™ã€‚
ログインãŒå¿…è¦ãªãƒšãƒ¼ã‚¸ã«ã‚¢ã‚¯ã‚»ã‚¹ã—ãŸéš›ãªã©ã«å‘¼ã°ã‚Œã¦ã„るよã†ã§ã™ã€‚

Provider

èªè¨¼ã®ãƒ¡ã‚¤ãƒ³å‡¦ç†ãŒè¨˜è¿°ã•れã¦ã„ã¾ã™ã€‚Laravelèªè¨¼ã®å¿ƒè‡“部ã§ã™ã€‚

src/Illuminate/Auth/EloquentUserProvider.php

ã“ã®å†…部処ç†ã‚’確èªã™ã‚‹ã“ã¨ã¯ã»ã¨ã‚“ã©ãªã„ã¨æ€ã„ã¾ã™ã®ã§ã€èª¬æ˜Žã¯å‰²æ„›ã—ã¾ã™ã€‚

å‚考サイト

https://qiita.com/tomoeine/items/40a966bf3801633cf90f

ï¼£onfigファイルã®è¨­å®š

実装を行ã£ã¦ã„ãå‰ã«Guardã¨ProviderãŒconfigファイル上ã®ã©ã“ã§è¨­å®šã•れã¦ã„ã‚‹ã‹ç¢ºèªãƒ»å¤‰æ›´ã—ã¦ã„ãã¾ã™ã€‚

設定ãŒè¨˜è¼‰ã•れã¦ã„ã‚‹ã®ã¯configディレクトリã®auth.phpã«ãªã‚Šã¾ã™ã€‚

    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

     'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
    ],

デフォルトã§åˆ©ç”¨ã™ã‚‹GuardãŒæŒ‡å®šã•れã€Guardã®ä¸­ã§åˆ©ç”¨ã™ã‚‹driverã¨providerãŒæŒ‡å®šã•れã¦ã„ã¾ã™ã€‚

今回ã¯ã“ã“ã«æ–°ãŸã«cognitoã¨ã„ã†Guardを追加ã—ã¦ã„ãã¾ã™ã€‚

    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],


        'cognito' => [
            'driver' => 'cognito',
            'provider' => 'users',
            'hash' => false,
        ],
    ],

driverã®è¨­å®šã¯Guardã®åç§°ã®ã‚ˆã†ãªã‚‚ã®ã§ã™ã€‚Guardã®ã‚«ã‚¹ã‚¿ãƒ ã‚¯ãƒ©ã‚¹ã‚’作æˆã™ã‚‹éš›ã«ã€Œdriverã®åç§°ã€+「Guardã€ã¨ã„ã†ã‚¯ãƒ©ã‚¹åã«ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚

今回ã¯cognitoã¨ã„ã†driveråã«ã—ã¦ã„ã‚‹ã®ã§ã€Guardã®ã‚«ã‚¹ã‚¿ãƒ ã‚¯ãƒ©ã‚¹ã¯CognitoGuardã§ä½œæˆã—ã¾ã™ã€‚ã“ã¡ã‚‰ã¯å¾Œã»ã©ã€‚

cognito連æº

cognito連æºç”¨ã®ã‚¯ãƒ©ã‚¹ã‚’作æˆã—ã¾ã™ã€‚

å¿…è¦ãªã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆã¯SDKをインストールã—ã¦åˆ©ç”¨ã•ã›ã¦ã‚‚らã„ã¾ã™ã€‚

composer require aws/aws-sdk-php

Cognito連æºç”¨ã®ã‚¯ãƒ©ã‚¹ã¯ã“ã¡ã‚‰ã«ãªã‚Šã¾ã™ã€‚

<?php

namespace App\Cognito;
use Aws\CognitoIdentity\Exception\CognitoIdentityException;
use Aws\CognitoIdentityProvider\CognitoIdentityProviderClient;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;


class CognitoClient
{

    protected $client;
    protected $clientId;
    protected $poolId;
    protected $authorization;

    public function __construct(CognitoIdentityProviderClient $client)
    {

        $this->client       = $client;
        $this->clientId     = config('cognito.clientId');
        $this->poolId       = config('cognito.poolId');
        $this->authorization = base64_encode(config('cognito.clientId').':'.config('clientId.clientSecret'));

    }

    public function fetchTokenEndpoint($code,$code_challenge){

        try{
            $response = Http::asForm()
                ->contentType('application/x-www-form-urlencoded')
                ->retry(3, 100)
                ->post(config('cognito.token_endpoint'), [
                    'client_id' => $this->getClientId(),
                    'grant_type' => 'authorization_code',
                    'code' =>  $code,           // 必須 èªå¯ã‚¨ãƒ³ãƒ‰ãƒã‚¤ãƒ³ãƒˆã®ãƒ¬ã‚¹ãƒãƒ³ã‚¹ã«å«ã¾ã‚Œã‚‹å€¤ã‚’指定
                    'redirect_uri' => config('cognito.callback_uri'),          // èªå¯ãƒªã‚¯ã‚¨ã‚¹ãƒˆã« redirect_uri ãŒå«ã¾ã‚Œã¦ã„れã°å¿…é ˆ
                    'client_secret' => config('cognito.clientSecret'),
                ]);

            if(!$response->successful()){
                throw new \Exception('status code error:'.$response->status());
            }

        }catch (CognitoIdentityException $e){

            return null;
        }catch (\Exception $e){

            return null;
        }

        return json_decode($response);
    }

    public function getClientId(){
        return $this->clientId;
    }

}


ãƒã‚¤ãƒ³ãƒˆã€€Http::asForm()

content typeを「x-www-form-urlencodedã€ã¨ã—ã¦ãƒªã‚¯ã‚¨ã‚¹ãƒˆã™ã‚‹å ´åˆã€Laravelã§ã¯asFormを利用ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚

フォームURLエンコードã•れãŸãƒªã‚¯ã‚¨ã‚¹ãƒˆã®é€ä¿¡

application/x-www-form-urlencodedコンテンツタイプを使用ã—ã¦ãƒ‡ãƒ¼ã‚¿ã‚’é€ä¿¡ã™ã‚‹å ´åˆã¯ã€ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’行ã†å‰ã«asFormメソッドを呼ã³å‡ºã™å¿…è¦ãŒã‚りã¾ã™ã€‚

Laravel 8.x HTTPクライアント

å¿…è¦ãªè¨­å®šæƒ…報等ã¯configファイルã«ã¾ã¨ã‚ã¾ã—ãŸã€‚æ–°è¦ã§ã€Œcognitoã€ç”¨ã®è¨­å®šãƒ•ァイルを作æˆã—ã¦ã„ã¾ã™ã€‚

<?php
return  [

    'clientId' => '{clientId}',
    'clientSecret' => '{clientSecret}',
    'poolId' => 'ap-northeast-1_{your pool id}',
    'region' =>  'ap-northeast-1',
    'version' =>  'latest',
    'endpoint' => 'https://{your domain}.auth.ap-northeast-1.amazoncognito.com/',
    'token_endpoint' => 'https://{your domain}.auth.ap-northeast-1.amazoncognito.com/oauth2/token',
    'callback_uri' => 'http://localhost:8000/login_cognito/callback/',
    'jwk' => 'https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_EAVdUIDWB/.well-known/jwks.json',

];

clientIdãªã©ã¯ã”自身ã®ç’°å¢ƒã«å¿œã˜ã¦é©å®œè¨­å®šãã ã•ã„。

コールãƒãƒƒã‚¯URIã¯é–‹ç™ºç”¨ã«ãƒ­ãƒ¼ã‚«ãƒ«ãƒ›ã‚¹ãƒˆã«è¨­å®šã—ã¦ã„ã¾ã™ã€‚ã“れã¯Cognitoã®è¨­å®šç”»é¢ã«ç™»éŒ²ã—ãŸã‚‚ã®ã¨åŒã˜è¨­å®šã«ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚

jwt検証用クラス作æˆ

jwtã®æ¤œè¨¼ã®ãŸã‚ã€firebase/php-jwtを利用ã•ã›ã¦é ‚ãã¾ã™ã€‚

composer require firebase/php-jwt

jwtã®æ¤œè¨¼ã¯ã“ã®ã‚ˆã†ã«å®Ÿè£…ã—ã¦ã„ã¾ã™ã€‚

<?php

namespace App\Services;

use Exception;
use Firebase\JWT\JWK;
use Firebase\JWT\JWT;
use Illuminate\Support\Facades\Http;
use UnexpectedValueException;

class JWTVerifier
{
    /**
     * @param string $jwt
     * @return object|null
     * @throws Exception
     */
    public function decode(string $jwt)
    {
        $tks = explode('.', $jwt);
        $header = $tks[0];
        $jwks = $this->fetchJWKs();

        try {
            $kid = $this->getKid($header); //jwtã‹ã‚‰kidã‚’å–å¾—
            $alg = $this->getAlg($jwks, $kid);//ç½²åアルゴリズムをå–å¾—
            $key = $this->getKey($jwks, $kid,$alg);//jwkã‹ã‚‰decode用ã®keyã‚’å–å¾—

            return JWT::decode($jwt, $key);
        }catch (UnexpectedValueException $e){
            throw new Exception('ãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ä¸æ­£');
            return null;
        }catch (Exception $exception) {
            throw $exception;
            return null;
        }
    }

    private function getKid(string $headb64)
    {
        $headb64 = json_decode(JWT::urlsafeB64Decode($headb64), true);
        if (array_key_exists('kid', $headb64)) {
            return $headb64['kid'];
        }
        throw new \RuntimeException();
    }

    private function getKey(array $jwks, string $kid,string $alg)
    {
        $keys = JWK::parseKeySet($jwks,$alg);
        if (array_key_exists($kid, $keys)) {
            return $keys[$kid];
        }
        throw new \RuntimeException();
    }

    private function getAlg(array $jwks, string $kid)
    {
        if (!array_key_exists('keys', $jwks)) {
            throw new \RuntimeException();
        }

        foreach ($jwks['keys'] as $key) {
            if ($key['kid'] === $kid && array_key_exists('alg', $key)) {
                return $key['alg'];
            }
        }
        throw new \RuntimeException();
    }

    private function fetchJWKs(): array
    {
        $response = HTTP::get(config('cognito.jwk'));
        return json_decode($response->body(), true) ?: [];
    }
}

Guardã®ä½œæˆ

Guardã¯SessionGuardã‚’å‚è€ƒã«æ©Ÿèƒ½æ‹¡å¼µã—ã¦ã„ãã¾ã™ã€‚

ã¾ãšã¯å‚考ã¨ã™ã‚‹SessionGuardã‹ã‚‰è§£èª¬ã§ã™ã€‚

    public function __construct($name,
                                UserProvider $provider,
                                Session $session,
                                Request $request = null)
    {
        $this->name = $name;
        $this->session = $session;
        $this->request = $request;
        $this->provider = $provider;
    }


    public function attempt(array $credentials = [], $remember = false)
    {
        $this->fireAttemptEvent($credentials, $remember);

        $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
        if ($this->hasValidCredentials($user, $credentials)) {
            $this->login($user, $remember);

            return true;
        }


    public function user()
    {
        if ($this->loggedOut) {
            return;
        }

        if (! is_null($this->user)) {
            return $this->user;
        }

        $id = $this->session->get($this->getName());

        if (! is_null($id) && $this->user = $this->provider->retrieveById($id)) {
            $this->fireAuthenticatedEvent($this->user);
        }

        if (is_null($this->user) && ! is_null($recaller = $this->recaller())) {
            $this->user = $this->userFromRecaller($recaller);

            if ($this->user) {
                $this->updateSession($this->user->getAuthIdentifier());

                $this->fireLoginEvent($this->user, true);
            }
        }

        return $this->user;
    }


Cognitoを利用ã™ã‚‹Guardã®ä½œæˆ

<?php

namespace App\Services;

use Illuminate\Auth\GuardHelpers;
use Illuminate\Auth\SessionGuard;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Support\Facades\Log;
use Illuminate\Contracts\Session\Session;

class CognitoGuard extends SessionGuard
{
    use GuardHelpers;
    public $name;
    private $jwt_verifier;
    protected $provider;
    protected $session;

    public function __construct($name,JWTVerifier $JWTVerifier,Session $session, UserProvider $userProvider)
    {
        $this->name = $name;
        $this->jwt_verifier = $JWTVerifier;
        $this->session = $session;
        $this->provider = $userProvider;

    }

    public function attempt(array $credentials = [], $remember = false)
    {
        $decoded_jwt = $this->jwt_verifier->decode($credentials['id_token']);
        $this->fireAttemptEvent(['email' => $decoded_jwt->email], $remember);
        $this->lastAttempted = $user = $this->provider->retrieveByCredentials(['email' => $decoded_jwt->email]);
        $this->login($user, $remember);

        return true;
    }
}

UserProviderã¯configã«è¨­å®šã•れãŸã‚‚ã®ãŒä¾å­˜æ€§æ³¨å…¥ã•れã¦åˆ©ç”¨ã•れã¦ã„ã¾ã™ã€‚

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],
    ],

eloquentã¨è¨˜è¿°ã•れã¦ã„ã¾ã™ã€‚ã“れãŒã¾ã•ã«Providerã®èª¬æ˜Žã§ç™»å ´ã—ãŸEloquentUserProviderã§ã™ã€‚

src/Illuminate/Auth/EloquentUserProvider.php

サービスプロãƒã‚¤ãƒ€ã®è¨­å®š

リクエスト処ç†ã«èªè¨¼åˆ¤å®šã‚’挟ã¿è¾¼ã‚€å¿…è¦ãŒã‚ã‚‹ãŸã‚ã€ã‚µãƒ¼ãƒ“スプロãƒã‚¤ãƒ€ã«è¨­å®šã—ã¾ã™ã€‚

èªè¨¼ã‚»ãƒƒã‚·ãƒ§ãƒ³ã‚’æŒã£ãŸçŠ¶æ…‹ã§ã‚¢ã‚¯ã‚»ã‚¹ã•れãŸå ´åˆã«è‡ªå‹•ログインを挟むフローを実ç¾ã§ãã¾ã™ã€‚

CognitoGuardã®å‘¼ã³å‡ºã—

サービスプロãƒã‚¤ãƒ€ã‹ã‚‰CognitoGuardã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã‚’作æˆã—ã¦èªè¨¼å‡¦ç†ã‚’行ã„ã¾ã™ã€‚

<?php

namespace App\Providers;

use App\Services\CognitoGuard;
use App\Services\JWTVerifier;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Auth;

class AuthServiceProvider extends ServiceProvider
{

    protected $policies = [
    ];


    public function boot()
    {
        $this->registerPolicies();

        Auth::extend('cognito', function($app, $name, array $config){
            return new CognitoGuard(
                config('auth.defaults.guards'),
                new JWTVerifier(),
                $app['session.store'],
                Auth::createUserProvider($config['provider'])
            );
        });
    }
}

controllerã®å®Ÿè£…

最後ã«controllerã®è¨­å®šã§ã™ã€‚

デフォルトã§ä½œæˆã•れã¦ã„ã‚‹LoginControllerã¨ã¯åˆ¥ã«Cognito用LoginControllerを作æˆã—ã¦ã„ã¾ã™ã€‚

<?php

namespace App\Http\Controllers;

use App\Cognito\CognitoClient;
use App\Providers\RouteServiceProvider;
use App\Traits\EncryptTrait;
use Aws\CognitoIdentityProvider\CognitoIdentityProviderClient;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;

class LoginCognitoController extends Controller
{
    use EncryptTrait;
    use AuthenticatesUsers;

    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }


    protected $redirectTo = RouteServiceProvider::HOME;


    public function index()
    {
        $callback_uri = config('cognito.callback_uri');

        $config = [
            'region'      => config('cognito.region'),
            'version'     => config('cognito.version')
        ];
        $client = new CognitoClient(
            new CognitoIdentityProviderClient($config)
        );

        $query_param = 'response_type=code';
        $query_param .= '&amp;client_id='.$client->getClientId();
        $query_param .= '&amp;redirect_uri='.$callback_uri;
        $query_param .= '&amp;state='.'authorization';

        return redirect(config('cognito.endpoint').'login?'.$query_param);
    }

    public function callback(Request $request){
        $state = $request->input('state');

        $code_challenge = $request->input('code_challenge');
        $code = $request->input('code');
        //アクセストークンã®å–å¾—
        if($state == 'authorization' && !empty($code)){
            $tokens = $this->fetchToken($code,$code_challenge);
            $credentials['id_token'] = $tokens->id_token;
            $credentials['access_token'] = $tokens->access_token;
            $credentials['refresh_token'] = $tokens->refresh_token;
            Auth::guard('cognito')->attempt($credentials);
        }

        if(empty($redirect_url)){
            return redirect('/home');
        }else{
            return redirect('/');
        }
    }

    private function fetchToken($code,$code_challenge){
        Log::info(self::class.' '.__FUNCTION__);

        $config = [
            'region'      => config('cognito.region'),
            'version'     => config('cognito.version')
        ];
        $client = new CognitoClient(
            new CognitoIdentityProviderClient($config)
        );
        return $client->fetchTokenEndpoint($code,$code_challenge);
    }
}

indexメソッド

ログイン画é¢ã®URLãŒã‚³ãƒ¼ãƒ«ã•れãŸéš›ã®å‡¦ç†ã§ã™ã€‚Cognitoã®ãƒ­ã‚°ã‚¤ãƒ³ç”»é¢ã¸ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹URLを生æˆã—リダイレクトã—ã¦ã„ã¾ã™ã€‚

クエリパラメータ
response_typecodeã¾ãŸã¯tokenã¨æŒ‡å®š
今回ã®ã‚±ãƒ¼ã‚¹ã¯èªå¯ã‚³ãƒ¼ãƒ‰ãƒ•ローãªã®ã§codeを指定
client_idConito管ç†ç”»é¢ã‹ã‚‰ç¢ºèªã§ãるクライアントID
redirect_uriコールãƒãƒƒã‚¯ã®URLを指定
stateCSRF対策ã®ãƒ‘ラメータ。今回ã®ã‚µãƒ³ãƒ—ルã§ã¯æ–‡å­—列を暗å·åŒ–ã—ã¦ãã®ã¾ã¾æ¸¡ã—ã¦ã„ã¾ã™ã€‚
Cognitoèªå¯ã‚¨ãƒ³ãƒ‰ãƒã‚¤ãƒ³ãƒˆ

callbackメソッド

レスãƒãƒ³ã‚¹ãƒ‘ラメータã«å«ã¾ã‚Œã‚‹ã€Œcodeã€ã‚’å–å¾—ã—ã¦ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã®å‡¦ç†ã‚’行ã„ã¾ã™ã€‚

トークンエンドãƒã‚¤ãƒ³ãƒˆã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã¯CognitoClientクラスã§å‡¦ç†ã—ã¦ã„ã¾ã™ã€‚

詳細ã¯AWSå…¬å¼ã®ãƒžãƒ‹ãƒ¥ã‚¢ãƒ«ã‚’ã”覧ãã ã•ã„

トークンエンドãƒã‚¤ãƒ³ãƒˆ

ã¾ã¨ã‚

Laravelを利用ã—ãŸèªè¨¼æ©Ÿèƒ½ã¯ã¨ã¦ã‚‚便利ã§ã™ãŒã€ã‚µã‚¤ãƒˆé–“SSOã‚„MFAã®å®Ÿç¾ã—よã†ã¨ã™ã‚‹ã¨çµæ§‹ãƒãƒ¼ãƒ‰ãƒ«ãŒé«˜ã„ã§ã™ã€‚

Amazon Cognitoãªã©ã®å¤–部ã®èªè¨¼ã‚µãƒ¼ãƒ“スを使ãˆã°ã“ã‚Œã‚‰ã®æ©Ÿèƒ½ãŒç°¡å˜ã«å°Žå…¥ã§ãã¾ã™ã®ã§ã€æœ¬æ¥ã®ã‚¢ãƒ—リケーション開発ã«å°‚念ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚

会員サイトãªã©ã‚’構築ã™ã‚‹å ´åˆã«ã¯ç¢ºå®Ÿã«å¿…è¦ã«ãªã‚‹èªè¨¼æ©Ÿèƒ½ã§ã™ã®ã§ã€å°Žå…¥æ¡ˆã®ä¸€ã¤ã¨ã—ã¦æ¤œè¨Žã—ãŸæ–¹ãŒã„ã„ã§ã™ã­ã€‚

コメントを残ã™