Laravelã§ Amazon Cognito ãƒã‚°ã‚¤ãƒ³èªè¨¼1ã€ãƒã‚°ã‚¤ãƒ³æ©Ÿèƒ½ã€‘
世ã®ä¸ã®ãƒã‚°ã‚¤ãƒ³æ©Ÿèƒ½ã®å¤§åŠã‚’å ã‚ã¦ã„ã‚‹OAuthã‚„OIDCã®ä»•組ã¿ã§ã™ãŒã€ã‚ã‹ã£ã¦ã„るよã†ã§ã‚¤ãƒžã‚¤ãƒç†è§£ã—ã¦ã„ãªã„ãªã¨æ€ã£ã¦ãŠã‚Šã¾ã—ãŸã€‚
最近æ¥å‹™ã§ãƒã‚°ã‚¤ãƒ³æ©Ÿèƒ½ã«ã¤ã„ã¦è§¦ã‚Œã‚‹æ©Ÿä¼šãŒã‚りã€ã“れを機ã«å®Ÿéš›ã«å®Ÿè£…ã—ãªãŒã‚‰ç†è§£ã‚’æ·±ã‚よã†ã¨æ€ã„ç«‹ã¡ã¾ã—ã¦æƒ…å ±ã‚’æ•´ç†ã—ã¦ã„ãã¾ã™ã€‚
今回ã¯AWSã®èªè¨¼ã‚µãƒ¼ãƒ“スã§ã‚ã‚‹Amazon Cognitoを利用ã—ã¦å®Ÿè£…を進ã‚ã¦ã„ãã¾ã™ã€‚
■やりãŸã„ã“ã¨
Cognitoを利用ã—ãŸãƒã‚°ã‚¤ãƒ³æ©Ÿèƒ½ã‚’実ç¾ã™ã‚‹
ãƒã‚°ã‚¤ãƒ³æ©Ÿèƒ½ã‚’カスタマイズã™ã‚‹
Cognitoã§ã®ä¼šå“¡ç™»éŒ²æƒ…å ±ã‚’é€£æºã™ã‚‹
â– å‚考URL
https://qiita.com/tomoeine/items/40a966bf3801633cf90f
Laravelã§ãƒã‚°ã‚¤ãƒ³èªè¨¼ã‚·ãƒªãƒ¼ã‚º
- Laravelã§ Amazon Cognito ãƒã‚°ã‚¤ãƒ³èªè¨¼1ã€ãƒã‚°ã‚¤ãƒ³æ©Ÿèƒ½ã€‘ â†ã€€ã‚¤ãƒžã‚³ã‚³
- Laravelã§ Amazon Cognito ãƒã‚°ã‚¤ãƒ³èªè¨¼2ã€ä¼šå“¡ç™»éŒ²æ©Ÿèƒ½ã€‘
- 1. 完æˆç³»ã®ç”»é¢ãƒ•ãƒãƒ¼
- 2. Amazon Cognitoã®è¨å®š
- 2.1. ユーザプールã®è¨å®š
- 2.2. 会員準備
- 3. Laravelã§ã®ãƒã‚°ã‚¤ãƒ³æ©Ÿèƒ½å®Ÿè£…
- 3.1. Laravelã§ã®èªè¨¼æ©Ÿèƒ½ãŠã•らã„
- 3.1.1. Guard
- 3.1.2. Provider
- 4. ï¼£onfigファイルã®è¨å®š
- 5. cognito連æº
- 5.1.1. ãƒã‚¤ãƒ³ãƒˆã€€Http::asForm()
- 6. jwt検証用クラス作æˆ
- 7. Guardã®ä½œæˆ
- 7.1. Cognitoを利用ã™ã‚‹Guardã®ä½œæˆ
- 8. サービスプãƒãƒã‚¤ãƒ€ã®è¨å®š
- 8.1. CognitoGuardã®å‘¼ã³å‡ºã—
- 9. controllerã®å®Ÿè£…
- 9.1. indexメソッド
- 9.2. callbackメソッド
- 10. ã¾ã¨ã‚
完æˆç³»ã®ç”»é¢ãƒ•ãƒãƒ¼

