Files
authentik/internal/outpost/proxyv2/application/auth.go
Dominic R 3353db0d7f outpost/proxyv2: more tests, fix pg password with spaces, and existing session on restart (#18211)
* outpost/proxyv2: handle PostgreSQL passwords with spaces and special characters

And modify / add some more tests and a bit of refactoring

* Potential fix for code scanning alert no. 268: Disabled TLS certificate check

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Signed-off-by: Dominic R <dominic@sdko.org>

* Revert "Potential fix for code scanning alert no. 268: Disabled TLS certificate check"

This reverts commit ead227a272.

* wip

* fix incorrect status code in error response

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

---------

Signed-off-by: Dominic R <dominic@sdko.org>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-12-11 14:25:41 +00:00

121 lines
3.1 KiB
Go

package application
import (
"fmt"
"net/http"
"time"
"github.com/mitchellh/mapstructure"
"goauthentik.io/internal/outpost/proxyv2/constants"
"goauthentik.io/internal/outpost/proxyv2/types"
)
// checkAuth Get claims which are currently in session
// Returns an error if the session can't be loaded or the claims can't be parsed/type-cast
func (a *Application) checkAuth(rw http.ResponseWriter, r *http.Request) (*types.Claims, error) {
c := a.getClaimsFromSession(rw, r)
if c != nil {
return c, nil
}
if rw == nil {
return nil, fmt.Errorf("no response writer")
}
// Check TTL cache
c = a.getClaimsFromCache(r)
if c != nil {
return c, nil
}
// Check bearer token if set
bearer := a.checkAuthHeaderBearer(r)
if bearer != "" {
a.log.Trace("checking bearer token")
tc := a.attemptBearerAuth(bearer)
if tc != nil {
return a.saveAndCacheClaims(rw, r, tc.Claims)
}
a.log.Trace("no/invalid bearer token")
}
// Check basic auth if set
username, password, basicSet := r.BasicAuth()
if basicSet {
a.log.Trace("checking basic auth")
tc := a.attemptBasicAuth(username, password)
if tc != nil {
return a.saveAndCacheClaims(rw, r, *tc)
}
a.log.Trace("no/invalid basic auth")
}
return nil, fmt.Errorf("failed to get claims from session")
}
func (a *Application) getClaimsFromSession(rw http.ResponseWriter, r *http.Request) *types.Claims {
s, err := a.sessions.Get(r, a.SessionName())
if err != nil {
// err == user has no session/session is not valid
// Delete the stale session cookie if it exists
if rw != nil {
s.Options.MaxAge = -1
if saveErr := s.Save(r, rw); saveErr != nil {
a.log.WithError(saveErr).Warning("failed to delete stale session cookie")
}
}
return nil
}
claims, ok := s.Values[constants.SessionClaims]
if claims == nil || !ok {
// no claims saved, reject
return nil
}
// Claims are always stored as types.Claims but may be deserialized differently:
// - Filesystem store (gob): preserves struct type as types.Claims
// - PostgreSQL store (JSON): deserializes as map[string]any
// Handle struct type (filesystem store)
if c, ok := claims.(types.Claims); ok {
return &c
}
// Handle map type (PostgreSQL store)
if claimsMap, ok := claims.(map[string]any); ok {
var c types.Claims
if err := mapstructure.Decode(claimsMap, &c); err != nil {
return nil
}
return &c
}
return nil
}
func (a *Application) getClaimsFromCache(r *http.Request) *types.Claims {
key := r.Header.Get(constants.HeaderAuthorization)
item := a.authHeaderCache.Get(key)
if item != nil && !item.IsExpired() {
v := item.Value()
return &v
}
return nil
}
func (a *Application) saveAndCacheClaims(rw http.ResponseWriter, r *http.Request, claims types.Claims) (*types.Claims, error) {
s, _ := a.sessions.Get(r, a.SessionName())
s.Values[constants.SessionClaims] = claims
err := s.Save(r, rw)
if err != nil {
return nil, err
}
key := r.Header.Get(constants.HeaderAuthorization)
item := a.authHeaderCache.Get(key)
// Don't set when the key is already found
if item == nil {
a.authHeaderCache.Set(key, claims, time.Second*60)
}
r.Header.Del(constants.HeaderAuthorization)
return &claims, nil
}