質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Cookie

HTTPにおけるCookieとは、クライアントのウェブブラウザ上に保存された一時的なデータを指します。クライアント側のJavaScriptでも、サーバー側のHTTPヘッダーでもクッキーの読み書き・修正・削除が可能です。

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

ログイン

ログインは、ユーザーがコンピューターシステムにアクセスするプロセスの事を呼びます。

CORS

CORSとはCross-Origin Resource Sharingの頭文字をとったもので、ブラウザがオリジン以外のサーバからデータを取得するシステムのことです。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

1回答

5021閲覧

Go/GAE上でCORSを用いてReactのSPAを動かす際にcookieが保存されない

HayateIshida

総合スコア10

Cookie

HTTPにおけるCookieとは、クライアントのウェブブラウザ上に保存された一時的なデータを指します。クライアント側のJavaScriptでも、サーバー側のHTTPヘッダーでもクッキーの読み書き・修正・削除が可能です。

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

ログイン

ログインは、ユーザーがコンピューターシステムにアクセスするプロセスの事を呼びます。

CORS

CORSとはCross-Origin Resource Sharingの頭文字をとったもので、ブラウザがオリジン以外のサーバからデータを取得するシステムのことです。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2020/03/11 05:26

初投稿です。質問失礼します。

###概要
・GolangとReactでcookieを使った認証機能(ログイン・新規登録)の実装(cookieをGoで保存)
・chorme検証ツールのNetWorkの login/ にはcookieの値が出力されている。
・chorme設定から「全てのcookieを表示」の欄にはcookieが保存されていない(エラーは出ていない)。つまりcookieがきちんと書き込まれておらず、読み込みができない。
・現在の実行環境はReactがローカル、GoはGAE上本番環境。(Reactの本番環境でも同様の現象)

歴史

歴史番号とコードを関連付けました。

Golang

・echoのcontextでログイン機能実装とcookieの設定(ログイン自体は200 OK、cookieの書き込みのみできていない)...1
・echoでルーティングを設定し、ルーティング内にCORS設定を追加**...2**

React

・Axiosを利用して外部APIとの通信、ログインボタンの作成(POSTメソッド)...3
・先日タイトルと全く同様の問題が発生したため、ReactのCORS設定を見直したら直ったが、コードを触っているうちにまたcookieが保存されなくなる。...4
・現在AWS のS3とcloudfrontを使ってデプロイしているが今回ローカルでも問題が発生しているので関係はないと思われるためcookieの設定はしていない。

ソースコード

1

1//user_controller.go 2//echoのcontext 3func Login(c Context) error { 4 //formのuser 5 var formUser domain.User 6 if err := c.Bind(&formUser); err != nil { 7 return err 8 } 9 //取ってくるuser 10 var u domain.User 11 u, err := database.LoginUser(formUser) 12 if err != nil { 13 return err 14 } 15 WriteCookie(c, u.UUID) 16 return c.JSON(http.StatusOK, u) 17} 18 19 20 21//user_repository.go 22func LoginUser(formUser domain.User) (domain.User, error) { 23 var u domain.User 24 var err error 25 //email一致のuserを返す 26 res := db.First(&u, "email = ?", formUser.Email) 27 err = res.Error 28 if res.RecordNotFound() { 29 fmt.Println("-----------------") 30 fmt.Println("ユーザーが存在しません。") 31 return u, err 32 } 33 if err != nil { 34 fmt.Println("-----------------") 35 fmt.Println("予期せぬエラーです。") 36 return u, err 37 } 38 //passwordが一致するか(第一引数ハッシュ後password, 第二引数formのハッシュ化前password) 39 err = bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(formUser.Password)) 40 if err != nil { 41 fmt.Println("-----------------") 42 fmt.Println("パスワードが違います。") 43 return u, err 44 } 45 if err := db.First(&u).Related(&u.Follows, "Follows").Related(&u.Followers, "Followers").Related(&u.Reviews, "Reviews").Error; err != nil { 46 return u, err 47 } 48 return u, nil 49} 50 51//cookie.go 52func WriteCookie(c Context, uuid string) { 53 cookie := new(http.Cookie) 54 cookie.Name = data 55 cookie.Value = uuid 56 cookie.Path = "/" 57 cookie.Expires = time.Now().Add(24 * time.Hour) 58 cookie.Secure = true 59 cookie.HttpOnly = true 60 cookie.SameSite = http.SameSiteLaxMode 61 c.SetCookie(cookie) 62}

2

