Skip to content

Commit

Permalink
Improve sever secret handling, improve cookie configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
giftkugel committed Aug 26, 2024
1 parent 4f6606b commit f56f1da
Show file tree
Hide file tree
Showing 15 changed files with 142 additions and 100 deletions.
30 changes: 17 additions & 13 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,22 @@ type TLS struct {
Keys Keys `yaml:"keys"`
}

type Cookies struct {
AuthName string `yaml:"authName"`
MessageName string `yaml:"messageName"`
}

type Server struct {
LogLevel string `yaml:"logLevel"`
Addr string `yaml:"addr"`
AuthCookieName string `yaml:"authCookieName"`
MessageCookieName string `yaml:"messageCookieName"`
Secret string `yaml:"secret"`
PrivateKey string `yaml:"privateKey"`
TLS TLS `yaml:"tls"`
LogoutRedirect string `yaml:"logoutRedirect"`
IntrospectScope string `yaml:"introspectScope"`
RevokeScope string `yaml:"revokeScopeScope"`
SessionTimeoutSeconds int `yaml:"sessionTimeoutSeconds"`
LogLevel string `yaml:"logLevel"`
Addr string `yaml:"addr"`
Cookies Cookies `yaml:"cookies"`
Secret string `yaml:"secret"`
PrivateKey string `yaml:"privateKey"`
TLS TLS `yaml:"tls"`
LogoutRedirect string `yaml:"logoutRedirect"`
IntrospectScope string `yaml:"introspectScope"`
RevokeScope string `yaml:"revokeScopeScope"`
SessionTimeoutSeconds int `yaml:"sessionTimeoutSeconds"`
}