ã„ãã¤ã‹ã‚„り方ã¯ã‚りã¾ã™ãŒã€ä»Šå›žã¯Amazon Cognitoã®ãƒã‚°ã‚¤ãƒ³ç”»é¢ã‚’利用ã—ã¦å®Ÿç¾ã™ã‚‹æ–¹æ³•ã§ã™ã€‚
Laravelã‹ã‚‰Amazon Cognitoã§ç”¨æ„ã•れã¦ã„ã‚‹èªå¯ã‚¨ãƒ³ãƒ‰ãƒã‚¤ãƒ³ãƒˆã¸ã‚¢ã‚¯ã‚»ã‚¹ã—ã€èªå¯ã‚³ãƒ¼ãƒ‰ã‚’å–å¾—ã—ã¾ã™ã€‚
※ホストã•れãŸWEB UI(AWSå´ã§æº–å‚™ã•れãŸãƒã‚°ã‚¤ãƒ³ç”»é¢ï¼‰ã‚’利用ã—ã¦ã„ã¾ã™ã€‚ã“ã®ç”»é¢ã¯æ—¥æœ¬èªžåŒ–ã§ãã¾ã›ã‚“。
大事ãªã“ã¨ãªã®ã§ã‚‚ã†ä¸€åº¦è¨€ã„ã¾ã™ã€‚日本語化ã§ãã¾ã›ã‚“。
Amazon Cognitoã®è¨å®š
Cognitoã«ã¯ã€Œãƒ¦ãƒ¼ã‚¶ãƒ—ールã€ã¨ã€ŒIDプールã€ã¨ã„ã†ï¼’ã¤ã®ç®¡ç†ãŒã‚りã¾ã™ã€‚ãれãžã‚Œã®é•ã„ã¯ä¸‹è¨˜ã®é€šã‚Šã§ã™ã€‚今回ã¯ä¸€èˆ¬ãƒ¦ãƒ¼ã‚¶å‘ã‘ãªã®ã§ã€Œãƒ¦ãƒ¼ã‚¶ãƒ—ールã€ã‚’利用ã—ã¾ã™ã€‚
- ユーザプール:会員サイトãªã©ã§ãƒ¦ãƒ¼ã‚¶ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’管ç†ã™ã‚‹å ´åˆã«åˆ©ç”¨
- IDプール:組織ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆç®¡ç†ãªã©ã«åˆ©ç”¨
ユーザプールã®è¨å®š
今回ã¯ã‚¹ãƒ†ãƒƒãƒ—ã«å¾“ã£ã¦ç´°ã‹ãè¨å®šã—ã¦ã„ãã¾ã™ã€‚
デフォルトを確èªã—ãªãŒã‚‰é€²ã‚ã¦é ‚ã„ã¦ã‚‚å•題ã”ã–ã„ã¾ã›ã‚“。

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

次ã«ãƒ‘スワード強度ã¨è‡ªå·±ã‚µã‚¤ãƒ³ã‚¢ãƒƒãƒ—ã®è¨å®šã§ã™ã€‚
パスワード強度ã¯ãƒ‡ãƒ•ォルトã®ã¾ã¾ã§å•題ã‚りã¾ã›ã‚“。セã‚ュリティãƒãƒªã‚·ãƒ¼ãªã©ãŒã‚ã‚‹å ´åˆã¯ã€ãƒãƒªã‚·ãƒ¼ã«å¾“ã£ã¦è¨å®šã‚’ã—ã¾ã™ã€‚
自己サインアップã¯ãƒ‡ãƒ•ォルトã§è¨±å¯ã•れã¦ã„ã‚‹ã®ã§ã“ã®ã¾ã¾ç™»éŒ²ã—ã¾ã™ã€‚会員制サイトãªã©ã§ç®¡ç†è€…ã«ã®ã¿ãƒ¦ãƒ¼ã‚¶ä½œæˆã®æ¨©é™ã‚’与ãˆã‚‹å ´åˆã«ã¯è¨å®šã‚’変更ã—ã¾ã™ã€‚

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

ã“ã¡ã‚‰ã¯ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹æ¤œè¨¼ã®ãƒ¡ãƒ¼ãƒ«é€ä¿¡æ™‚ã®è¨å®šã§ã™ã€‚fromメールアドレスを変更ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚
Amazon SESを経由ã—ã¦ãƒ¡ãƒ¼ãƒ«ã‚’é€ä¿¡ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ãŒã€ä»Šå›žã¯åˆ©ç”¨ã—ã¦ãŠã‚Šã¾ã›ã‚“。
ã‚ャプãƒãƒ£ã—ãれã¦ãŠã‚Šã¾ã›ã‚“ãŒã€é€ä¿¡ã™ã‚‹ãƒ¡ãƒ¼ãƒ«æœ¬æ–‡ã®ã‚«ã‚¹ã‚¿ãƒžã‚¤ã‚ºãªã©ã‚‚ã“ã¡ã‚‰ã®è¨å®šã§å¯èƒ½ã§ã™ã€‚

アプリクライアントã®è¨å®šã§ã™ã€‚アプリクライアントåã¯ãƒã‚°ã‚¤ãƒ³æ©Ÿèƒ½ã‚’実装ã™ã‚‹éš›ã«ã‚³ãƒ¼ãƒ‰ã¨ã—ã¦åˆ©ç”¨ã—ã¾ã™ã€‚
ãã®ä»–ã®è¨å®šã¯ãƒ‡ãƒ•ォルトã®ã¾ã¾ã§ã‚‚å•題ã”ã–ã„ã¾ã›ã‚“。

今回ã¯èªè¨¼ãƒ•ãƒãƒ¼ã¨ã—ã¦æ›´æ–°ãƒˆãƒ¼ã‚¯ãƒ³ãƒ™ãƒ¼ã‚¹ã®èªè¨¼ã‚’利用ã—ã¾ã™ã€‚デフォルトã§ãƒã‚§ãƒƒã‚¯æ¸ˆã¿ã§ã™ã€‚
ä»–ã«ã‚‚デフォルトã§ãƒã‚§ãƒƒã‚¯ã•れã¦ã„ã‚‹ã‚‚ã®ãŒã‚りã¾ã™ãŒã€ä»Šå›žã®ãƒ•ãƒãƒ¼ã§ã¯åˆ©ç”¨ã—ãªã„ã®ã§å¤–ã—ã¦ã‚‚å•題ã”ã–ã„ã¾ã›ã‚“。

ã“れã§Cognitoã®è¨å®šã¯å®Œäº†ã§ã™ã€‚
会員準備
è¨å®šãŒå®Œäº†ã—ãŸã‚‰ã€å…ˆã«æ¤œè¨¼ç”¨ã®ãƒ¦ãƒ¼ã‚¶ã‚’作æˆã—ã¦ãŠãã¾ã™ã€‚ã“ã®ãƒ¦ãƒ¼ã‚¶ã§Laravelã¨Cognitoã®èªè¨¼é€£æºã®æ¤œè¨¼ã‚’行ã„ã¾ã™ã€‚
会員登録ã¯ã€Œãƒ¦ãƒ¼ã‚¶ã¨ã‚°ãƒ«ãƒ¼ãƒ—ã€ã®è¨å®šã‹ã‚‰è¡Œã„ã¾ã™ã€‚

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

メールアドレスã®é‡è¤‡ãƒã‚§ãƒƒã‚¯ã¯ãƒ‡ãƒ•ォルトã§å®Ÿæ–½ã•れã¦ã„ã¾ã™
ユーザã®ä½œæˆã‚’押下ã™ã‚‹ã¨ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã«æ¤œè¨¼ã‚³ãƒ¼ãƒ‰ãŒé€ä»˜ã•れã¾ã™ã®ã§ã€ã‚³ãƒ¼ãƒ‰ã‚’入力ã—ã¦èªè¨¼ã‚’行ã„ã¾ã™ã€‚
「Eã‚ーるã‚ã©ã‚Œã™ã‚’検証済ã¿ã«ã—ã¾ã™ã‹ï¼Ÿã€ã®ãƒã‚§ãƒƒã‚¯ã‚’入れã¦ãŠãã¨ã“ã®ä½œæ¥ã¯ä¸è¦ã«ãªã‚Šã¾ã™ã€‚


ã“ã‚Œã§æ¤œè¨¼ç”¨ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®æº–備も完了ã—ã¾ã—ãŸã€‚
ã“ã®å¾Œã®ãƒã‚°ã‚¤ãƒ³æ©Ÿèƒ½ã®æ¤œè¨¼ã§åˆ©ç”¨ã—ã¾ã™ã®ã§ã€ãƒ‘スワードを忘れãªã„よã†ã«æŽ§ãˆã¦ãŠãã¾ã—ょã†ã€‚
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 .= '&client_id='.$client->getClientId();
$query_param .= '&redirect_uri='.$callback_uri;
$query_param .= '&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_type | codeã¾ãŸã¯tokenã¨æŒ‡å®š 今回ã®ã‚±ãƒ¼ã‚¹ã¯èªå¯ã‚³ãƒ¼ãƒ‰ãƒ•ãƒãƒ¼ãªã®ã§codeを指定 |
| client_id | Conito管ç†ç”»é¢ã‹ã‚‰ç¢ºèªã§ãるクライアントID |
| redirect_uri | コールãƒãƒƒã‚¯ã®URLを指定 |
| state | CSRF対ç–ã®ãƒ‘ラメータ。今回ã®ã‚µãƒ³ãƒ—ルã§ã¯æ–‡å—列を暗å·åŒ–ã—ã¦ãã®ã¾ã¾æ¸¡ã—ã¦ã„ã¾ã™ã€‚ |
callbackメソッド
レスãƒãƒ³ã‚¹ãƒ‘ラメータã«å«ã¾ã‚Œã‚‹ã€Œcodeã€ã‚’å–å¾—ã—ã¦ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã®å‡¦ç†ã‚’行ã„ã¾ã™ã€‚
トークンエンドãƒã‚¤ãƒ³ãƒˆã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã¯CognitoClientクラスã§å‡¦ç†ã—ã¦ã„ã¾ã™ã€‚
詳細ã¯AWSå…¬å¼ã®ãƒžãƒ‹ãƒ¥ã‚¢ãƒ«ã‚’ã”覧ãã ã•ã„
トークンエンドãƒã‚¤ãƒ³ãƒˆ
ã¾ã¨ã‚
Laravelを利用ã—ãŸèªè¨¼æ©Ÿèƒ½ã¯ã¨ã¦ã‚‚便利ã§ã™ãŒã€ã‚µã‚¤ãƒˆé–“SSOã‚„MFAã®å®Ÿç¾ã—よã†ã¨ã™ã‚‹ã¨çµæ§‹ãƒãƒ¼ãƒ‰ãƒ«ãŒé«˜ã„ã§ã™ã€‚
Amazon Cognitoãªã©ã®å¤–部ã®èªè¨¼ã‚µãƒ¼ãƒ“スを使ãˆã°ã“ã‚Œã‚‰ã®æ©Ÿèƒ½ãŒç°¡å˜ã«å°Žå…¥ã§ãã¾ã™ã®ã§ã€æœ¬æ¥ã®ã‚¢ãƒ—リケーション開発ã«å°‚念ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚
会員サイトãªã©ã‚’構築ã™ã‚‹å ´åˆã«ã¯ç¢ºå®Ÿã«å¿…è¦ã«ãªã‚‹èªè¨¼æ©Ÿèƒ½ã§ã™ã®ã§ã€å°Žå…¥æ¡ˆã®ä¸€ã¤ã¨ã—ã¦æ¤œè¨Žã—ãŸæ–¹ãŒã„ã„ã§ã™ã。