1//infra/router.go 2func CreateMux() *echo.Echo { 3 e := echo.New() 4 e.Validator = NewValidator() 5 // CORS設定 6 e.Use(middleware.CORSWithConfig(middleware.CORSConfig{ 7 AllowOrigins: []string{"http://app.ahiboh.com.s3-website-ap-northeast-1.amazonaws.com", "http://d32v7mjl1zna31.cloudfront.net", "http://localhost:3000"}, 8 AllowHeaders: []string{echo.HeaderAccept, echo.HeaderContentType, echo.HeaderOrigin, echo.HeaderCookie, echo.HeaderSetCookie, echo.HeaderAccessControlAllowOrigin, "*"}, 9 AllowMethods: []string{http.MethodGet, http.MethodPost, http.MethodDelete, http.MethodPatch, http.MethodPut}, 10 AllowCredentials: true, 11 })) 12 //ローカルならlog表示 13 if !appengine.IsAppEngine() { 14 e.Use(middleware.Recover()) 15 e.Use(middleware.Logger()) 16 e.Use(middleware.Gzip()) 17 e.Static("/", "public") 18 } 19 return e 20} 21 22func Router(e *echo.Echo) { 23 e.POST("/signup/", func(c echo.Context) error { return controllers.Signup(c) }) 24 e.POST("/login/", func(c echo.Context) error { return controllers.Login(c) }) 25 e.DELETE("/logout", func(c echo.Context) error { return controllers.Logout(c) }) 26 e.GET("/cuser", func(c echo.Context) error { return controllers.CurrentUserGet(c) }) 27 e.GET("/user", func(c echo.Context) error { return controllers.UserGet(c) }) 28 e.GET("/users", func(c echo.Context) error { return controllers.UsersGet(c) }) 29 e.POST("/review/", func(c echo.Context) error { return controllers.ReviewPost(c) }) 30 e.GET("/reviews", func(c echo.Context) error { return controllers.ReviewsGet(c) }) 31 e.POST("/follow/", func(c echo.Context) error { return controllers.FollowPost(c) }) 32 e.DELETE("/follow", func(c echo.Context) error { return controllers.FollowDelete(c) }) 33 e.GET("/follows", func(c echo.Context) error { return controllers.FollowsGet(c) }) 34 e.GET("/test", func(c echo.Context) error { return test(c) }) 35} 36 37//server.go 38func init() { 39 var e = infra.CreateMux() 40 infra.Router(e) 41 //全ての通信をechoで行う設定 42 http.Handle("/", e) 43} 44

以下React, Login/index.tsx

3

1export const useLogin = () => { 2 const ini: formStruct = { 3 name: "", 4 email: "", 5 password: "" 6 } 7 const [state, setState] = useState(ini) 8 const { getCuser } = useCuser() 9 const [redirect, setRedirect] = useState(<></>) 10//ログインボタンを押すとloginUser発火 11 const loginUser = (email: string, password: string) => { 12 getCuser( 13 //ここで通信している 14 Axios.post('https://gratio-ahiboh-0222.appspot.com/login/', { 15 email: email, 16 password: password, 17 }) 18 ) 19 setState(ini) 20 setRedirect(<Redirect to="/" />) 21 } 22 return {state, setState, loginUser, redirect} 23} 24 25 const getCuser = useCallback(async (axios: any) => { 26 setLoading(true) 27 try { 28 const result = await axios 29 const res: UserType = result.data 30 setLoading(false) 31 dispatch({type: USERSET, result: res}) 32 } catch (e) { 33 setLoading(false) 34 setError(e.message) 35 } 36 }, [loading, error, sess] )

index.tsx (Reactのルートディレクトリ)

4

1Axios.defaults.withCredentials = true

仮説

・1週間ほど前まで同様のGo/GAE本番環境 + Reactの開発環境で動いていた(コードは少し変えました)ので、原因がいまいち掴めておりません。
・個人的にはGolangよりReactの方が怪しいと思っております(API通信自体はうまくいっている、最近はGoよりもReactのコードを書き換えた)
・しかし実際にできていないのは1のSetCookieの部分なのでなんとも言えません。変更点は強いて言うなら2のCORSのAllowOrigin部分です。
・React側はAxiosのグローバル設定のみで動いているのですが、React側でcookieだけブロックされている(?)のかな、とも考えています。もしくはAxiosの書き方の問題?

・本当にエラーも出ずcookieが書き込まれないだけとなっております。

###検証
・デバッグではfmtを使ってWriteCookieメソッドまでたどり着いていることは確認されました。
・ダメもとでAllowOriginsを* (全て許可)に設定しましたが治らず。
・cookieのSecure, HTTPOnly, SameSite属性はあってもなくても治りませんでした。
・Axiosの記述をtry catchではなくpromiseパターンとしてthen-catchで記述しましたが治りませんでした。

補足情報

・Mac OS Catalina
・VSCode
・Golang (https://gratio-ahiboh-0222.appspot.com/)
Echo
・React (http://localhost:3000/)
Axios
・CORSはこちら等を参考にしました。
https://qiita.com/att55/items/2154a8aad8bf1409db2b

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoshi-takanori

2020/03/12 00:01

個人的には SameSite が怪しい(Chrome が最近更新されたのでは?)と思いますが、まず 1 週間前に動いていたコードに戻して検証することが先決かと。戻せない(≒ バージョン管理できてない)のであれば、そのような開発体制を見直すべきでしょうね。
HayateIshida

2020/03/13 05:01

バージョン管理についても今後徹底していくようにします。 ご指摘ありがとうございます。 SameSite=Laxの解釈を間違えており、サードパーティcookieはブロックされるようでした。
guest

回答1

0

自己解決

解決しました。

1の下の方

Golang

1// cookie.SameSite = http.SameSiteLaxModeはダメ 2cookie.SameSite = http.SameSiteNoneMode

結論、SameSiteのLaxとNoneの違いをよくわかっていなかったためおきました。
参考↓
https://digiday.jp/platforms/what-is-chrome-samesite/

どうやらLaxはサードパーティCookieを基本的にブロックします。そのため今回外部APIで通信していたのでCookieがクライアント側で許可されておらず、Chromeに保存されていませんでした。

これまでうまくいっていたのは、ChromeのSameSiteのdefaultがNoneになっていたためで、Chrome 80 からSameSiteのdefaultはLaxになったようです。

さらに補足としてhttp.SameSiteNoneModehはgo 1.13以降のバージョンでのみ使用できますので、app.yamlにruntime111を書き込んでいるなど、1.13以前のバージョンを使用しているとundifinedになります(ハマりました)

投稿2020/03/13 05:10

HayateIshida

総合スコア10

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問