type UserAddress struct {
Expand Down Expand Up @@ -246,11 +250,11 @@ func (config *Config) GetClient(name string) (*Client, bool) {
}

func (config *Config) GetAuthCookieName() string {
return GetOrDefaultString(config.Server.AuthCookieName, "stopnik_auth")
return GetOrDefaultString(config.Server.Cookies.AuthName, "stopnik_auth")
}

func (config *Config) GetMessageCookieName() string {
return GetOrDefaultString(config.Server.MessageCookieName, "stopnik_message")
return GetOrDefaultString(config.Server.Cookies.MessageName, "stopnik_message")
}

func (config *Config) GetSessionTimeoutSeconds() int {
Expand Down
6 changes: 4 additions & 2 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,10 @@ func simpleServerConfiguration(t *testing.T) {
origin := out.(*Config)
*origin = Config{
Server: Server{
Secret: "5XyLSgKpo5kWrJqm",
AuthCookieName: "my_auth",
Secret: "5XyLSgKpo5kWrJqm",
Cookies: Cookies{
AuthName: "my_auth",
},
IntrospectScope: "i:a",
RevokeScope: "r:b",
SessionTimeoutSeconds: 4200,
Expand Down
31 changes: 31 additions & 0 deletions internal/crypto/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import (
"encoding/pem"
"errors"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/webishdev/stopnik/internal/config"
"os"
)

Expand All @@ -14,6 +17,34 @@ type SigningPrivateKey struct {
SignatureAlgorithm jwa.SignatureAlgorithm
}

type ManagedKey struct {
Id string
Clients []*config.Client
Server bool
Key *jwk.Key
}

type ServerSecretLoader interface {
GetServerSecret() jwt.SignEncryptParseOption
}

type serverSecret struct {
secret string
}

type KeyLoader interface {
LoadKeys(client *config.Client) (*ManagedKey, bool)
ServerSecretLoader
}

func NewServerSecretLoader(config *config.Config) ServerSecretLoader {
return &serverSecret{secret: config.GetServerSecret()}
}

func (s *serverSecret) GetServerSecret() jwt.SignEncryptParseOption {
return jwt.WithKey(jwa.HS256, []byte(s.secret))
}

func LoadPrivateKey(name string) (*SigningPrivateKey, error) {
privateKeyBytes, readError := os.ReadFile(name)
if readError != nil {
Expand Down
19 changes: 13 additions & 6 deletions internal/http/cookie.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package http

import (
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/webishdev/stopnik/internal/config"
"github.com/webishdev/stopnik/internal/crypto"
"github.com/webishdev/stopnik/log"
"net/http"
"time"
Expand All @@ -12,16 +12,21 @@ import (
type Now func() time.Time

type CookieManager struct {
config *config.Config
now Now
config *config.Config
keyFallback crypto.ServerSecretLoader
now Now
}

func NewCookieManager(config *config.Config) *CookieManager {
return newCookieManagerWithTime(config, time.Now)
}

func newCookieManagerWithTime(config *config.Config, now Now) *CookieManager {
return &CookieManager{config: config, now: now}
return &CookieManager{
config: config,
keyFallback: crypto.NewServerSecretLoader(config),
now: now,
}
}

func (cookieManager *CookieManager) CreateMessageCookie(message string) http.Cookie {
Expand Down Expand Up @@ -87,7 +92,8 @@ func (cookieManager *CookieManager) ValidateAuthCookie(r *http.Request) (*config
}

func (cookieManager *CookieManager) validateCookieValue(cookie *http.Cookie) (*config.User, bool) {
token, err := jwt.Parse([]byte(cookie.Value), jwt.WithKey(jwa.HS256, []byte(cookieManager.config.GetServerSecret())))
options := cookieManager.keyFallback.GetServerSecret()
token, err := jwt.Parse([]byte(cookie.Value), options)
if err != nil {
return &config.User{}, false
}
Expand All @@ -108,7 +114,8 @@ func (cookieManager *CookieManager) generateCookieValue(username string) (string
return "", builderError
}

tokenString, tokenError := jwt.Sign(token, jwt.WithKey(jwa.HS256, []byte(cookieManager.config.GetServerSecret())))
options := cookieManager.keyFallback.GetServerSecret()
tokenString, tokenError := jwt.Sign(token, options)
if tokenError != nil {
return "", tokenError
}
Expand Down
8 changes: 4 additions & 4 deletions internal/server/handler/authorize/authorize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ func testAuthorizeValidLoginNoSession(t *testing.T, testConfig *config.Config, k
requestValidator := validation.NewRequestValidator(testConfig)
sessionManager := store.NewSessionManager(testConfig)
cookieManager := internalHttp.NewCookieManager(testConfig)
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManger))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManger))

authorizeHandler := NewAuthorizeHandler(requestValidator, cookieManager, sessionManager, tokenManager, &template.Manager{})

Expand Down Expand Up @@ -389,7 +389,7 @@ func testAuthorizeValidLoginAuthorizationGrant(t *testing.T, testConfig *config.
requestValidator := validation.NewRequestValidator(testConfig)
sessionManager := store.NewSessionManager(testConfig)
cookieManager := internalHttp.NewCookieManager(testConfig)
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManger))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManger))
sessionManager.StartSession(authSession)

authorizeHandler := NewAuthorizeHandler(requestValidator, cookieManager, sessionManager, tokenManager, &template.Manager{})
Expand Down Expand Up @@ -477,7 +477,7 @@ func testAuthorizeValidLoginImplicitGrant(t *testing.T, testConfig *config.Confi
requestValidator := validation.NewRequestValidator(testConfig)
sessionManager := store.NewSessionManager(testConfig)
cookieManager := internalHttp.NewCookieManager(testConfig)
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManger))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManger))
sessionManager.StartSession(authSession)

authorizeHandler := NewAuthorizeHandler(requestValidator, cookieManager, sessionManager, tokenManager, &template.Manager{})
Expand Down Expand Up @@ -586,7 +586,7 @@ func testAuthorizeImplicitGrant(t *testing.T, testConfig *config.Config, keyMang
requestValidator := validation.NewRequestValidator(testConfig)
sessionManager := store.NewSessionManager(testConfig)
cookieManager := internalHttp.NewCookieManager(testConfig)
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManger))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManger))

client, _ := testConfig.GetClient("foo")
user, _ := testConfig.GetUser("foo")
Expand Down
4 changes: 2 additions & 2 deletions internal/server/handler/health/health_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func Test_Health(t *testing.T) {
}

