Files
authentik/internal/outpost/proxyv2/application/session.go
Marc 'risson' Schmitt 2f70351c90 packages/client-go: init (#21139)
* packages/client-go: init

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>

* format

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* remove mod/sum

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix translate

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* no go replace

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* update rust makefile with pwd

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>

* fix build

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix docs

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* don't need a version ig?

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* exclude go client from cspell

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix main docker build

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2026-03-25 15:26:50 +01:00

144 lines
4.0 KiB
Go

package application
import (
"context"
"math"
"net/http"
"net/url"
"os"
"path"
"strings"
"github.com/gorilla/securecookie"
"github.com/gorilla/sessions"
"goauthentik.io/internal/outpost/proxyv2/codecs"
"goauthentik.io/internal/outpost/proxyv2/constants"
"goauthentik.io/internal/outpost/proxyv2/filesystemstore"
"goauthentik.io/internal/outpost/proxyv2/postgresstore"
"goauthentik.io/internal/outpost/proxyv2/types"
api "goauthentik.io/packages/client-go"
)
const PostgresKeyPrefix = "authentik_proxy_session_"
func (a *Application) getStore(p api.ProxyOutpostConfig, externalHost *url.URL) (sessions.Store, error) {
maxAge := 0
if p.AccessTokenValidity.IsSet() {
t := p.AccessTokenValidity.Get()
// Add one to the validity to ensure we don't have a session with indefinite length
maxAge = int(*t) + 1
}
sessionBackend := a.srv.SessionBackend()
switch sessionBackend {
case "postgres":
// New PostgreSQL store
ps, err := postgresstore.NewPostgresStore(a.log)
if err != nil {
return nil, err
}
ps.KeyPrefix(PostgresKeyPrefix)
ps.Options(sessions.Options{
HttpOnly: true,
Secure: strings.ToLower(externalHost.Scheme) == "https",
Domain: *p.CookieDomain,
SameSite: http.SameSiteLaxMode,
MaxAge: maxAge,
Path: "/",
})
return ps, nil
case "filesystem":
dir := os.TempDir()
cs, err := filesystemstore.GetPersistentStore(dir)
if err != nil {
return nil, err
}
cs.Codecs = codecs.CodecsFromPairs(maxAge, []byte(*p.CookieSecret))
// https://github.com/markbates/goth/commit/7276be0fdf719ddff753f3574ef0f967e4a5a5f7
// set the maxLength of the cookies stored on the disk to a larger number to prevent issues with:
// securecookie: the value is too long
// when using OpenID Connect, since this can contain a large amount of extra information in the id_token
// Note, when using the FilesystemStore only the session.ID is written to a browser cookie, so this is explicit for the storage on disk
cs.MaxLength(math.MaxInt)
cs.Options.HttpOnly = true
cs.Options.Secure = strings.ToLower(externalHost.Scheme) == "https"
cs.Options.Domain = *p.CookieDomain
cs.Options.SameSite = http.SameSiteLaxMode
cs.Options.MaxAge = maxAge
cs.Options.Path = "/"
return cs, nil
default:
a.log.WithField("backend", sessionBackend).Panic("unknown session backend type")
return nil, nil
}
}
func (a *Application) SessionName() string {
return a.sessionName
}
func (a *Application) getAllCodecs() []securecookie.Codec {
apps := a.srv.Apps()
cs := []securecookie.Codec{}
for _, app := range apps {
cs = append(cs, codecs.CodecsFromPairs(0, []byte(*app.proxyConfig.CookieSecret))...)
}
return cs
}
func (a *Application) Logout(ctx context.Context, filter func(c types.Claims) bool) error {
if _, ok := a.sessions.(*filesystemstore.Store); ok {
files, err := os.ReadDir(os.TempDir())
if err != nil {
return err
}
for _, file := range files {
s := sessions.Session{}
if !strings.HasPrefix(file.Name(), "session_") {
continue
}
fullPath := path.Join(os.TempDir(), file.Name())
data, err := os.ReadFile(fullPath)
if err != nil {
a.log.WithError(err).Warning("failed to read file")
continue
}
err = securecookie.DecodeMulti(
a.SessionName(), string(data),
&s.Values, a.getAllCodecs()...,
)
if err != nil {
a.log.WithError(err).Trace("failed to decode session")
continue
}
rc, ok := s.Values[constants.SessionClaims]
if !ok || rc == nil {
continue
}
claims := s.Values[constants.SessionClaims].(types.Claims)
if filter(claims) {
a.log.WithField("path", fullPath).Trace("deleting session")
err := os.Remove(fullPath)
if err != nil {
a.log.WithError(err).Warning("failed to delete session")
continue
}
}
}
}
if ps, ok := a.sessions.(*postgresstore.PostgresStore); ok {
err := ps.LogoutSessions(ctx, func(c types.Claims) bool {
return filter(types.Claims(c))
})
if err != nil {
a.log.WithError(err).Warning("failed to logout sessions from PostgreSQL")
return err
}
}
return nil
}