Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions controller/grpc/authenticate.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"evolve/proto"
"evolve/util/auth"
dbutil "evolve/util/db/user"
"os"
)

type GRPCServer struct {
Expand All @@ -15,6 +16,7 @@ type GRPCServer struct {

func (*GRPCServer) Auth(ctx context.Context, req *proto.TokenValidateRequest) (*proto.TokenValidateResponse, error) {
user, err := auth.ValidateToken(req.GetToken())

if err != nil {
return &proto.TokenValidateResponse{
Valid: false,
Expand All @@ -28,6 +30,21 @@ func (*GRPCServer) Auth(ctx context.Context, req *proto.TokenValidateRequest) (*
}, err
}

//checking for CSRF token
if os.Getenv("CSRF_PROTECTION") == "true" && req.GetCsrfToken() != "" && user["csrf_token"] != "" {

csrfToken := req.GetCsrfToken()
if csrfToken != user["csrf_token"] {
return &proto.TokenValidateResponse{
Valid: false,
}, nil
}
} else {
return &proto.TokenValidateResponse{
Valid: false,
}, nil
}

userData, err := dbutil.UserById(ctx, user["id"], db)
if err != nil {
return &proto.TokenValidateResponse{
Expand Down
22 changes: 22 additions & 0 deletions controller/login.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
package controller

import (
"crypto/rand"
"encoding/base64"
"evolve/modules"
"evolve/util"
"net/http"
"os"
"time"
)

func generateCSRFToken() string {
b := make([]byte, 32)
rand.Read(b)
return base64.StdEncoding.EncodeToString(b)
}

func Login(res http.ResponseWriter, req *http.Request) {

logger := util.SharedLogger
Expand Down Expand Up @@ -42,5 +51,18 @@ func Login(res http.ResponseWriter, req *http.Request) {

delete(user, "token")

if os.Getenv("CSRF_PROTECTION") == "true" {
csrfToken := generateCSRFToken()
http.SetCookie(res, &http.Cookie{
Name: "csrf_token",
Value: csrfToken,
Path: "/",
HttpOnly: false,
SameSite: http.SameSiteLaxMode,
})

res.Header().Set("X-CSRF-Token", csrfToken)
}

util.JSONResponse(res, http.StatusOK, "Success", user)
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ require (
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.37.0 // indirect
golang.org/x/text v0.30.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda // indirect
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda h1:i/Q+bfisr7gq6feoJnS/DlpdwEL4ihp41fvRiM3Ork0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
Expand Down
17 changes: 14 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"os"
"runtime"

"reflect"

"aidanwoods.dev/go-paseto"
"github.com/rs/cors"
"google.golang.org/grpc"
Expand All @@ -27,6 +29,8 @@ var (

func serveHTTP() {
logger := util.SharedLogger

fmt.Println("type of logger", reflect.TypeOf(&logger))
// Register routes.
http.HandleFunc(routes.TEST, controller.Test)
http.HandleFunc(routes.REGISTER, controller.Register)
Expand All @@ -46,11 +50,18 @@ func serveHTTP() {
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"*"},
AllowCredentials: true,
ExposedHeaders: []string{"X-CSRF-Token"},
}).Handler(http.DefaultServeMux)

handler := util.SharedLogger.LogMiddleware(corsHandler)
var finalHandler http.Handler
if os.Getenv("CSRF_PROTECTION") == "true" {
finalHandler = util.CSRFMiddleware(handler)
} else {
finalHandler = handler
}

if err := http.ListenAndServe(HTTP_PORT, handler); err != nil {
if err := http.ListenAndServe(HTTP_PORT, finalHandler); err != nil {
logger.Error(fmt.Sprintf("Failed to start server: %v", err), err)
return
}
Expand All @@ -75,15 +86,15 @@ func serveGRPC() {

func main() {

logger, err := util.InitLogger(os.Getenv("ENV"))
util.SharedLogger = logger
HTTP_PORT = fmt.Sprintf(":%v", os.Getenv("HTTP_PORT"))
GRPC_PORT = fmt.Sprintf(":%v", os.Getenv("GRPC_PORT"))

logger, err := util.InitLogger(os.Getenv("ENV"))
if err != nil {
fmt.Println("failed to init logger:", err)
return
}
util.SharedLogger = logger

// Initialize db with schema.
if err := db.InitDb(context.Background()); err != nil {
Expand Down
54 changes: 28 additions & 26 deletions proto/authenticate.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions proto/authenticate.proto
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ service Authenticate {
// TokenValidateRequest is the request message for the Authenticate method.
message TokenValidateRequest {
string token = 1;
optional string csrf_token = 2;
}

// TokenValidateResponse is the response message for the Authenticate method.
Expand All @@ -22,4 +23,5 @@ message TokenValidateResponse {
string email = 4;
string userName = 5;
string fullName = 6;

}
2 changes: 1 addition & 1 deletion proto/authenticate_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions util/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,28 @@ func FromJson[T any](data map[string]any) (*T, error) {

return result, nil
}

func CSRFMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

//skipping CSRF authentication for routes where user is not authenticated
if r.URL.Path == "/api/login" || r.URL.Path == "/api/register" || r.URL.Path == "/api/password/reset" || r.URL.Path == "/api/password/verify" || r.URL.Path == "/api/verify" {
next.ServeHTTP(w, r)
return
}

cookie, err := r.Cookie("csrf_token")
if err != nil {
http.Error(w, "CSRF cookie not found", http.StatusForbidden)
return
}

csrfToken := r.Header.Get("X-CSRF-Token")
if csrfToken == "" || csrfToken != cookie.Value {
http.Error(w, "Invalid CSRF token", http.StatusForbidden)
return
}

next.ServeHTTP(w, r)
})
}