t.Run("Health without token", func(t *testing.T) {
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManger))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManger))

healthHandler := NewHealthHandler(tokenManager)

Expand All @@ -65,7 +65,7 @@ func Test_Health(t *testing.T) {
})

t.Run("Health with token", func(t *testing.T) {
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManger))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManger))

client, clientExists := testConfig.GetClient("foo")
if !clientExists {
Expand Down
14 changes: 7 additions & 7 deletions internal/server/handler/introspect/introspect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func Test_Introspect(t *testing.T) {
func testIntrospectMissingClientCredentials(t *testing.T, testConfig *config.Config, keyManger *store.KeyManger) {
t.Run("Missing client credentials", func(t *testing.T) {
requestValidator := validation.NewRequestValidator(testConfig)
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManger))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManger))

introspectHandler := NewIntrospectHandler(testConfig, requestValidator, tokenManager)

Expand All @@ -92,7 +92,7 @@ func testIntrospectMissingClientCredentials(t *testing.T, testConfig *config.Con
func testIntrospectInvalidClientCredentials(t *testing.T, testConfig *config.Config, keyManger *store.KeyManger) {
t.Run("Invalid client credentials", func(t *testing.T) {
requestValidator := validation.NewRequestValidator(testConfig)
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManger))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManger))

introspectHandler := NewIntrospectHandler(testConfig, requestValidator, tokenManager)

Expand Down Expand Up @@ -123,7 +123,7 @@ func testIntrospectEmptyToken(t *testing.T, testConfig *config.Config, keyManger
testMessage := fmt.Sprintf("Introspect empty %v", test.tokenHint)
t.Run(testMessage, func(t *testing.T) {
requestValidator := validation.NewRequestValidator(testConfig)
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManger))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManger))

introspectHandler := NewIntrospectHandler(testConfig, requestValidator, tokenManager)

Expand Down Expand Up @@ -169,7 +169,7 @@ func testIntrospectInvalidToken(t *testing.T, testConfig *config.Config, keyMang
testMessage := fmt.Sprintf("Introspect invalid %v", test.tokenHint)
t.Run(testMessage, func(t *testing.T) {
requestValidator := validation.NewRequestValidator(testConfig)
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManger))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManger))

introspectHandler := NewIntrospectHandler(testConfig, requestValidator, tokenManager)

Expand Down Expand Up @@ -234,7 +234,7 @@ func testIntrospect(t *testing.T, testConfig *config.Config, keyManger *store.Ke

requestValidator := validation.NewRequestValidator(testConfig)
sessionManager := store.NewSessionManager(testConfig)
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManger))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManger))
sessionManager.StartSession(authSession)
accessTokenResponse := tokenManager.CreateAccessTokenResponse(user.Username, client, scopes)

Expand Down Expand Up @@ -306,7 +306,7 @@ func testIntrospectWithoutHint(t *testing.T, testConfig *config.Config, keyMange

requestValidator := validation.NewRequestValidator(testConfig)
sessionManager := store.NewSessionManager(testConfig)
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManger))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManger))
sessionManager.StartSession(authSession)
accessTokenResponse := tokenManager.CreateAccessTokenResponse(user.Username, client, scopes)

Expand Down Expand Up @@ -377,7 +377,7 @@ func testIntrospectDisabled(t *testing.T, testConfig *config.Config, keyManger *

requestValidator := validation.NewRequestValidator(testConfig)
sessionManager := store.NewSessionManager(testConfig)
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManger))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManger))
sessionManager.StartSession(authSession)
accessTokenResponse := tokenManager.CreateAccessTokenResponse(user.Username, client, scopes)

Expand Down
3 changes: 2 additions & 1 deletion internal/server/handler/keys/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package keys
import (
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/webishdev/stopnik/internal/config"
"github.com/webishdev/stopnik/internal/crypto"
http2 "github.com/webishdev/stopnik/internal/http"
errorHandler "github.com/webishdev/stopnik/internal/server/handler/error"
"github.com/webishdev/stopnik/internal/store"
Expand Down Expand Up @@ -59,7 +60,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}

