From 200efe0e90489da0f0e5941d54776fd1cac89cef Mon Sep 17 00:00:00 2001 From: Florian Sylvain Date: Thu, 12 Jan 2023 00:04:26 +0100 Subject: [PATCH] First part of httpOnly JWT cookie implementation --- cmd/main.go | 21 +++++++++++++++------ internal/api/auth.go | 23 ++++++++++++++++++----- web/admin-gui/src/utils/database.ts | 6 +++--- web/admin-gui/src/views/Login.vue | 13 +++++++------ 4 files changed, 43 insertions(+), 20 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 1a5a2e2..ee26f6c 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -10,10 +10,12 @@ import ( "github.com/joho/godotenv" ) -var ginMode string -var apiPort string -var frontPort string -var hostAddress string +var ( + ginMode string + apiPort string + frontPort string + hostAddress string +) func initEnvVariables() { if godotenv.Load() != nil { @@ -50,6 +52,12 @@ func corsMiddleware(c *gin.Context) { c.Next() } +func jwtProxyMiddleware(c *gin.Context) { + jwtToken, _ := c.Cookie("jwt") + c.Request.Header.Set("Authorization", fmt.Sprintf("Bearer %v", jwtToken)) + c.Next() +} + func initArticlesRoutes(r *gin.Engine) { articlesRouter := r.Group("/articles") articlesRouter.Use(corsMiddleware, api.AuthMiddleware.MiddlewareFunc()) @@ -63,10 +71,11 @@ func initArticlesRoutes(r *gin.Engine) { func initGin() { r := gin.Default() - r.Use(corsMiddleware) + r.Use(jwtProxyMiddleware, corsMiddleware) if ginMode == "release" { gin.SetMode(ginMode) + api.AuthMiddleware.SecureCookie = true } initBasicRoutes(r) @@ -77,6 +86,6 @@ func initGin() { func main() { initEnvVariables() - initJWT() initGin() + initJWT() } diff --git a/internal/api/auth.go b/internal/api/auth.go index 5e44944..686319f 100644 --- a/internal/api/auth.go +++ b/internal/api/auth.go @@ -2,6 +2,7 @@ package api import ( "errors" + "net/http" "os" "time" @@ -19,13 +20,25 @@ type User struct { var UsersLocation = database.Location{Database: "gohcms", Collection: "users"} var AuthMiddleware, _ = jwt.New(&jwt.GinJWTMiddleware{ - Realm: "GohCMS", - Key: []byte(os.Getenv("APP_JWT_SECRET")), - Timeout: time.Hour, - MaxRefresh: time.Hour, - Authenticator: JWTAuthenticator, + Realm: "GohCMS", + Key: []byte(os.Getenv("APP_JWT_SECRET")), + SendCookie: true, + CookieHTTPOnly: true, + CookieSameSite: http.SameSiteStrictMode, + Timeout: time.Hour, + MaxRefresh: time.Hour, + LoginResponse: JWTLoginResponse, + Authenticator: JWTAuthenticator, }) +func JWTLoginResponse(c *gin.Context, code int, message string, expire time.Time) { + if code == http.StatusOK { + c.JSON(code, gin.H{"code": code, "message": "Successfully logged in!", "expire": expire.Format(time.RFC3339)}) + } else { + c.JSON(code, gin.H{"code": code, "message": "Something wrong has happened."}) + } +} + func JWTAuthenticator(c *gin.Context) (interface{}, error) { var user = User{} err := c.BindJSON(&user) diff --git a/web/admin-gui/src/utils/database.ts b/web/admin-gui/src/utils/database.ts index f3467bf..1d91e2f 100644 --- a/web/admin-gui/src/utils/database.ts +++ b/web/admin-gui/src/utils/database.ts @@ -15,8 +15,8 @@ const baseURL = `http://${__APP_ENV__.APP_HOST_ADDRESS}:${__APP_ENV__.APP_API_PO export async function getArticles(id: string) : Promise> { return await fetch(`${baseURL}/articles/${id}`, { + credentials: 'include', method: 'GET', - headers: { "Authorization": `Bearer ${useAuthStore().token}` } }) .then(result => result.json()) .catch(error => { @@ -26,9 +26,9 @@ export async function getArticles(id: string) : Promise> { export async function postArticle(article: Article) : Promise { return await fetch(`${baseURL}/articles/${article.titleID}`, { + credentials: 'include', method: 'POST', - headers: { "Authorization": `Bearer ${useAuthStore().token}` }, - body: JSON.stringify(article) + body: JSON.stringify(article) }) .then(result => result.json()) .catch(error => { diff --git a/web/admin-gui/src/views/Login.vue b/web/admin-gui/src/views/Login.vue index 64c925c..5dc2cf6 100644 --- a/web/admin-gui/src/views/Login.vue +++ b/web/admin-gui/src/views/Login.vue @@ -19,11 +19,11 @@ function updateJWTcookies(JWTdata: jwtFormat): void { const cookieExpire = new Date(JWTdata.expire) cookieExpire.setDate(cookieExpire.getDate() + 1) - setCookie({ - key: 'JWTtoken', - value: JWTdata.token, - expire: cookieExpire.toString() - }) + // setCookie({ + // key: 'JWTtoken', + // value: JWTdata.token, + // expire: cookieExpire.toString() + // }) setCookie({ key: 'JWTexpire', value: JWTdata.expire, @@ -43,7 +43,7 @@ function jwtHandler(apiResponse: jwtFormat): void { } updateJWTcookies(apiResponse) disableErrors() - authStore.token = apiResponse.token + authStore.token = 'pouet' authStore.expire = apiResponse.expire isTokenOK.value = true } @@ -51,6 +51,7 @@ function jwtHandler(apiResponse: jwtFormat): void { function login(email: string, password: string): void { fetch(`http://${__APP_ENV__.APP_HOST_ADDRESS}:${__APP_ENV__.APP_API_PORT}/login/`, { method: "POST", + credentials: 'include', body: JSON.stringify({ email: email, password: password