diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e69de29 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..411353d --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# Environment variables +.env + +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work \ No newline at end of file diff --git a/README.md b/README.md index 37ede64..56e6bd1 100644 --- a/README.md +++ b/README.md @@ -1 +1,2 @@ -# csl-backend \ No newline at end of file +# csl-backend +## Combat Surf League API \ No newline at end of file diff --git a/controller/home.go b/controller/home.go new file mode 100644 index 0000000..2f3df1f --- /dev/null +++ b/controller/home.go @@ -0,0 +1,7 @@ +package controller + +import "net/http" + +func Home(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("home")) +} diff --git a/controller/login.go b/controller/login.go new file mode 100644 index 0000000..bd980c1 --- /dev/null +++ b/controller/login.go @@ -0,0 +1,37 @@ +package controller + +import ( + "encoding/json" + "net/http" + + "github.com/robyzzz/csl-backend/utils/env" + "github.com/robyzzz/csl-backend/utils/store" + "github.com/solovev/steam_go" +) + +func Login(w http.ResponseWriter, r *http.Request) { + opId := steam_go.NewOpenId(r) + + switch opId.Mode() { + case "cancel": + http.Redirect(w, r, "/", http.StatusTemporaryRedirect) + case "": + // redirect to steam auth + http.Redirect(w, r, opId.AuthUrl(), http.StatusTemporaryRedirect) + default: + // login success + + // get user + user, err := opId.ValidateAndGetUser(env.STEAM_API_KEY) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + + store.CreateSessionID(w, r, user.SteamId) + + // TODO: store user info in database + + w.WriteHeader(http.StatusCreated) + json.NewEncoder(w).Encode(user) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..84816cf --- /dev/null +++ b/go.mod @@ -0,0 +1,15 @@ +module github.com/robyzzz/csl-backend + +go 1.17 + +require ( + github.com/gorilla/mux v1.8.0 + github.com/gorilla/sessions v1.2.1 + github.com/solovev/steam_go v0.0.0-20170222182106-48eb5aae6c50 + +) + +require ( + github.com/gorilla/securecookie v1.1.1 // indirect + github.com/joho/godotenv v1.4.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0243420 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/solovev/steam_go v0.0.0-20170222182106-48eb5aae6c50 h1:5wu+B07+rk5rr6KYxYK+5fRr+m8ikSblPSgDTdrFUE4= +github.com/solovev/steam_go v0.0.0-20170222182106-48eb5aae6c50/go.mod h1:wDBDgAJlQWhdrpQeJcw6+FZwMddaNWFUo8u8bSfzA50= diff --git a/middleware/auth.go b/middleware/auth.go new file mode 100644 index 0000000..eedf4d0 --- /dev/null +++ b/middleware/auth.go @@ -0,0 +1,22 @@ +package middleware + +import ( + "net/http" + + "github.com/robyzzz/csl-backend/utils/store" +) + +// Authentication middleware called on routes that need to know if user is logged in. +// Returns to root page if session already exists. +// i.e usage: /login +func IsAuthenticated(h func(w http.ResponseWriter, r *http.Request)) http.Handler { + next := http.HandlerFunc(h) + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if store.SessionAlreadyExists(r) { + http.Redirect(w, r, "/", http.StatusFound) + return + } + + next.ServeHTTP(w, r) + }) +} diff --git a/server.go b/server.go new file mode 100644 index 0000000..b3b682b --- /dev/null +++ b/server.go @@ -0,0 +1,29 @@ +package main + +import ( + "fmt" + "log" + "net/http" + + "github.com/gorilla/mux" + "github.com/robyzzz/csl-backend/controller" + "github.com/robyzzz/csl-backend/middleware" + "github.com/robyzzz/csl-backend/utils/env" +) + +var router *mux.Router + +func main() { + env.GetEnvVariables() + + setupRouter() + + fmt.Printf("router initialized and listening on %s\n", env.PORT) + log.Fatal(http.ListenAndServe(":"+env.PORT, router)) +} + +func setupRouter() { + router = mux.NewRouter() + router.HandleFunc("/", controller.Home) + router.Handle("/login", middleware.IsAuthenticated(controller.Login)) +} diff --git a/utils/env/env.go b/utils/env/env.go new file mode 100644 index 0000000..feea62d --- /dev/null +++ b/utils/env/env.go @@ -0,0 +1,25 @@ +package env + +import ( + "log" + "os" + + "github.com/joho/godotenv" +) + +var SESSION_SECRET_KEY string +var SESSION_NAME string +var STEAM_API_KEY string +var PORT string + +func GetEnvVariables() { + err := godotenv.Load(".env") + if err != nil { + log.Fatalf("Error loading .env file") + } + + SESSION_SECRET_KEY = os.Getenv("SESSION_SECRET_KEY") + SESSION_NAME = os.Getenv("SESSION_NAME") + STEAM_API_KEY = os.Getenv("STEAM_API_KEY") + PORT = os.Getenv("PORT") +} diff --git a/utils/store/store.go b/utils/store/store.go new file mode 100644 index 0000000..48725b1 --- /dev/null +++ b/utils/store/store.go @@ -0,0 +1,21 @@ +package store + +import ( + "net/http" + + "github.com/gorilla/sessions" + "github.com/robyzzz/csl-backend/utils/env" +) + +var Store = sessions.NewCookieStore([]byte("test")) + +func SessionAlreadyExists(r *http.Request) bool { + session, _ := Store.Get(r, env.SESSION_NAME) + return !session.IsNew +} + +func CreateSessionID(w http.ResponseWriter, r *http.Request, value string) error { + session, _ := Store.Get(r, env.SESSION_NAME) + session.Values["session-id"] = value + return session.Save(r, w) +}