func (h *Handler) addKey(mangedKey *store.ManagedKey) error {
func (h *Handler) addKey(mangedKey *crypto.ManagedKey) error {
key := *mangedKey.Key

addKeyError := h.keySet.AddKey(key)
Expand Down
1 change: 1 addition & 0 deletions internal/server/handler/metadata/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
jwa.ES256,
jwa.ES384,
jwa.ES512,
jwa.HS256,
}

metadataResponse := &response{
Expand Down
14 changes: 7 additions & 7 deletions internal/server/handler/revoke/revoke_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func Test_Revoke(t *testing.T) {
func testRevokeMissingClientCredentials(t *testing.T, testConfig *config.Config, keyManager *store.KeyManger) {
t.Run("Missing client credentials", func(t *testing.T) {
requestValidator := validation.NewRequestValidator(testConfig)
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManager))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManager))

revokeHandler := NewRevokeHandler(testConfig, requestValidator, tokenManager)

Expand All @@ -90,7 +90,7 @@ func testRevokeMissingClientCredentials(t *testing.T, testConfig *config.Config,
func testRevokeInvalidClientCredentials(t *testing.T, testConfig *config.Config, keyManager *store.KeyManger) {
t.Run("Invalid client credentials", func(t *testing.T) {
requestValidator := validation.NewRequestValidator(testConfig)
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManager))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManager))

revokeHandler := NewRevokeHandler(testConfig, requestValidator, tokenManager)

Expand Down Expand Up @@ -121,7 +121,7 @@ func testRevokeEmptyToken(t *testing.T, testConfig *config.Config, keyManager *s
testMessage := fmt.Sprintf("Revoke empty %v", test.tokenHint)
t.Run(testMessage, func(t *testing.T) {
requestValidator := validation.NewRequestValidator(testConfig)
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManager))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManager))

revokeHandler := NewRevokeHandler(testConfig, requestValidator, tokenManager)

Expand Down Expand Up @@ -160,7 +160,7 @@ func testRevokeInvalidToken(t *testing.T, testConfig *config.Config, keyManager
testMessage := fmt.Sprintf("Revoke invalid %v", test.tokenHint)
t.Run(testMessage, func(t *testing.T) {
requestValidator := validation.NewRequestValidator(testConfig)
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManager))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManager))

revokeHandler := NewRevokeHandler(testConfig, requestValidator, tokenManager)

Expand Down Expand Up @@ -217,7 +217,7 @@ func testRevoke(t *testing.T, testConfig *config.Config, keyManager *store.KeyMa

requestValidator := validation.NewRequestValidator(testConfig)
sessionManager := store.NewSessionManager(testConfig)
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManager))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManager))
sessionManager.StartSession(authSession)
accessTokenResponse := tokenManager.CreateAccessTokenResponse(user.Username, client, scopes)

Expand Down Expand Up @@ -294,7 +294,7 @@ func testRevokeWithoutHint(t *testing.T, testConfig *config.Config, keyManager *

requestValidator := validation.NewRequestValidator(testConfig)
sessionManager := store.NewSessionManager(testConfig)
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManager))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManager))
sessionManager.StartSession(authSession)
accessTokenResponse := tokenManager.CreateAccessTokenResponse(user.Username, client, scopes)

Expand Down Expand Up @@ -370,7 +370,7 @@ func testRevokeDisabled(t *testing.T, testConfig *config.Config, keyManager *sto

requestValidator := validation.NewRequestValidator(testConfig)
sessionManager := store.NewSessionManager(testConfig)
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(keyManager))
tokenManager := store.NewTokenManager(testConfig, store.NewDefaultKeyLoader(testConfig, keyManager))
sessionManager.StartSession(authSession)
accessTokenResponse := tokenManager.CreateAccessTokenResponse(user.Username, client, scopes)

Expand Down
Loading

0 comments on commit f56f1da

Please sign in to comment.