diff --git a/authz/authz.go b/authz/authz.go index c65676f..b68835b 100644 --- a/authz/authz.go +++ b/authz/authz.go @@ -40,7 +40,7 @@ type Service struct { authClient authv3connect.AuthorizationClient } -func NewService(cfg *Config, opaURL, secretKey string) *Service { +func NewService(cfg *Config, opaURL, secretKey string, redisAddrs []string) *Service { var c authv3connect.AuthorizationClient if opaURL != "" { u, err := url.Parse(opaURL) @@ -67,7 +67,7 @@ func NewService(cfg *Config, opaURL, secretKey string) *Service { return &Service{ cfg: cfg, authClient: c, - store: store.NewStore(), + store: store.NewStore(redisAddrs), secretKey: []byte(secretKey), } } @@ -136,8 +136,7 @@ func (s *Service) Check(ctx context.Context, req *connect.Request[auth.CheckRequ func (s *Service) authProcess(ctx context.Context, req *auth.AttributeContext_HttpRequest, provider *OIDCProvider) (*auth.CheckResponse, error) { var headers []*core.HeaderValueOption - var sessionData *pb.SessionData - sessionCookieName := provider.CookieNamePrefix + "-" + ServiceName + var sessionCookieName = provider.CookieNamePrefix + "-" + ServiceName requestedURL := req.GetScheme() + "://" + req.GetHost() + req.GetPath() slog.Debug("client request url", slog.String("url", requestedURL)) @@ -154,7 +153,7 @@ func (s *Service) authProcess(ctx context.Context, req *auth.AttributeContext_Ht return s.authResponse(false, envoy_type.StatusCode_Found, headers, nil, "redirect to Idp"), nil } - slog.Debug("session data found in cookie", slog.String("session_id", sessionId), slog.String("req_url", requestedURL)) + slog.Debug("session data found in cookie", slog.String("session_id", sessionId), slog.String("url", requestedURL)) // If the request is for the callback URI, then we need to exchange the code for tokens if strings.HasPrefix(requestedURL, provider.CallbackURI+"?") && sessionData.AccessToken == "" { @@ -168,7 +167,7 @@ func (s *Service) authProcess(ctx context.Context, req *auth.AttributeContext_Ht return s.authResponse(false, envoy_type.StatusCode_Found, headers, nil, "redirect to requested url"), nil } - err := s.validateTokens(ctx, provider, sessionData, sessionCookieName, sessionId) + sessionData, err := s.validateTokens(ctx, provider, sessionData, sessionCookieName, sessionId) if err != nil { slog.Warn("couldn't validating tokens", slog.String("err", err.Error())) headers, err := s.newSession(ctx, requestedURL, sessionCookieName, provider) @@ -188,23 +187,22 @@ func (s *Service) retriveTokens(ctx context.Context, provider *OIDCProvider, ses if err != nil { return err } - tokens, err := provider.p.RetriveTokens(ctx, code, sessionData.CodeVerifier) + t, err := provider.p.RetriveTokens(ctx, code, sessionData.CodeVerifier) if err != nil { return err } // Copy the tokens into the session data - sessionData.RefreshToken = tokens.RefreshToken - sessionData.AccessToken = tokens.AccessToken - sessionData.IdToken = tokens.IDToken + sessionData.RefreshToken = t.RefreshToken + sessionData.AccessToken = t.AccessToken + sessionData.IdToken = t.IDToken - enc, err := session.EncodeToken(ctx, [32]byte(s.secretKey), sessionData) + slog.Debug("Token retrieved, updating session", slog.Any("expire", t.IDTokenClaims.GetExpiration())) + enc, err := session.EncryptSession(ctx, [32]byte(s.secretKey), sessionData) if err != nil { slog.Error("error encrypting session data", slog.String("err", err.Error())) return err } - - // store session data in cache if err := s.store.Set(ctx, sessionId, enc); err != nil { return err } @@ -213,40 +211,39 @@ func (s *Service) retriveTokens(ctx context.Context, provider *OIDCProvider, ses } // Validates and poteintially refreshes the token -func (s *Service) validateTokens(ctx context.Context, provider *OIDCProvider, d *pb.SessionData, sessionCookieName, sessionId string) error { - expired, err := provider.p.VerifyTokens(ctx, d.AccessToken, d.IdToken) +func (s *Service) validateTokens(ctx context.Context, provider *OIDCProvider, sessionData *pb.SessionData, sessionCookieName, sessionId string) (*pb.SessionData, error) { + expired, err := provider.p.VerifyTokens(ctx, sessionData.AccessToken, sessionData.IdToken) if err != nil { - return err + return nil, err } if !expired { - return nil + return sessionData, nil } - slog.Debug("Token expired, refreshing token...") - if expired && d.RefreshToken == "" { - return errors.New("token expired and no refresh token found, add scope=offline_access to the auth request to get a refresh token") + if expired && sessionData.RefreshToken == "" { + return nil, errors.New("token expired and no refresh token found, add scope=offline_access to the auth request to get a refresh token") } - t, err := provider.p.RefreshTokens(ctx, d.RefreshToken, d.AccessToken) + t, err := provider.p.RefreshTokens(ctx, sessionData.RefreshToken, sessionData.AccessToken) if err != nil { - return err + return nil, err } - d.RefreshToken = t.RefreshToken - d.AccessToken = t.AccessToken - d.IdToken = t.IDToken + // Update the session data with the new tokens + sessionData.RefreshToken = t.RefreshToken + sessionData.AccessToken = t.AccessToken + sessionData.IdToken = t.IDToken - slog.Debug("Token refreshed, updating session") - enc, err := session.EncodeToken(ctx, [32]byte(s.secretKey), d) + slog.Debug("Token refreshed, updating session", slog.Any("expire", t.IDTokenClaims.GetExpiration())) + enc, err := session.EncryptSession(ctx, [32]byte(s.secretKey), sessionData) if err != nil { slog.Error("error encrypting session data", slog.String("err", err.Error())) - return err + return nil, err } - if err := s.store.Set(ctx, sessionId, enc); err != nil { - return err + return nil, err } - return nil + return sessionData, nil } func (s *Service) newSession(ctx context.Context, requestedURL, sessionCookieName string, provider *OIDCProvider) ([]*core.HeaderValueOption, error) { @@ -258,15 +255,13 @@ func (s *Service) newSession(ctx context.Context, requestedURL, sessionCookieNam if err != nil { return nil, err } - slog.Debug("setting requested url", slog.String("requested_url", requestedURL)) + slog.Debug("setting requested url", slog.String("url", requestedURL)) sessionData.RequestedUrl = requestedURL - enc, err := session.EncodeToken(ctx, [32]byte(s.secretKey), sessionData) + enc, err := session.EncryptSession(ctx, [32]byte(s.secretKey), sessionData) if err != nil { return nil, err } - - // store session data in cache if err := s.store.Set(ctx, sessionCookieToken, enc); err != nil { return nil, err } @@ -282,6 +277,7 @@ func (s *Service) newSession(ctx context.Context, requestedURL, sessionCookieNam Secure: provider.SecureCookie, SameSite: http.SameSiteLaxMode, } + return append(headers, s.setCookie(cookie)...), nil } @@ -304,14 +300,13 @@ func (s *Service) getSessionCookieData(ctx context.Context, req *auth.AttributeC return nil, "" } - slog.Debug("getting session data from session store", slog.String("session_id", sessionId)) - d, err := s.store.Cache.Get(ctx, sessionId) + enc, err := s.store.Get(ctx, sessionId) if err != nil { - slog.Error("error getting session data from cache", slog.String("err", err.Error())) + slog.Warn("error getting session data from cache", slog.String("err", err.Error())) return nil, "" } - sessionData, err = session.DecodeToken(ctx, [32]byte(s.secretKey), d) + sessionData, err = session.DecryptSession(ctx, [32]byte(s.secretKey), enc) if err != nil { slog.Error("error decrypt session data", slog.String("err", err.Error())) return nil, "" diff --git a/authz/authz_test.go b/authz/authz_test.go index 4dba791..69b366c 100644 --- a/authz/authz_test.go +++ b/authz/authz_test.go @@ -54,7 +54,7 @@ func TestCheckServiceAuthFlow(t *testing.T) { require.NoError(t, err, "init cfg should not have failed") secretKey := []byte("G_TdvPJ9T8C4p&A?Wr3YAUYW$*9vn4?t") - authz := Service{cfg: testCfg, store: store.NewStore(), secretKey: secretKey} + authz := Service{cfg: testCfg, store: store.NewStore(nil), secretKey: secretKey} //1. Check Authorization response without callback and no cookie req. initialRequestedURL := "http://foo.bar/" diff --git a/go.mod b/go.mod index da348f5..97e1407 100644 --- a/go.mod +++ b/go.mod @@ -10,10 +10,10 @@ require ( connectrpc.com/grpcreflect v1.2.0 connectrpc.com/otelconnect v0.6.0 github.com/eko/gocache/lib/v4 v4.1.5 + github.com/eko/gocache/store/redis/v4 v4.2.1 github.com/gogo/googleapis v1.4.1 github.com/google/uuid v1.5.0 github.com/grokify/go-pkce v0.2.3 - github.com/peterbourgon/ff/v3 v3.4.0 github.com/stretchr/testify v1.8.4 github.com/zitadel/oidc/v3 v3.8.1 go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1 @@ -31,6 +31,7 @@ require ( require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/golang/mock v1.6.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/prometheus/client_golang v1.14.0 // indirect @@ -42,10 +43,9 @@ require ( require ( buf.build/gen/go/cncf/xds/protocolbuffers/go v1.32.0-20231212190141-23263dcfaa96.1 // indirect buf.build/gen/go/envoyproxy/protoc-gen-validate/protocolbuffers/go v1.32.0-20231130202533-71881f09a0c5.1 // indirect - github.com/allegro/bigcache v1.2.1 github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/eko/gocache/store/bigcache/v4 v4.2.1 + github.com/eko/gocache/store/go_cache/v4 v4.2.1 github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-jose/go-jose/v3 v3.0.1 // indirect github.com/go-logr/logr v1.3.0 // indirect @@ -57,7 +57,10 @@ require ( github.com/lmittmann/tint v1.0.3 github.com/mattn/go-isatty v0.0.20 github.com/muhlemmer/gu v0.3.1 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible + github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/redis/go-redis/v9 v9.4.0 github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/zitadel/logging v0.5.0 // indirect diff --git a/go.sum b/go.sum index a244a8b..a6696dd 100644 --- a/go.sum +++ b/go.sum @@ -58,14 +58,14 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= -github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk= -github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -81,10 +81,14 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/eko/gocache/lib/v4 v4.1.5 h1:CeMQmdIzwBKKLRjk3FCDXzNFsQTyqJ01JLI7Ib0C9r8= github.com/eko/gocache/lib/v4 v4.1.5/go.mod h1:XaNfCwW8KYW1bRZ/KoHA1TugnnkMz0/gT51NDIu7LSY= -github.com/eko/gocache/store/bigcache/v4 v4.2.1 h1:xf9R5HZqmrfT4+NzlJPQJQUWftfWW06FHbjz4IEjE08= -github.com/eko/gocache/store/bigcache/v4 v4.2.1/go.mod h1:Q9+hxUE+XUVGSRGP1tqW8sPHcZ50PfyBVh9VKh0OjrA= +github.com/eko/gocache/store/go_cache/v4 v4.2.1 h1:3xSksOamzCf+YZXz9l67gr6jOj3AA4hnk0mV4z3Jwbs= +github.com/eko/gocache/store/go_cache/v4 v4.2.1/go.mod h1:rd6tUbaBOdqgi5xT3+OGeU7lQuhVJGTapNWFlZou524= +github.com/eko/gocache/store/redis/v4 v4.2.1 h1:uPAgZIn7knH6a55tO4ETN9V93VD3Rcyx0ZIyozEqC0I= +github.com/eko/gocache/store/redis/v4 v4.2.1/go.mod h1:JoLkNA5yeGNQUwINAM9529cDNQCo88WwiKlO9e/+39I= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -229,8 +233,12 @@ github.com/muhlemmer/httpforwarded v0.1.0 h1:x4DLrzXdliq8mprgUMR0olDvHGkou5BJsK/ github.com/muhlemmer/httpforwarded v0.1.0/go.mod h1:yo9czKedo2pdZhoXe+yDkGVbU0TJ0q9oQ90BVoDEtw0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc= -github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= +github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 h1:aiqS8aBlF9PsAKeMddMSfbwp3smONCn3UO8QfUg0Z7Y= +github.com/peterbourgon/ff/v4 v4.0.0-alpha.4/go.mod h1:H/13DK46DKXy7EaIxPhk2Y0EC8aubKm35nBjBe8AAGc= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -262,6 +270,8 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/redis/go-redis/v9 v9.4.0 h1:Yzoz33UZw9I/mFhx4MNrB6Fk+XHO1VukNcCa1+lwyKk= +github.com/redis/go-redis/v9 v9.4.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= diff --git a/main.go b/main.go index 8f7ab73..851a130 100644 --- a/main.go +++ b/main.go @@ -1,14 +1,13 @@ package main import ( - "flag" "log/slog" "os" "os/signal" "syscall" - "github.com/peterbourgon/ff/v3" - "github.com/peterbourgon/ff/v3/ffyaml" + "github.com/peterbourgon/ff/v4" + "github.com/peterbourgon/ff/v4/ffyaml" "github.com/shelmangroup/envoy-oidc-authserver/authz" "github.com/shelmangroup/envoy-oidc-authserver/logging" @@ -17,19 +16,22 @@ import ( ) func main() { - fs := flag.NewFlagSet("envoy-oidc-authserver", flag.ContinueOnError) - addr := fs.String("listen-addr", ":8080", "address to listen on") - otlpAddr := fs.String("otlp-addr", ":4317", "address to send OTLP traces to") - opaURL := fs.String("opa-url", "", "base url to send OPA requests to") - secretKey := fs.String("secret-key", "", "secret key used to encrypt JWT tokens") - providersConfig := fs.String("providers-config", "", "oidc config file") - logJson := fs.Bool("log-json", false, "log in JSON format") - logLevel := fs.String("log-level", "info", "log level (debug, info, warn, error)") + + fs := ff.NewFlagSet("envoy-oidc-authserver") + addr := fs.String('s', "listen-addr", ":8080", "address to listen on") + otlpAddr := fs.StringLong("otlp-addr", ":4317", "address to send OTLP traces to") + redisAddrs := fs.StringSet('r', "redis-addrs", "Sentinel addresses to use for Redis cache") + opaURL := fs.StringLong("opa-url", "", "base url to send OPA requests to") + secretKey := fs.StringLong("secret-key", "", "secret key used to encrypt JWT tokens") + providersConfig := fs.String('c', "providers-config", "", "oidc config file") + logJson := fs.BoolLong("log-json", "log in JSON format") + logLevel := fs.StringLong("log-level", "info", "log level (debug, info, warn, error)") err := ff.Parse(fs, os.Args[1:], ff.WithEnvVarPrefix("ENVOY_AUTHZ"), + ff.WithEnvVarSplit(","), ff.WithConfigFileFlag("config"), - ff.WithConfigFileParser(ffyaml.Parser), + ff.WithConfigFileParser(ffyaml.Parse), ) if err != nil { slog.Error("Configuration error", err) @@ -38,7 +40,7 @@ func main() { // check secret key to be 32 bytes if len(*secretKey) != 32 { - slog.Error("Secret key must be 32 bytes") + slog.Error("Secret key must be 32 bytes long", slog.Int("current_len", len(*secretKey))) os.Exit(1) } @@ -63,7 +65,7 @@ func main() { } // Create new server - s := server.NewServer(*addr, authz.NewService(c, *opaURL, *secretKey)) + s := server.NewServer(*addr, authz.NewService(c, *opaURL, *secretKey, *redisAddrs)) defer func() { err := s.Shutdown() if err != nil { diff --git a/oidc/oidc.go b/oidc/oidc.go index f5bcac1..e948022 100644 --- a/oidc/oidc.go +++ b/oidc/oidc.go @@ -74,7 +74,7 @@ func (o *OIDCProvider) IdpAuthURL(codeChallenge string) string { func (o *OIDCProvider) VerifyTokens(ctx context.Context, accessToken, idToken string) (bool, error) { var expired bool - t, err := rp.VerifyTokens[*oidc.IDTokenClaims](ctx, accessToken, idToken, o.provider.IDTokenVerifier()) + _, err := rp.VerifyTokens[*oidc.IDTokenClaims](ctx, accessToken, idToken, o.provider.IDTokenVerifier()) if err != nil { if err == oidc.ErrExpired { expired = true @@ -82,7 +82,6 @@ func (o *OIDCProvider) VerifyTokens(ctx context.Context, accessToken, idToken st return false, err } } - slog.Debug("tokens verified", slog.String("expire", t.GetExpiration().String())) return expired, nil } diff --git a/oidc/oidc_mock.go b/oidc/oidc_mock.go index 9140ebf..4ed3dce 100644 --- a/oidc/oidc_mock.go +++ b/oidc/oidc_mock.go @@ -31,7 +31,8 @@ func (o *OIDCMockProvider) RetriveTokens(ctx context.Context, code, codeVerifier RefreshToken: "bar", Expiry: time.Now().Add(1 * time.Hour), }, - IDToken: "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTA2MTI5MDIyfQ", + IDToken: "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTA2MTI5MDIyfQ", + IDTokenClaims: &oidc.IDTokenClaims{}, }, nil } @@ -48,6 +49,7 @@ func (o *OIDCMockProvider) RefreshTokens(ctx context.Context, refreshToken, clie RefreshToken: "bar", Expiry: time.Now().Add(2 * time.Hour), }, - IDToken: "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaW1111111111111111asda", + IDToken: "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaW1111111111111111asda", + IDTokenClaims: &oidc.IDTokenClaims{}, }, nil } diff --git a/run/config/dex.yaml b/run/config/dex.yaml index f6e1ec3..efc65c5 100644 --- a/run/config/dex.yaml +++ b/run/config/dex.yaml @@ -29,9 +29,9 @@ connectors: # Uncomment this block to enable configuration for the expiration time durations. # Is possible to specify units using only s, m and h suffixes. expiry: - deviceRequests: "5m" + deviceRequests: "1m" signingKeys: "6h" - idTokens: "5m" + idTokens: "1m" refreshTokens: reuseInterval: "3s" validIfNotUsedFor: "360h" # 15 days diff --git a/session/encrypt.go b/session/encrypt.go index 9778dc1..1749ceb 100644 --- a/session/encrypt.go +++ b/session/encrypt.go @@ -12,10 +12,10 @@ import ( ) var ( - errInvalidToken = errors.New("invalid token") + errInvalid = errors.New("invalid encrypted data") ) -func EncodeToken(ctx context.Context, key [32]byte, sessionData *pb.SessionData) ([]byte, error) { +func EncryptSession(ctx context.Context, key [32]byte, sessionData *pb.SessionData) ([]byte, error) { message, err := proto.Marshal(sessionData) if err != nil { return nil, err @@ -32,16 +32,16 @@ func EncodeToken(ctx context.Context, key [32]byte, sessionData *pb.SessionData) return box, nil } -func DecodeToken(ctx context.Context, key [32]byte, box []byte) (*pb.SessionData, error) { +func DecryptSession(ctx context.Context, key [32]byte, box []byte) (*pb.SessionData, error) { if len(box) < 24 { - return nil, errInvalidToken + return nil, errInvalid } var nonce [24]byte copy(nonce[:], box[:24]) message, ok := secretbox.Open(nil, box[24:], &nonce, &key) if !ok { - return nil, errInvalidToken + return nil, errInvalid } sessionData := &pb.SessionData{} diff --git a/store/store.go b/store/store.go index f28957a..fda410f 100644 --- a/store/store.go +++ b/store/store.go @@ -1,23 +1,45 @@ package store import ( + "log/slog" "time" - "github.com/allegro/bigcache" "github.com/eko/gocache/lib/v4/cache" - bigcache_store "github.com/eko/gocache/store/bigcache/v4" + "github.com/eko/gocache/lib/v4/store" + gocache_store "github.com/eko/gocache/store/go_cache/v4" + redis_store "github.com/eko/gocache/store/redis/v4" + gocache "github.com/patrickmn/go-cache" + redis "github.com/redis/go-redis/v9" ) type Store struct { - *cache.Cache[[]byte] + *cache.ChainCache[[]byte] } -func NewStore() *Store { - bigcacheClient, _ := bigcache.NewBigCache(bigcache.DefaultConfig(24 * time.Hour)) - bigcacheStore := bigcache_store.NewBigcache(bigcacheClient) - // TODO: chain the cache with a redis cache - c := cache.New[[]byte](bigcacheStore) +func NewStore(redisAddrs []string) *Store { + gocacheClient := gocache.New(1*time.Hour, 10*time.Minute) + gocacheStore := gocache_store.NewGoCache(gocacheClient) + + if redisAddrs == nil { + return &Store{ + cache.NewChain[[]byte]( + cache.New[[]byte](gocacheStore), + ), + } + } + + slog.Info("Using Redis cache", slog.Any("addrs", redisAddrs), slog.Int("len", len(redisAddrs))) + + redisClient := redis.NewFailoverClient(&redis.FailoverOptions{ + MasterName: "mymaster", + SentinelAddrs: redisAddrs, + }) + redisStore := redis_store.NewRedis(redisClient, store.WithExpiration(24*time.Hour)) + return &Store{ - c, + cache.NewChain[[]byte]( + cache.New[[]byte](gocacheStore), + cache.New[[]byte](redisStore), + ), } }