"""authentik core tasks"""
from datetime import datetime, timedelta

from django.contrib.sessions.backends.cache import KEY_PREFIX
from django.core.cache import cache
from django.utils.timezone import now
from structlog.stdlib import get_logger

from authentik.core.models import (
    USER_ATTRIBUTE_EXPIRES,
    USER_ATTRIBUTE_GENERATED,
    AuthenticatedSession,
    ExpiringModel,
    User,
)
from authentik.events.monitored_tasks import (
    MonitoredTask,
    TaskResult,
    TaskResultStatus,
    prefill_task,
)
from authentik.root.celery import CELERY_APP

LOGGER = get_logger()


@CELERY_APP.task(bind=True, base=MonitoredTask)
@prefill_task
def clean_expired_models(self: MonitoredTask):
    """Remove expired objects"""
    messages = []
    for cls in ExpiringModel.__subclasses__():
        cls: ExpiringModel
        objects = (
            cls.objects.all().exclude(expiring=False).exclude(expiring=True, expires__gt=now())
        )
        amount = objects.count()
        for obj in objects:
            obj.expire_action()
        LOGGER.debug("Expired models", model=cls, amount=amount)
        messages.append(f"Expired {amount} {cls._meta.verbose_name_plural}")
    # Special case
    amount = 0
    for session in AuthenticatedSession.objects.all():
        cache_key = f"{KEY_PREFIX}{session.session_key}"
        try:
            value = cache.get(cache_key)
        # pylint: disable=broad-except
        except Exception as exc:
            LOGGER.debug("Failed to get session from cache", exc=exc)
        if not value:
            session.delete()
            amount += 1
    LOGGER.debug("Expired sessions", model=AuthenticatedSession, amount=amount)
    messages.append(f"Expired {amount} {AuthenticatedSession._meta.verbose_name_plural}")
    self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, messages))


@CELERY_APP.task(bind=True, base=MonitoredTask)
@prefill_task
def clean_temporary_users(self: MonitoredTask):
    """Remove temporary users created by SAML Sources"""
    _now = datetime.now()
    messages = []
    deleted_users = 0
    for user in User.objects.filter(**{f"attributes__{USER_ATTRIBUTE_GENERATED}": True}):
        if not user.attributes.get(USER_ATTRIBUTE_EXPIRES):
            continue
        delta: timedelta = _now - datetime.fromtimestamp(
            user.attributes.get(USER_ATTRIBUTE_EXPIRES)
        )
        if delta.total_seconds() > 0:
            LOGGER.debug("User is expired and will be deleted.", user=user, delta=delta)
            user.delete()
            deleted_users += 1
    messages.append(f"Successfully deleted {deleted_users} users.")
    self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, messages))

authentik/core/tasks.py
"""authentik core tasks"""
from datetime import datetime, timedelta

from django.contrib.sessions.backends.cache import KEY_PREFIX
from django.core.cache import cache
from django.utils.timezone import now
from structlog.stdlib import get_logger

from authentik.core.models import (
    USER_ATTRIBUTE_EXPIRES,
    USER_ATTRIBUTE_GENERATED,
    AuthenticatedSession,
    ExpiringModel,
    User,
)
from authentik.events.monitored_tasks import (
    MonitoredTask,
    TaskResult,
    TaskResultStatus,
    prefill_task,
)
from authentik.root.celery import CELERY_APP

LOGGER = get_logger()


@CELERY_APP.task(bind=True, base=MonitoredTask)
@prefill_task
def clean_expired_models(self: MonitoredTask):
    """Remove expired objects"""
    messages = []
    for cls in ExpiringModel.__subclasses__():
        cls: ExpiringModel
        objects = (
            cls.objects.all().exclude(expiring=False).exclude(expiring=True, expires__gt=now())
        )
        amount = objects.count()
        for obj in objects:
            obj.expire_action()
        LOGGER.debug("Expired models", model=cls, amount=amount)
        messages.append(f"Expired {amount} {cls._meta.verbose_name_plural}")
    # Special case
    amount = 0
    for session in AuthenticatedSession.objects.all():
        cache_key = f"{KEY_PREFIX}{session.session_key}"
        value = None
        try:
            value = cache.get(cache_key)
        # pylint: disable=broad-except
        except Exception as exc:
            LOGGER.debug("Failed to get session from cache", exc=exc)
        if not value:
            session.delete()
            amount += 1
    LOGGER.debug("Expired sessions", model=AuthenticatedSession, amount=amount)
    messages.append(f"Expired {amount} {AuthenticatedSession._meta.verbose_name_plural}")
    self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, messages))


