mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2025-10-28 04:35:40 +00:00
fix: encrypt the cookie in sessions (#225)
* fix: encrypt the cookie in sessions * tests: use new auth config in tests * fix: coderabbit suggestions
This commit is contained in:
12
cmd/root.go
12
cmd/root.go
@@ -74,6 +74,15 @@ var rootCmd = &cobra.Command{
|
|||||||
csrfCookieName := fmt.Sprintf("%s-%s", constants.CsrfCookieName, cookieId)
|
csrfCookieName := fmt.Sprintf("%s-%s", constants.CsrfCookieName, cookieId)
|
||||||
redirectCookieName := fmt.Sprintf("%s-%s", constants.RedirectCookieName, cookieId)
|
redirectCookieName := fmt.Sprintf("%s-%s", constants.RedirectCookieName, cookieId)
|
||||||
|
|
||||||
|
// Generate HMAC and encryption secrets
|
||||||
|
log.Debug().Msg("Deriving HMAC and encryption secrets")
|
||||||
|
|
||||||
|
hmacSecret, err := utils.DeriveKey(config.Secret, "hmac")
|
||||||
|
HandleError(err, "Failed to derive HMAC secret")
|
||||||
|
|
||||||
|
encryptionSecret, err := utils.DeriveKey(config.Secret, "encryption")
|
||||||
|
HandleError(err, "Failed to derive encryption secret")
|
||||||
|
|
||||||
// Create OAuth config
|
// Create OAuth config
|
||||||
oauthConfig := types.OAuthConfig{
|
oauthConfig := types.OAuthConfig{
|
||||||
GithubClientId: config.GithubClientId,
|
GithubClientId: config.GithubClientId,
|
||||||
@@ -115,13 +124,14 @@ var rootCmd = &cobra.Command{
|
|||||||
authConfig := types.AuthConfig{
|
authConfig := types.AuthConfig{
|
||||||
Users: users,
|
Users: users,
|
||||||
OauthWhitelist: config.OAuthWhitelist,
|
OauthWhitelist: config.OAuthWhitelist,
|
||||||
Secret: config.Secret,
|
|
||||||
CookieSecure: config.CookieSecure,
|
CookieSecure: config.CookieSecure,
|
||||||
SessionExpiry: config.SessionExpiry,
|
SessionExpiry: config.SessionExpiry,
|
||||||
Domain: domain,
|
Domain: domain,
|
||||||
LoginTimeout: config.LoginTimeout,
|
LoginTimeout: config.LoginTimeout,
|
||||||
LoginMaxRetries: config.LoginMaxRetries,
|
LoginMaxRetries: config.LoginMaxRetries,
|
||||||
SessionCookieName: sessionCookieName,
|
SessionCookieName: sessionCookieName,
|
||||||
|
HMACSecret: hmacSecret,
|
||||||
|
EncryptionSecret: encryptionSecret,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create hooks config
|
// Create hooks config
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ var handlersConfig = types.HandlersConfig{
|
|||||||
var authConfig = types.AuthConfig{
|
var authConfig = types.AuthConfig{
|
||||||
Users: types.Users{},
|
Users: types.Users{},
|
||||||
OauthWhitelist: "",
|
OauthWhitelist: "",
|
||||||
Secret: "super-secret-api-thing-for-tests", // It is 32 chars long
|
HMACSecret: "super-secret-api-thing-for-test1",
|
||||||
|
EncryptionSecret: "super-secret-api-thing-for-test2",
|
||||||
CookieSecure: false,
|
CookieSecure: false,
|
||||||
SessionExpiry: 3600,
|
SessionExpiry: 3600,
|
||||||
LoginTimeout: 0,
|
LoginTimeout: 0,
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ type Auth struct {
|
|||||||
|
|
||||||
func (auth *Auth) GetSession(c *gin.Context) (*sessions.Session, error) {
|
func (auth *Auth) GetSession(c *gin.Context) (*sessions.Session, error) {
|
||||||
// Create cookie store
|
// Create cookie store
|
||||||
store := sessions.NewCookieStore([]byte(auth.Config.Secret))
|
store := sessions.NewCookieStore([]byte(auth.Config.HMACSecret), []byte(auth.Config.EncryptionSecret))
|
||||||
|
|
||||||
// Configure cookie store
|
// Configure cookie store
|
||||||
store.Options = &sessions.Options{
|
store.Options = &sessions.Options{
|
||||||
@@ -46,9 +46,21 @@ func (auth *Auth) GetSession(c *gin.Context) (*sessions.Session, error) {
|
|||||||
|
|
||||||
// Get session
|
// Get session
|
||||||
session, err := store.Get(c.Request, auth.Config.SessionCookieName)
|
session, err := store.Get(c.Request, auth.Config.SessionCookieName)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Failed to get session")
|
log.Warn().Err(err).Msg("Invalid session, clearing cookie and retrying")
|
||||||
return nil, err
|
|
||||||
|
// Delete the session cookie if there is an error
|
||||||
|
c.SetCookie(auth.Config.SessionCookieName, "", -1, "/", fmt.Sprintf(".%s", auth.Config.Domain), auth.Config.CookieSecure, true)
|
||||||
|
|
||||||
|
// Try to get the session again
|
||||||
|
session, err = store.Get(c.Request, auth.Config.SessionCookieName)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// If we still can't get the session, log the error and return nil
|
||||||
|
log.Error().Err(err).Msg("Failed to get session")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return session, nil
|
return session, nil
|
||||||
|
|||||||
@@ -80,12 +80,13 @@ type AuthConfig struct {
|
|||||||
Users Users
|
Users Users
|
||||||
OauthWhitelist string
|
OauthWhitelist string
|
||||||
SessionExpiry int
|
SessionExpiry int
|
||||||
Secret string
|
|
||||||
CookieSecure bool
|
CookieSecure bool
|
||||||
Domain string
|
Domain string
|
||||||
LoginTimeout int
|
LoginTimeout int
|
||||||
LoginMaxRetries int
|
LoginMaxRetries int
|
||||||
SessionCookieName string
|
SessionCookieName string
|
||||||
|
HMACSecret string
|
||||||
|
EncryptionSecret string
|
||||||
}
|
}
|
||||||
|
|
||||||
// HooksConfig is the configuration for the hooks service
|
// HooksConfig is the configuration for the hooks service
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@@ -11,6 +14,7 @@ import (
|
|||||||
"tinyauth/internal/types"
|
"tinyauth/internal/types"
|
||||||
|
|
||||||
"github.com/traefik/paerser/parser"
|
"github.com/traefik/paerser/parser"
|
||||||
|
"golang.org/x/crypto/hkdf"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
@@ -405,3 +409,32 @@ func FilterIP(filter string, ip string) (bool, error) {
|
|||||||
// If the filter is not a CIDR range or a single IP, return false
|
// If the filter is not a CIDR range or a single IP, return false
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DeriveKey(secret string, info string) (string, error) {
|
||||||
|
// Create hashing function
|
||||||
|
hash := sha256.New
|
||||||
|
|
||||||
|
// Create a new key using the secret and info
|
||||||
|
hkdf := hkdf.New(hash, []byte(secret), nil, []byte(info)) // I am not using a salt because I just want two different keys from one secret, maybe bad practice
|
||||||
|
|
||||||
|
// Create a new key
|
||||||
|
key := make([]byte, 24)
|
||||||
|
|
||||||
|
// Read the key from the HKDF
|
||||||
|
_, err := io.ReadFull(hkdf, key)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the key is not empty
|
||||||
|
if bytes.Equal(key, make([]byte, 24)) {
|
||||||
|
return "", errors.New("derived key is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode the key to base64
|
||||||
|
encodedKey := base64.StdEncoding.EncodeToString(key)
|
||||||
|
|
||||||
|
// Return the key as a base64 encoded string
|
||||||
|
return encodedKey, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user