aboutsummaryrefslogtreecommitdiff
path: root/auth
diff options
context:
space:
mode:
Diffstat (limited to 'auth')
-rw-r--r--auth/config.go34
-rw-r--r--auth/cookie.go21
-rw-r--r--auth/crypto.go54
-rw-r--r--auth/middlewares.go92
4 files changed, 0 insertions, 201 deletions
diff --git a/auth/config.go b/auth/config.go
deleted file mode 100644
index 6a55839..0000000
--- a/auth/config.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package auth
-
-import (
- "encoding/hex"
- "errors"
- "os"
-
- "github.com/martinlehoux/kagamigo/core"
- "golang.org/x/exp/slog"
-)
-
-var (
- ErrCookieBadLength = errors.New("cookie secret must be 32 bytes")
-)
-
-type AuthConfig struct {
- Domain string
- CookieSecret []byte
-}
-
-func LoadCookieSecret(cookieSecretString string) []byte {
- cookiesSecret, err := hex.DecodeString(cookieSecretString)
- if err != nil {
- err = core.Wrap(err, "error decoding cookie secret")
- slog.Error(err.Error())
- os.Exit(1)
- }
- if len(cookiesSecret) != 32 {
- slog.Error(ErrCookieBadLength.Error())
- os.Exit(1)
- }
- slog.Info("cookie secret loaded")
- return cookiesSecret
-}
diff --git a/auth/cookie.go b/auth/cookie.go
deleted file mode 100644
index 61349fa..0000000
--- a/auth/cookie.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package auth
-
-import (
- "fmt"
- "net/http"
- "time"
-
- "github.com/martinlehoux/kagamigo/core"
-)
-
-func CraftCookie(id core.ID, config AuthConfig) http.Cookie {
- expiresAt := time.Now().Add(24 * time.Hour)
- cookieValue := encrypt(config.CookieSecret, fmt.Sprintf("%s:%d", id.String(), expiresAt.Unix()))
- return http.Cookie{
- Domain: config.Domain,
- Name: "authentication",
- Value: cookieValue,
- Expires: expiresAt,
- Path: "/",
- }
-}
diff --git a/auth/crypto.go b/auth/crypto.go
deleted file mode 100644
index 254539f..0000000
--- a/auth/crypto.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package auth
-
-import (
- "crypto/aes"
- "crypto/cipher"
- "crypto/rand"
- "encoding/base64"
- "errors"
-
- "github.com/martinlehoux/kagamigo/core"
-)
-
-var (
- ErrEncryptedTooShort = errors.New("encrypted text is too short")
-)
-
-func encrypt(secret []byte, plainText string) string {
- block, err := aes.NewCipher(secret)
- core.Expect(err, "error creating AES cipher")
-
- aead, err := cipher.NewGCM(block)
- core.Expect(err, "error creating AEAD")
-
- nonce := make([]byte, aead.NonceSize())
- _, err = rand.Read(nonce)
- core.Expect(err, "error generating nonce")
-
- return base64.URLEncoding.EncodeToString(aead.Seal(nonce, nonce, []byte(plainText), nil))
-}
-
-func decrypt(secret []byte, encryptedText string) (string, error) {
- encryptedBytes, err := base64.URLEncoding.DecodeString(encryptedText)
- if err != nil {
- err = core.Wrap(err, "error decoding base64")
- return "", err
- }
- block, err := aes.NewCipher(secret)
- core.Expect(err, "error creating AES cipher")
-
- aead, err := cipher.NewGCM(block)
- core.Expect(err, "error creating AEAD")
-
- nonceSize := aead.NonceSize()
- if len(encryptedBytes) < nonceSize {
- return "", ErrEncryptedTooShort
- }
- nonce, cipherText := encryptedBytes[:nonceSize], encryptedBytes[nonceSize:]
- plainBytes, err := aead.Open(nil, nonce, cipherText, nil)
- if err != nil {
- err = core.Wrap(err, "error decrypting")
- return "", err
- }
- return string(plainBytes), nil
-}
diff --git a/auth/middlewares.go b/auth/middlewares.go
deleted file mode 100644
index 1534b16..0000000
--- a/auth/middlewares.go
+++ /dev/null
@@ -1,92 +0,0 @@
-package auth
-
-import (
- "context"
- "errors"
- "net/http"
- "strconv"
- "strings"
- "time"
-
- "github.com/martinlehoux/kagamigo/core"
- "golang.org/x/exp/slog"
-)
-
-var (
- ErrUserNotLoggedIn = errors.New("user not logged in")
- ErrCookieExpired = errors.New("cookie expired")
- ErrBadCookie = errors.New("invalid cookie")
-)
-
-type userContext struct{}
-
-func UserFromContext[U any](ctx context.Context) (U, bool) {
- user, ok := ctx.Value(userContext{}).(U)
- return user, ok
-}
-
-func LoginFromContext[U core.WithLanguage](ctx context.Context) core.Login[U] {
- user, ok := UserFromContext[U](ctx)
- return core.LoginFromUser(user, ok)
-}
-
-func Unauthorized(w http.ResponseWriter, err error) {
- w.WriteHeader(http.StatusUnauthorized)
- if err != nil {
- _, err = w.Write([]byte(err.Error()))
- core.Expect(err, "error writing response")
- }
-}
-
-func CookieAuthMiddleware(loadUser func(context.Context, core.ID) (any, error), config AuthConfig) func(http.Handler) http.Handler {
- return func(next http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- ctx := r.Context()
- cookie, err := r.Cookie("authentication")
- if errors.Is(err, http.ErrNoCookie) {
- next.ServeHTTP(w, r)
- return
- }
- core.Expect(err, "error reading cookie")
-
- authentication, err := decrypt(config.CookieSecret, cookie.Value)
- if err != nil {
- err = core.Wrap(err, "error decrypting cookie")
- slog.Warn(err.Error())
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
- parts := strings.Split(authentication, ":")
- if len(parts) != 2 {
- slog.Warn(ErrBadCookie.Error())
- http.Error(w, ErrBadCookie.Error(), http.StatusBadRequest)
- return
- }
- userId, err := core.ParseID(parts[0])
- if err != nil {
- err = core.Wrap(err, "error parsing user id")
- slog.Warn(err.Error())
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
- expiresAtSeconds, err := strconv.Atoi(parts[1])
- if err != nil {
- err = core.Wrap(err, "error parsing expires at")
- slog.Warn(err.Error())
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
- expiresAt := time.Unix(int64(expiresAtSeconds), 0)
- if time.Now().After(expiresAt) {
- slog.Warn(ErrCookieExpired.Error())
- http.Error(w, ErrCookieExpired.Error(), http.StatusUnauthorized)
- return
- }
- user, err := loadUser(ctx, userId)
- core.Expect(err, "error loading user")
-
- ctx = context.WithValue(ctx, userContext{}, user)
- next.ServeHTTP(w, r.WithContext(ctx))
- })
- }
-}