@CELERY_APP.task(bind=True, base=MonitoredTask)
@prefill_task
def clean_temporary_users(self: MonitoredTask):
    """Remove temporary users created by SAML Sources"""
    _now = datetime.now()
    messages = []
    deleted_users = 0
    for user in User.objects.filter(**{f"attributes__{USER_ATTRIBUTE_GENERATED}": True}):
        if not user.attributes.get(USER_ATTRIBUTE_EXPIRES):
            continue
        delta: timedelta = _now - datetime.fromtimestamp(
            user.attributes.get(USER_ATTRIBUTE_EXPIRES)
        )
        if delta.total_seconds() > 0:
            LOGGER.debug("User is expired and will be deleted.", user=user, delta=delta)
            user.delete()
            deleted_users += 1
    messages.append(f"Successfully deleted {deleted_users} users.")
    self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, messages))

authentik/core/tasks.py
diff --git a/authentik/core/tasks.py b/authentik/core/tasks.py
index 9f7d73b10c56..0b4c839e8986 100644
--- a/authentik/core/tasks.py
+++ b/authentik/core/tasks.py
@@ -43,6 +43,7 @@ def clean_expired_models(self: MonitoredTask):
     amount = 0
     for session in AuthenticatedSession.objects.all():
         cache_key = f"{KEY_PREFIX}{session.session_key}"
+        value = None
         try:
             value = cache.get(cache_key)
         # pylint: disable=broad-except
diff --git a/authentik/policies/tests/test_engine.py b/authentik/policies/tests/test_engine.py
index ad33aed8a7d2..5dd7f95e7b6d 100644
--- a/authentik/policies/tests/test_engine.py
+++ b/authentik/policies/tests/test_engine.py
@@ -5,6 +5,7 @@
 from authentik.core.models import User
 from authentik.policies.dummy.models import DummyPolicy
 from authentik.policies.engine import PolicyEngine
+from authentik.policies.exceptions import PolicyEngineException
 from authentik.policies.expression.models import ExpressionPolicy
 from authentik.policies.models import Policy, PolicyBinding, PolicyBindingModel, PolicyEngineMode
 from authentik.policies.tests.test_process import clear_policy_cache
@@ -93,7 +94,7 @@ def test_engine_policy_type(self):
         """Test invalid policy type"""
         pbm = PolicyBindingModel.objects.create()
         PolicyBinding.objects.create(target=pbm, policy=self.policy_wrong_type, order=0)
-        with self.assertRaises(TypeError):
+        with self.assertRaises(PolicyEngineException):
             engine = PolicyEngine(pbm, self.user)
             engine.build()
 
