commit 0a24c08c3e23ef7f9a05529d9c7c72792e0618e1
parent b208717e2030b059d34df75c9abe88ec8950d230
Author: Vetle Haflan <vetle@haflan.dev>
Date: Sat, 12 Feb 2022 17:13:48 +0100
wip: work on making redis-session into usable auth server
Diffstat:
7 files changed, 211 insertions(+), 97 deletions(-)
diff --git a/go/redis-session/cmd/auth-server/main.go b/go/redis-session/cmd/auth-server/main.go
@@ -0,0 +1,80 @@
+package main
+
+import (
+ "crypto/rand"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+
+ "gl.haflan.dev/general/experiments/go/redis/session"
+)
+
+var sessionIDSize = 32
+
+func getRand() (string, error) {
+ sidBytes := make([]byte, sessionIDSize)
+ _, err := io.ReadFull(rand.Reader, sidBytes)
+ if err != nil {
+ return "", err
+ }
+ sidHex := make([]byte, hex.EncodedLen(len(sidBytes)))
+ hex.Encode(sidHex, sidBytes)
+ return string(sidHex), nil
+}
+
+func postIndex(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ return
+ }
+ sid, err := getRand()
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ err = session.SetSession(sid, "me")
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ c := &http.Cookie{
+ Name: "sess_id",
+ Value: sid,
+ // Setting this includes subdomains
+ Domain: "local.test",
+ }
+ http.SetCookie(w, c)
+ gotoURL := r.URL.Query().Get("goto")
+ if gotoURL != "" {
+ fmt.Println(gotoURL)
+ http.Redirect(w, r, gotoURL, http.StatusPermanentRedirect)
+ }
+}
+
+func getIndex(w http.ResponseWriter, r *http.Request) {
+ // TODO: If logged in, go directly to target page (or generic logged in page if no target)
+ loginPage, _ := os.ReadFile("login.html")
+ w.Write(loginPage)
+}
+
+func handleIndex(w http.ResponseWriter, r *http.Request) {
+ fmt.Println(r.Method)
+ switch r.Method {
+ case http.MethodPost:
+ postIndex(w, r)
+ case http.MethodGet:
+ getIndex(w, r)
+ default:
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ }
+}
+
+func main() {
+ http.HandleFunc("/", handleIndex)
+ if err := http.ListenAndServe(":7676", nil); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+}
diff --git a/go/redis-session/cmd/tester/main.go b/go/redis-session/cmd/tester/main.go
@@ -0,0 +1,44 @@
+package main
+
+import (
+ "fmt"
+ "net/http"
+ "net/url"
+ "os"
+
+ "gl.haflan.dev/general/experiments/go/redis/session"
+)
+
+var authServer string
+
+func init() {
+ authServer = os.Getenv("AUTH_SERVER")
+ u, err := url.Parse(authServer)
+ if err != nil || u.Host == "" {
+ panic("malformed auth server url: " + authServer)
+ }
+}
+
+func handleTest(w http.ResponseWriter, r *http.Request) {
+ gotoURL := authServer + "?goto=" + url.QueryEscape("http://"+r.Host+r.URL.Path)
+ c, err := r.Cookie("sess_id")
+ if err != nil {
+ http.Redirect(w, r, gotoURL, http.StatusTemporaryRedirect)
+ return
+ }
+ fmt.Println("Cookie is:", c.Value)
+ user, err := session.GetSession(c.Value)
+ if err != nil {
+ http.Redirect(w, r, gotoURL, http.StatusTemporaryRedirect)
+ return
+ }
+ w.Write([]byte("logged in as " + user))
+}
+
+func main() {
+ http.HandleFunc("/", handleTest)
+ if err := http.ListenAndServe(":7677", nil); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+}
diff --git a/go/redis-session/login.html b/go/redis-session/login.html
@@ -0,0 +1,20 @@
+<html>
+<body>
+ <div><input type="text" id="username" placeholder="username"><br></div>
+ <div><input type="password" id="password" placeholder="password"></div>
+ <div><button onclick="doLogin()">Login</button></div>
+</html>
+<script type="text/javascript">
+ function doLogin() {
+ let username = document.getElementById("username").value
+ let password = document.getElementById("password").value
+ let data = {username, password}
+ fetch('/' + location.search, {
+ method: 'POST',
+ mode: 'cors',
+ credentials: 'same-origin',
+ body: JSON.stringify(data)
+ })
+ }
+</script>
+</body>+
\ No newline at end of file
diff --git a/go/redis-session/main.go b/go/redis-session/main.go
@@ -1,5 +0,0 @@
-package main
-
-func main() {
- Serve()
-}
diff --git a/go/redis-session/redistest.go b/go/redis-session/redistest.go
@@ -1,36 +0,0 @@
-package main
-
-import (
- "context"
- "fmt"
- "github.com/go-redis/redis/v8"
- "os"
- "time"
-)
-
-var ctx = context.Background()
-
-func redistest() {
- rdb := redis.NewClient(&redis.Options{
- Addr: "localhost:6379",
- Password: "",
- DB: 0,
- })
- if len(os.Args) > 2 && os.Args[1] == "put" {
- err := rdb.Set(
- ctx, "sess:mkey", os.Args[2], time.Duration(5*time.Second),
- ).Err()
- if err != nil {
- fmt.Println("Couldn't set key:", err)
- }
- return
- }
- val, err := rdb.Get(ctx, "sess:mkey").Result()
- if err == redis.Nil {
- fmt.Println("No such key")
- } else if err != nil {
- fmt.Println("Couldn't get key:", err)
- } else {
- fmt.Println(val)
- }
-}
diff --git a/go/redis-session/server.go b/go/redis-session/server.go
@@ -1,56 +0,0 @@
-package main
-
-import (
- "crypto/rand"
- "encoding/hex"
- "fmt"
- "io"
- "net/http"
- "os"
-)
-
-var sessionIDSize = 32
-
-func getRand() (string, error) {
- sidBytes := make([]byte, sessionIDSize)
- _, err := io.ReadFull(rand.Reader, sidBytes)
- if err != nil {
- return "", err
- }
- sidHex := make([]byte, hex.EncodedLen(len(sidBytes)))
- hex.Encode(sidHex, sidBytes)
- return string(sidHex), nil
-}
-
-func handleLogin(w http.ResponseWriter, r *http.Request) {
- sid, err := getRand()
- if err != nil {
- w.WriteHeader(http.StatusInternalServerError)
- return
- }
- c := &http.Cookie{
- Name: "sess_id",
- Value: sid,
- MaxAge: 120,
- }
- http.SetCookie(w, c)
- return
-}
-
-func handleTest(w http.ResponseWriter, r *http.Request) {
- c, err := r.Cookie("sess_id")
- if err != nil {
- fmt.Println("Couldn't get cookie:", err)
- } else {
- fmt.Println(c.Value)
- }
-}
-
-func Serve() {
- http.HandleFunc("/", handleTest)
- http.HandleFunc("/login", handleLogin)
- if err := http.ListenAndServe(":8888", nil); err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
-}
diff --git a/go/redis-session/session/session.go b/go/redis-session/session/session.go
@@ -0,0 +1,66 @@
+package session
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "os"
+ "time"
+
+ "github.com/go-redis/redis/v8"
+)
+
+var rdb *redis.Client
+var ctx = context.Background()
+var expirationTime = time.Duration(15 * time.Second)
+
+func init() {
+ redisAddr := os.Getenv("REDIS_ADDR")
+ redisPW := os.Getenv("REDIS_PW")
+ if redisAddr == "" {
+ redisAddr = "localhost:6379"
+ }
+ rdb = redis.NewClient(&redis.Options{
+ Addr: redisAddr,
+ Password: redisPW,
+ DB: 0,
+ })
+ if rdb == nil {
+ panic("could not init Redis client")
+ }
+}
+
+var (
+ ErrNoSessID = errors.New("empty session id")
+ ErrInvalidSessID = errors.New("invalid session id")
+ ErrExpirationFailed = errors.New("unable to refresh session id expiration")
+)
+
+func GetSession(sessID string) (data string, err error) {
+ if sessID == "" {
+ return "", ErrNoSessID
+ }
+ val, err := rdb.Get(ctx, "sess:"+sessID).Result()
+ if err == redis.Nil {
+ return "", ErrInvalidSessID
+ } else if err != nil {
+ return "", fmt.Errorf("could not read session id: %v", err)
+ }
+ // Keep session alive
+ _, err = rdb.Expire(ctx, "sess:"+sessID, expirationTime).Result()
+ if err != nil {
+ return "", ErrExpirationFailed
+ }
+ return val, err
+}
+
+func SetSession(sessID, data string) error {
+ if sessID == "" {
+ return ErrNoSessID
+ }
+ err := rdb.Set(ctx, "sess:"+sessID, data, expirationTime).Err()
+ if err != nil {
+ return fmt.Errorf("failed to set session id: %v", err)
+ }
+ return nil
+}