From 124277282f54218870f4d0f01c0de988bb34140c Mon Sep 17 00:00:00 2001 From: Vinayak Goyal <73058928+ivinayakg@users.noreply.github.com> Date: Sat, 2 Mar 2024 05:16:58 +0530 Subject: [PATCH] feat: migrated to authentication with cookies --- api/constants/main.go | 6 ++ api/controllers/url.go | 4 +- api/controllers/user.go | 13 ++-- api/helpers/helpers.go | 2 + api/helpers/urls.go | 3 +- api/main.go | 4 +- api/middleware/auth.go | 25 +++++-- api/sample.env | 3 +- api/tests/integration/main_test.go | 1 + api/tests/integration/system_test.go | 18 +++-- api/tests/integration/tracking_test.go | 6 +- api/tests/integration/url_test.go | 88 ++++++++++++++++-------- api/tests/integration/user_test.go | 23 ++++--- api/tests/test.env | 3 +- api/utils/jwt.go | 42 +++++++++++ client/src/App.tsx | 10 +-- client/src/components/DeleteURLModal.tsx | 2 +- client/src/components/UpdateURLModal.tsx | 2 +- client/src/components/main-provider.tsx | 29 +++----- client/src/pages/CreateShort.tsx | 2 +- client/src/pages/MyUrls.tsx | 2 +- client/src/utils/auth.tsx | 14 ---- 22 files changed, 197 insertions(+), 105 deletions(-) create mode 100644 api/constants/main.go diff --git a/api/constants/main.go b/api/constants/main.go new file mode 100644 index 0000000..d7abcb0 --- /dev/null +++ b/api/constants/main.go @@ -0,0 +1,6 @@ +package constants + +type env_type string + +const Dev env_type = "development" +const Prod env_type = "prod" diff --git a/api/controllers/url.go b/api/controllers/url.go index cf636f6..de89537 100644 --- a/api/controllers/url.go +++ b/api/controllers/url.go @@ -155,7 +155,7 @@ func ResolveURL(w http.ResponseWriter, r *http.Request) { return } - go func(r http.Request, url models.URL) { + go func(r *http.Request, url models.URL) { userAgent := r.Header.Get("User-Agent") ua := uasurfer.Parse(userAgent) @@ -172,7 +172,7 @@ func ResolveURL(w http.ResponseWriter, r *http.Request) { timestamp := time.Now().Unix() helpers.Tracker.CaptureRedirectEvent(device, ip, os, referrer, urlId, timestamp) - }(*r, *url) + }(r, *url) w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") http.Redirect(w, r, url.Destination, http.StatusMovedPermanently) diff --git a/api/controllers/user.go b/api/controllers/user.go index 689c044..2c4ceee 100644 --- a/api/controllers/user.go +++ b/api/controllers/user.go @@ -97,21 +97,22 @@ func CallbackSignInWithGoogle(w http.ResponseWriter, r *http.Request) { return } + var token *string + if user != nil { - token, _ := utils.CreateJWT(user) - http.Redirect(w, r, fmt.Sprintf(os.Getenv("FRONTEND_AUTH_URL")+"%v", *token), http.StatusSeeOther) - return + token, _ = utils.CreateJWT(user) } else { user, err = models.CreateUser(googleProfile["email"].(string), googleProfile["name"].(string), googleProfile["picture"].(string)) if err != nil { helpers.SendJSONError(w, http.StatusInternalServerError, "Internal Server Error") return } - token, _ := utils.CreateJWT(user) - http.Redirect(w, r, fmt.Sprintf(os.Getenv("FRONTEND_AUTH_URL")+"%v", *token), http.StatusSeeOther) - return + token, _ = utils.CreateJWT(user) } + cookie := utils.CreateAuthCookie(*token) + http.SetCookie(w, cookie) + http.Redirect(w, r, os.Getenv("FRONTEND_URL"), http.StatusSeeOther) } func SelfUser(w http.ResponseWriter, r *http.Request) { diff --git a/api/helpers/helpers.go b/api/helpers/helpers.go index d0fcc6f..6afa753 100644 --- a/api/helpers/helpers.go +++ b/api/helpers/helpers.go @@ -9,6 +9,8 @@ import ( "time" ) +var ENV string + type ErrorResponse struct { Error string `json:"error"` } diff --git a/api/helpers/urls.go b/api/helpers/urls.go index 1535660..c454621 100644 --- a/api/helpers/urls.go +++ b/api/helpers/urls.go @@ -30,8 +30,7 @@ func RemoverDomainError(url string) bool { } func BuildUrl(url string) string { - env := os.Getenv("ENV") - if env == "development" { + if ENV == "development" { return "http://" + os.Getenv("SHORTED_URL_DOMAIN") + url } return "https://" + os.Getenv("SHORTED_URL_DOMAIN") + url diff --git a/api/main.go b/api/main.go index 3173977..48624e4 100644 --- a/api/main.go +++ b/api/main.go @@ -9,6 +9,7 @@ import ( "time" "github.com/gorilla/mux" + "github.com/ivinayakg/shorte.live/api/constants" "github.com/ivinayakg/shorte.live/api/controllers" "github.com/ivinayakg/shorte.live/api/helpers" "github.com/ivinayakg/shorte.live/api/middleware" @@ -56,9 +57,10 @@ func main() { } PORT := os.Getenv("PORT") + helpers.ENV = os.Getenv("ENV") go func() { - if os.Getenv("ENV") != "development" { + if os.Getenv("ENV") == string(constants.Prod) { return } router := createRouter() diff --git a/api/middleware/auth.go b/api/middleware/auth.go index 1bf894e..2f46126 100644 --- a/api/middleware/auth.go +++ b/api/middleware/auth.go @@ -7,6 +7,7 @@ import ( "net/http" "strings" + "github.com/ivinayakg/shorte.live/api/constants" "github.com/ivinayakg/shorte.live/api/helpers" "github.com/ivinayakg/shorte.live/api/models" "github.com/ivinayakg/shorte.live/api/utils" @@ -19,14 +20,26 @@ const UserAuthKey userAuth = "User" func Authentication(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - tokenHeader := strings.Split(r.Header.Get("Authorization"), "Bearer ") - if len(tokenHeader) < 2 { - errMsg := "Authentication error!, Provide valid auth token" + var token string + + cookie := utils.GetCookie(r) + if cookie != nil { + token = cookie.Value + } else if helpers.ENV != string(constants.Prod) { + tokenHeader := strings.Split(r.Header.Get("Authorization"), "Bearer ") + if len(tokenHeader) < 2 { + errMsg := "Authentication error!, Provide valid auth token" + helpers.SendJSONError(w, http.StatusForbidden, errMsg) + log.Println(errMsg) + return + } + token = tokenHeader[1] + } else { + errMsg := "Authentication error!, login first" helpers.SendJSONError(w, http.StatusForbidden, errMsg) log.Println(errMsg) return } - token := tokenHeader[1] systemNotAvailable := helpers.SystemUnderMaintenance(false) if systemNotAvailable { @@ -54,7 +67,9 @@ func Authentication(next http.Handler) http.Handler { return } - user.Token = token + if helpers.ENV != string(constants.Prod) { + user.Token = token + } c := context.WithValue(r.Context(), UserAuthKey, user) next.ServeHTTP(w, r.WithContext(c)) diff --git a/api/sample.env b/api/sample.env index 9bb3819..916f8b3 100644 --- a/api/sample.env +++ b/api/sample.env @@ -20,4 +20,5 @@ DB_CONFIG_COLLECTION_NAME="config" SHORTED_URL_DOMAIN="localhost:5100" FRONTEND_URL_MAINTENANCE="http://localhost:5173/maintenance" UI_NOT_FOUND_URL="http://localhost:5173/not-found/redirect" -ENV="development" \ No newline at end of file +ENV="development" +COOKIE_NAME="shorte-cookie" \ No newline at end of file diff --git a/api/tests/integration/main_test.go b/api/tests/integration/main_test.go index abd9ef1..84edb23 100644 --- a/api/tests/integration/main_test.go +++ b/api/tests/integration/main_test.go @@ -57,6 +57,7 @@ func setupTests() func() { helpers.CreateDBInstance() helpers.RedisSetup() helpers.SetupTracker(time.Second*2, 5, 0) + helpers.ENV = "test" go helpers.Tracker.StartFlush() diff --git a/api/tests/integration/system_test.go b/api/tests/integration/system_test.go index 23e2739..50dfc96 100644 --- a/api/tests/integration/system_test.go +++ b/api/tests/integration/system_test.go @@ -14,7 +14,8 @@ import ( func TestURLSystemAvailability(t *testing.T) { req, err := http.NewRequest(http.MethodGet, ServerURL+"/system/available", nil) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } testhelper.PutSystemUnderMaintenance(helpers.Redis, false) @@ -22,7 +23,8 @@ func TestURLSystemAvailability(t *testing.T) { client := &http.Client{} resp, err := client.Do(req) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } body := map[string]interface{}{} @@ -36,7 +38,8 @@ func TestURLSystemAvailability(t *testing.T) { func TestURLSystemAvailabilityFail(t *testing.T) { req, err := http.NewRequest(http.MethodGet, ServerURL+"/system/available", nil) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } testhelper.PutSystemUnderMaintenance(helpers.Redis, true) @@ -45,7 +48,8 @@ func TestURLSystemAvailabilityFail(t *testing.T) { client := &http.Client{} resp, err := client.Do(req) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } body := map[string]interface{}{} @@ -59,7 +63,8 @@ func TestURLSystemAvailabilityFail(t *testing.T) { func TestNotFound(t *testing.T) { resp, err := RedirecthttpClient.Get(ServerURL + "/" + "something/random") if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } notFoundUrl := os.Getenv("UI_NOT_FOUND_URL") @@ -71,7 +76,8 @@ func TestNotFound(t *testing.T) { func TestRedirectHome(t *testing.T) { resp, err := RedirecthttpClient.Get(ServerURL + "/") if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } assert.Equal(t, resp.StatusCode, http.StatusSeeOther, "Excpected status code to be 303") diff --git a/api/tests/integration/tracking_test.go b/api/tests/integration/tracking_test.go index edf2628..8fc33bc 100644 --- a/api/tests/integration/tracking_test.go +++ b/api/tests/integration/tracking_test.go @@ -16,7 +16,8 @@ import ( func TestURLRedirectTracking(t *testing.T) { resp, err := RedirecthttpClient.Get(ServerURL + "/" + URLFixture.Short) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } destinationURL := URLFixture.Destination @@ -29,7 +30,8 @@ func TestURLRedirectTracking(t *testing.T) { for result == nil { err := helpers.CurrentDb.RedirectEvent.FindOne(context.Background(), bson.M{"url_id": URLFixture.ID}).Decode(&result) if err != nil && err != mongo.ErrNoDocuments { - t.Fatal(err) + t.Log(err) + t.Fail() } time.Sleep(time.Second * 2) } diff --git a/api/tests/integration/url_test.go b/api/tests/integration/url_test.go index 3884bbf..cda75ca 100644 --- a/api/tests/integration/url_test.go +++ b/api/tests/integration/url_test.go @@ -20,13 +20,15 @@ var RedirecthttpClient = &http.Client{ return http.ErrUseLastResponse }, } + var HttpClient = &http.Client{} // resolve url func TestURLResolve(t *testing.T) { resp, err := RedirecthttpClient.Get(ServerURL + "/" + URLFixture.Short) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } destinationURL := URLFixture.Destination @@ -38,7 +40,8 @@ func TestURLResolve(t *testing.T) { func TestURLResolveNotFound(t *testing.T) { resp, err := RedirecthttpClient.Get(ServerURL + "/random") if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } notFoundurl := os.Getenv("UI_NOT_FOUND_URL") @@ -50,7 +53,8 @@ func TestURLResolveNotFound(t *testing.T) { func TestURLResolveExpired(t *testing.T) { resp, err := RedirecthttpClient.Get(ServerURL + "/" + ExpiredURLFixture.Short) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } notFoundurl := os.Getenv("UI_NOT_FOUND_URL") @@ -62,14 +66,16 @@ func TestURLResolveExpired(t *testing.T) { // test get user urls func TestGetUserURLs(t *testing.T) { userJwt, _ := utils.CreateJWT(&UserFixture1) + authCookie := utils.CreateAuthCookie(*userJwt) req, _ := http.NewRequest(http.MethodGet, ServerURL+"/url/all", nil) - req.Header.Add("Authorization", "Bearer "+*userJwt) + req.AddCookie(authCookie) resp, err := HttpClient.Do(req) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } respBody := []map[string]interface{}{} @@ -93,14 +99,16 @@ func TestCreateShortedUrl(t *testing.T) { payloadJSON, _ := json.Marshal(payloadData) userJwt, _ := utils.CreateJWT(&UserFixture1) + authCookie := utils.CreateAuthCookie(*userJwt) req, _ := http.NewRequest(http.MethodPost, ServerURL+"/url", bytes.NewBuffer(payloadJSON)) - req.Header.Add("Authorization", "Bearer "+*userJwt) + req.AddCookie(authCookie) resp, err := HttpClient.Do(req) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } respBody := map[string]interface{}{} @@ -124,14 +132,16 @@ func TestCreateShortedUrlWithInvalidUrl(t *testing.T) { payloadJSON, _ := json.Marshal(payloadData) userJwt, _ := utils.CreateJWT(&UserFixture1) + authCookie := utils.CreateAuthCookie(*userJwt) req, _ := http.NewRequest(http.MethodPost, ServerURL+"/url", bytes.NewBuffer(payloadJSON)) - req.Header.Add("Authorization", "Bearer "+*userJwt) + req.AddCookie(authCookie) resp, err := HttpClient.Do(req) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } respBody := map[string]interface{}{} @@ -157,14 +167,16 @@ func TestCreateShortedUrlWithInvalidUrl2(t *testing.T) { payloadJSON, _ := json.Marshal(payloadData) userJwt, _ := utils.CreateJWT(&UserFixture1) + authCookie := utils.CreateAuthCookie(*userJwt) req, _ := http.NewRequest(http.MethodPost, ServerURL+"/url", bytes.NewBuffer(payloadJSON)) - req.Header.Add("Authorization", "Bearer "+*userJwt) + req.AddCookie(authCookie) resp, err := HttpClient.Do(req) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } respBody := map[string]interface{}{} @@ -187,14 +199,16 @@ func TestCreateShortedUrlWithPreoccupiedShort(t *testing.T) { payloadJSON, _ := json.Marshal(payloadData) userJwt, _ := utils.CreateJWT(&UserFixture1) + authCookie := utils.CreateAuthCookie(*userJwt) req, _ := http.NewRequest(http.MethodPost, ServerURL+"/url", bytes.NewBuffer(payloadJSON)) - req.Header.Add("Authorization", "Bearer "+*userJwt) + req.AddCookie(authCookie) resp, err := HttpClient.Do(req) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } respBody := map[string]interface{}{} @@ -217,14 +231,16 @@ func TestCreateShortedUrlWithInvalidShort(t *testing.T) { payloadJSON, _ := json.Marshal(payloadData) userJwt, _ := utils.CreateJWT(&UserFixture1) + authCookie := utils.CreateAuthCookie(*userJwt) req, _ := http.NewRequest(http.MethodPost, ServerURL+"/url", bytes.NewBuffer(payloadJSON)) - req.Header.Add("Authorization", "Bearer "+*userJwt) + req.AddCookie(authCookie) resp, err := HttpClient.Do(req) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } respBody := map[string]interface{}{} @@ -247,14 +263,16 @@ func TestCreateShortedUrlWithDuplicateShort(t *testing.T) { payloadJSON, _ := json.Marshal(payloadData) userJwt, _ := utils.CreateJWT(&UserFixture1) + authCookie := utils.CreateAuthCookie(*userJwt) req, _ := http.NewRequest(http.MethodPost, ServerURL+"/url", bytes.NewBuffer(payloadJSON)) - req.Header.Add("Authorization", "Bearer "+*userJwt) + req.AddCookie(authCookie) resp, err := HttpClient.Do(req) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } respBody := map[string]interface{}{} @@ -276,14 +294,16 @@ func TestUpdateURL(t *testing.T) { payloadJSON, _ := json.Marshal(payloadData) userJwt, _ := utils.CreateJWT(&UserFixture1) + authCookie := utils.CreateAuthCookie(*userJwt) req, _ := http.NewRequest(http.MethodPatch, ServerURL+"/url/"+URLFixture.ID.Hex(), bytes.NewBuffer(payloadJSON)) - req.Header.Add("Authorization", "Bearer "+*userJwt) + req.AddCookie(authCookie) resp, err := HttpClient.Do(req) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } respBody := map[string]interface{}{} @@ -306,14 +326,16 @@ func TestUpdateURLInvalidId(t *testing.T) { payloadJSON, _ := json.Marshal(payloadData) userJwt, _ := utils.CreateJWT(&UserFixture1) + authCookie := utils.CreateAuthCookie(*userJwt) req, _ := http.NewRequest(http.MethodPatch, ServerURL+"/url/"+primitive.NewObjectID().Hex(), bytes.NewBuffer(payloadJSON)) - req.Header.Add("Authorization", "Bearer "+*userJwt) + req.AddCookie(authCookie) resp, err := HttpClient.Do(req) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } respBody := map[string]interface{}{} @@ -334,14 +356,16 @@ func TestUpdateURLUnauthorized(t *testing.T) { payloadJSON, _ := json.Marshal(payloadData) userJwt, _ := utils.CreateJWT(&UserFixture2) + authCookie := utils.CreateAuthCookie(*userJwt) req, _ := http.NewRequest(http.MethodPatch, ServerURL+"/url/"+URLFixture.ID.Hex(), bytes.NewBuffer(payloadJSON)) - req.Header.Add("Authorization", "Bearer "+*userJwt) + req.AddCookie(authCookie) resp, err := HttpClient.Do(req) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } respBody := map[string]interface{}{} @@ -355,14 +379,16 @@ func TestUpdateURLUnauthorized(t *testing.T) { // delete url func TestDeleteURL(t *testing.T) { userJwt, _ := utils.CreateJWT(&UserFixture1) + authCookie := utils.CreateAuthCookie(*userJwt) req, _ := http.NewRequest(http.MethodDelete, ServerURL+"/url/"+ExpiredURLFixture.ID.Hex(), nil) - req.Header.Add("Authorization", "Bearer "+*userJwt) + req.AddCookie(authCookie) resp, err := HttpClient.Do(req) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } assert.Equal(t, resp.StatusCode, http.StatusNoContent, "Excpected status code to be 204") @@ -370,14 +396,16 @@ func TestDeleteURL(t *testing.T) { func TestDeleteURLInvalidId(t *testing.T) { userJwt, _ := utils.CreateJWT(&UserFixture1) + authCookie := utils.CreateAuthCookie(*userJwt) req, _ := http.NewRequest(http.MethodDelete, ServerURL+"/url/"+primitive.NewObjectID().Hex(), nil) - req.Header.Add("Authorization", "Bearer "+*userJwt) + req.AddCookie(authCookie) resp, err := HttpClient.Do(req) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } var respBody map[string]interface{} @@ -389,13 +417,15 @@ func TestDeleteURLInvalidId(t *testing.T) { func TestDeleteURLUnauthorized(t *testing.T) { userJwt, _ := utils.CreateJWT(&UserFixture2) + authCookie := utils.CreateAuthCookie(*userJwt) req, _ := http.NewRequest(http.MethodDelete, ServerURL+"/url/"+primitive.NewObjectID().Hex(), nil) - req.Header.Add("Authorization", "Bearer "+*userJwt) + req.AddCookie(authCookie) resp, err := HttpClient.Do(req) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } var respBody map[string]interface{} diff --git a/api/tests/integration/user_test.go b/api/tests/integration/user_test.go index f28bc66..4f255a7 100644 --- a/api/tests/integration/user_test.go +++ b/api/tests/integration/user_test.go @@ -3,6 +3,7 @@ package integration_tests import ( "context" "encoding/json" + "fmt" "net/http" "testing" @@ -21,7 +22,8 @@ func TestGoogleLogin(t *testing.T) { resp, err := httpClient.Get(ServerURL + "/user/sign_in_with_google") if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } assert.Equal(t, http.StatusFound, resp.StatusCode, "Expected status code to be 302") assert.Contains(t, resp.Header.Get("Location"), "https://accounts.google.com/o/oauth2/auth", "Expected redirect to Google OAuth URL") @@ -33,24 +35,27 @@ func TestSelfUser(t *testing.T) { TestDb.User.FindOne(context.Background(), bson.M{"email": "test1@gmail.com"}).Decode(&user) userJwt, _ := utils.CreateJWT(&user) + authCookie := utils.CreateAuthCookie(*userJwt) req, err := http.NewRequest(http.MethodGet, ServerURL+"/user/self", nil) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } - - req.Header.Add("Authorization", "Bearer "+*userJwt) + req.AddCookie(authCookie) // Send the request using the default HTTP client client := &http.Client{} resp, err := client.Do(req) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } body := map[string]interface{}{} json.NewDecoder(resp.Body).Decode(&body) + fmt.Println(body) assert.Equal(t, resp.StatusCode, http.StatusOK, "Expected status code to be 200") assert.IsType(t, body["_id"], "string", "Expected _id to be a string") @@ -60,16 +65,16 @@ func TestSelfUser(t *testing.T) { func TestSelfUserUnauthenticated(t *testing.T) { req, err := http.NewRequest(http.MethodGet, ServerURL+"/user/self", nil) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } - req.Header.Add("Authorization", "Bearer hello") - // Send the request using the default HTTP client client := &http.Client{} resp, err := client.Do(req) if err != nil { - t.Fatal(err) + t.Log(err) + t.Fail() } body := map[string]interface{}{} diff --git a/api/tests/test.env b/api/tests/test.env index 1caecca..8a233c1 100644 --- a/api/tests/test.env +++ b/api/tests/test.env @@ -20,4 +20,5 @@ DB_CONFIG_COLLECTION_NAME="config" SHORTED_URL_DOMAIN="localhost:5100" FRONTEND_URL_MAINTENANCE="http://localhost:5173/maintenance" UI_NOT_FOUND_URL="http://localhost:5173/not-found/redirect" -ENV="development" \ No newline at end of file +ENV="development" +COOKIE_NAME="shorte-cookie" \ No newline at end of file diff --git a/api/utils/jwt.go b/api/utils/jwt.go index 9fe230a..8f3adcc 100644 --- a/api/utils/jwt.go +++ b/api/utils/jwt.go @@ -2,6 +2,7 @@ package utils import ( "fmt" + "net/http" "os" "strconv" "time" @@ -62,3 +63,44 @@ func VerifyJwt(tokenString string) (*map[string]string, error) { return nil, fmt.Errorf("failed to extract claims from token") } } + +func CreateAuthCookie(token string) *http.Cookie { + cookieName := os.Getenv("COOKIE_NAME") + var expiry = os.Getenv("JWT_EXPIRY") + expiryTotal, err := strconv.Atoi(expiry) + if err != nil { + fmt.Println("Error:", err) + expiryTotal = 21600 + } + + return &http.Cookie{ + Name: cookieName, + Value: token, + MaxAge: expiryTotal, + HttpOnly: true, + Secure: true, + SameSite: http.SameSiteLaxMode, + Path: "/", + } +} + +func RemoveAuthCookie() *http.Cookie { + cookieName := os.Getenv("COOKIE_NAME") + return &http.Cookie{ + Name: cookieName, + Value: "", + Expires: time.Now().Add(-time.Hour), + HttpOnly: true, + Secure: true, + SameSite: http.SameSiteLaxMode, + } +} + +func GetCookie(r *http.Request) *http.Cookie { + cookieName := os.Getenv("COOKIE_NAME") + cookie, err := r.Cookie(cookieName) + if err != nil { + return nil + } + return cookie +} diff --git a/client/src/App.tsx b/client/src/App.tsx index dea8966..357716d 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -3,7 +3,7 @@ import "./App.css"; import { createBrowserRouter, Outlet, RouterProvider } from "react-router-dom"; import { MainProvider } from "@/components/main-provider"; import { Toaster } from "@/components/ui/toaster"; -import { AuthComponent } from "@/utils/auth"; +// import { AuthComponent } from "@/utils/auth"; import MyUrls from "@/pages/MyUrls"; import Header from "@/components/Header"; import CreateShort from "@/pages/CreateShort"; @@ -33,10 +33,10 @@ const router = createBrowserRouter([ }, ], }, - { - path: "/auth/", - element: , - }, + // { + // path: "/auth/", + // element: , + // }, ]); function Main() { diff --git a/client/src/components/DeleteURLModal.tsx b/client/src/components/DeleteURLModal.tsx index 19aeaee..b7f2ceb 100644 --- a/client/src/components/DeleteURLModal.tsx +++ b/client/src/components/DeleteURLModal.tsx @@ -30,7 +30,7 @@ function DeleteURL( const { toast } = useToast(); const deleteURLForm = async () => { const res = await fetch.delete(`/url/${urlObj._id}`, { - headers: { Authorization: `Bearer ${userState.token}` }, + withCredentials: true, }); if (res.status === 204) { toast({ diff --git a/client/src/components/UpdateURLModal.tsx b/client/src/components/UpdateURLModal.tsx index 903ee4f..d49cfd1 100644 --- a/client/src/components/UpdateURLModal.tsx +++ b/client/src/components/UpdateURLModal.tsx @@ -55,7 +55,7 @@ function UpdateURL( } const res = await fetch.patch(`/url/${urlObj._id}`, request, { - headers: { Authorization: `Bearer ${userState.token}` }, + withCredentials: true, }); if (res.status === 204) { toast({ diff --git a/client/src/components/main-provider.tsx b/client/src/components/main-provider.tsx index 1968d91..637b003 100644 --- a/client/src/components/main-provider.tsx +++ b/client/src/components/main-provider.tsx @@ -1,8 +1,7 @@ -import auth from "@/utils/auth"; -import { getFromLocalStorage } from "@/utils/localstorage"; import { createContext, useContext, useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { useToast } from "@/components/ui/use-toast"; +import fetch from "@/utils/axios"; export type UserState = { email: string; @@ -10,7 +9,6 @@ export type UserState = { picture: string; _id: string; created_at: string; - token: string; login: boolean; }; @@ -29,7 +27,6 @@ const initialUserState: UserState = { picture: "", _id: "", created_at: "", - token: "", login: false, }; @@ -52,22 +49,18 @@ export function MainProvider({ children, ...props }: MainProviderProps) { useEffect(() => { (async () => { try { - const token = getFromLocalStorage("userToken"); - if (token) { - const response = await auth(token); - if (response?.status !== 200) { - resetState(setUserState); - navigate("/"); - } else { - setUserState({ ...response.data, login: true }); - toast({ - title: "Logged in Successfully", - duration: 2000, - }); - } - } else { + const response = await fetch.get("/user/self", { + withCredentials: true, + }); + if (response?.status !== 200) { resetState(setUserState); navigate("/"); + } else { + setUserState({ ...response.data, login: true }); + toast({ + title: "Logged in Successfully", + duration: 2000, + }); } } catch (e: any) { if (typeof e === "string") toast({ title: e, duration: 2000 }); diff --git a/client/src/pages/CreateShort.tsx b/client/src/pages/CreateShort.tsx index 6580cc1..73dbb13 100644 --- a/client/src/pages/CreateShort.tsx +++ b/client/src/pages/CreateShort.tsx @@ -35,7 +35,7 @@ function CreateShort() { }; const res = await fetch.post("/url", requestData, { - headers: { Authorization: `Bearer ${userState.token}` }, + withCredentials: true, }); if (res.status !== 201) { throw new Error("Check again later"); diff --git a/client/src/pages/MyUrls.tsx b/client/src/pages/MyUrls.tsx index 5a201e2..a6dfb07 100644 --- a/client/src/pages/MyUrls.tsx +++ b/client/src/pages/MyUrls.tsx @@ -21,7 +21,7 @@ function MyUrls() { if (userState.login && location.pathname === "/my-urls") { (async () => { const res = await fetch.get("/url/all", { - headers: { Authorization: `Bearer ${userState.token}` }, + withCredentials: true, }); const data = res.data; setUrlsData(data); diff --git a/client/src/utils/auth.tsx b/client/src/utils/auth.tsx index 53d4270..f228efc 100644 --- a/client/src/utils/auth.tsx +++ b/client/src/utils/auth.tsx @@ -1,21 +1,7 @@ import { useNavigate, useSearchParams } from "react-router-dom"; -import fetch from "./axios"; import { setInLocalStorage } from "./localstorage"; import { useEffect } from "react"; -export default async function (token: string) { - if (!token) return; - const res = await fetch.get("/user/self", { - headers: { Authorization: `Bearer ${token}` }, - }); - if (res.status !== 200) { - setInLocalStorage("userToken", null); - } else { - setInLocalStorage("userToken", token); - } - return res; -} - function AuthComponent() { const [searchParams] = useSearchParams(); const navigate = useNavigate();