diff --git a/internal/outpost/proxyv2/application/application.go b/internal/outpost/proxyv2/application/application.go
index f00720631c6b..6cddbb929b15 100644
--- a/internal/outpost/proxyv2/application/application.go
+++ b/internal/outpost/proxyv2/application/application.go
@@ -43,9 +43,10 @@ type Application struct {
 	outpostName   string
 	sessionName   string
 
-	sessions    sessions.Store
-	proxyConfig api.ProxyOutpostConfig
-	httpClient  *http.Client
+	sessions             sessions.Store
+	proxyConfig          api.ProxyOutpostConfig
+	httpClient           *http.Client
+	publicHostHTTPClient *http.Client
 
 	log *log.Entry
 	mux *mux.Router
@@ -110,25 +111,27 @@ func NewApplication(p api.ProxyOutpostConfig, c *http.Client, server Server) (*A
 	}
 	mux := mux.NewRouter()
 
+	// Save cookie name, based on hashed client ID
 	h := sha256.New()
 	bs := string(h.Sum([]byte(*p.ClientId)))
 	sessionName := fmt.Sprintf("authentik_proxy_%s", bs[:8])
 
 	a := &Application{
-		Host:            externalHost.Host,
-		log:             muxLogger,
-		outpostName:     server.API().Outpost.Name,
-		sessionName:     sessionName,
-		endpoint:        endpoint,
-		oauthConfig:     oauth2Config,
-		tokenVerifier:   verifier,
-		proxyConfig:     p,
-		httpClient:      c,
-		mux:             mux,
-		errorTemplates:  templates.GetTemplates(),
-		ak:              server.API(),
-		authHeaderCache: ttlcache.New(ttlcache.WithDisableTouchOnHit[string, Claims]()),
-		srv:             server,
+		Host:                 externalHost.Host,
+		log:                  muxLogger,
+		outpostName:          server.API().Outpost.Name,
+		sessionName:          sessionName,
+		endpoint:             endpoint,
+		oauthConfig:          oauth2Config,
+		tokenVerifier:        verifier,
+		proxyConfig:          p,
+		httpClient:           c,
+		publicHostHTTPClient: web.NewHostInterceptor(c, server.API().Outpost.Config["authentik_host"].(string)),
+		mux:                  mux,
+		errorTemplates:       templates.GetTemplates(),
+		ak:                   server.API(),
+		authHeaderCache:      ttlcache.New(ttlcache.WithDisableTouchOnHit[string, Claims]()),
+		srv:                  server,
 	}
 	go a.authHeaderCache.Start()
 	a.sessions = a.getStore(p, externalHost)
diff --git a/internal/outpost/proxyv2/application/endpoint.go b/internal/outpost/proxyv2/application/endpoint.go
index 8c32a63fb7c5..28735db143a0 100644
--- a/internal/outpost/proxyv2/application/endpoint.go
+++ b/internal/outpost/proxyv2/application/endpoint.go
@@ -64,7 +64,6 @@ func GetOIDCEndpoint(p api.ProxyOutpostConfig, authentikHost string, embedded bo
 	ep.AuthURL = updateURL(authUrl, aku.Scheme, aku.Host)
 	ep.EndSessionEndpoint = updateURL(endUrl, aku.Scheme, aku.Host)
 	ep.JwksUri = updateURL(jwksUrl, aku.Scheme, aku.Host)
-	ep.TokenURL = updateURL(tokenUrl, aku.Scheme, aku.Host)
 	ep.Issuer = updateURL(ep.Issuer, aku.Scheme, aku.Host)
 	return ep
 }
diff --git a/internal/outpost/proxyv2/application/oauth_callback.go b/internal/outpost/proxyv2/application/oauth_callback.go
index 36ec3a3a5c31..eef418a8414f 100644
--- a/internal/outpost/proxyv2/application/oauth_callback.go
+++ b/internal/outpost/proxyv2/application/oauth_callback.go
@@ -24,7 +24,7 @@ func (a *Application) redeemCallback(savedState string, u *url.URL, c context.Co
 		return nil, fmt.Errorf("blank code")
 	}
 
-	ctx := context.WithValue(c, oauth2.HTTPClient, a.httpClient)
+	ctx := context.WithValue(c, oauth2.HTTPClient, a.publicHostHTTPClient)
 	// Verify state and errors.
 	oauth2Token, err := a.oauthConfig.Exchange(ctx, code)
 	if err != nil {
diff --git a/internal/utils/web/http_host_interceptor.go b/internal/utils/web/http_host_interceptor.go
new file mode 100644
index 000000000000..d3c7de862cb7
--- /dev/null
+++ b/internal/utils/web/http_host_interceptor.go
@@ -0,0 +1,31 @@
+package web
+
+import (
+	"net/http"
+	"net/url"
+
+	log "github.com/sirupsen/logrus"
+)
+
+type hostInterceptor struct {
+	inner http.RoundTripper
+	host  string
+}
+
+func (t hostInterceptor) RoundTrip(r *http.Request) (*http.Response, error) {
+	r.Host = t.host
+	return t.inner.RoundTrip(r)
+}
+
+func NewHostInterceptor(inner *http.Client, host string) *http.Client {
+	aku, err := url.Parse(host)
+	if err != nil {
+		log.WithField("host", host).WithError(err).Warn("failed to parse host")
+	}
+	return &http.Client{
+		Transport: hostInterceptor{
+			inner: inner.Transport,
+			host:  aku.Host,
+		},
+	}
+}

Proxy Provider not working 
Hello

Please help me, I updated the authentik server to 23.1.2, it worked perfectly until now, now the Proxy Provider is not working because of the following error

this is in the server log

{"error":"Post \"https://auth.xxx.com/application/o/token/\": dial tcp 192.168.10.240:443: connect: connection refused","event":"failed to redeem code","level":"warning","logger":"authentik.outpost.proxyv2.application","name":"Kuma","timestamp":"2023-01-24T13:01:34Z"}

The IP in the log is the IP of the nginx reverse proxy manager. The proxy works properly, I don't see any errors. Anyone have any ideas?

