Existing Session Remains Valid After Account Is Deactivated in External IdP

I’m testing OpenZiti 2.0.1 with Authentik as the external OIDC identity provider.

I authenticated successfully through Authentik and established an OpenZiti session. I then deactivated the corresponding user account in Authentik while leaving the existing OpenZiti session active.

OpenZiti continues to permit access using that existing session. I tested again after more than 72 hours, and access was still permitted.

Is this the expected behavior?

I understand that Authentik is involved during authentication and that an already-issued OpenZiti session may remain valid independently afterward. However, I would expect disabling the account in the authoritative IdP to result in access being revoked within a reasonably short period.

Centralized identity management is an important part of a Zero Trust approach. If an identity is disabled in the authoritative IdP, I would expect that change to propagate to systems relying on that identity so that access can be revoked consistently rather than continuing indefinitely under an existing session.

Is there currently a mechanism in OpenZiti 2.0.1 to detect that the Authentik account has been disabled and terminate or invalidate existing sessions? If not, is the recommended approach to rely on relatively short OpenZiti session/token lifetimes and require periodic reauthentication against Authentik?

Hi @tomc, sorry for the delay, been a bit busy.

It's easy to be surprised by this, but yes, it's expected. Once you authenticate, the controller issues its own session token and the IdP drops out of the loop. Nothing
in the controller goes back and asks Authentik whether that account is still enabled, and the SDK will happily keep refreshing its OpenZiti session, which is why you
still had access 72 hours later.

What you're describing is secondary auth, not primary. Put those identities in an auth policy with a required secondary external JWT signer (it can be the same signer as primary):

ziti edge update auth-policy <name> --secondary-req-ext-jwt-signer <signer-id>

With that set, every request has to carry a currently-valid Authentik JWT, not just the initial login. Your revocation window becomes the lifetime of Authentik's access
token instead of unbounded, so set that short.

Don't bother shortening the OpenZiti session. The refresh never touches Authentik, so it succeeds either way. The Authentik access token lifetime is your revocation window. Keycloak kills the refresh grant the instant the account is disabled, so the client can't renew its own IdP token either.

I had claude stand this up against 2.0.1 with Keycloak to confirm. With primary ext-jwt only, the session survived deactivation, refreshed fine, and kept working. With a required secondary signer, the identity got a 401 the moment its IdP token expired and couldn't be renewed.

Can we discuss this in the context of a hypothetical mobile app where each individual user logs in with their own identity? How would you configure OpenZiti to support that model?

I thought I'd described that, but maybe not well enough! :slight_smile: If I were developing a mobile app where a user "logs in with their own identity" I'd first make an assumption that you're meaning a cert-backed identity...

When I see "their own identity" I take that to mean an OpenZiti identity that uses a cert. So an actual .json file with a key/cert inside it. Then I would create an auth policy that requires 'secondary auth' and I would assign that user into the auth policy. Then your app would need to login to the IdP to obtain a token, that token + the OpenZiti identity would then be used to authenticate to the controller. After the token invalidates, your app would have to obtain a new token from the IdP, reapply it you continue on. I don't have an example handy for you to look at -- but that's what I'd end up doing.

Hopefully that makes sense?

It does make sense. There is a follow-on question, with a little leeway in how it is framed.

All of the individual users, credentials, and associated policies already exist in the IdP, and the goal is to avoid managing another set of per-user credentials strictly for OpenZiti.

Could the certificate-backed OpenZiti identity represent the mobile application / no credential rather than the individual user? In other words, could the key/certificate be generic and shared across instances of the mobile app, with the external IdP JWT providing the individual user identity and authentication?

The question is really whether the certificate needs to uniquely identify each user in this model, or whether the IdP can remain the authoritative source for individual user identity.

nooooo, no... :slight_smile: You're misunderstanding... You never want to do that in a zero trust setup...

Having a key/cert in an identity file basically ties THAT identity to THAT piece of hardware (unless you move the identity which you also 'should not do' in a production situation). Instead, what you want is to use an 'ext-jwt-signer' for primary auth. Then when you enroll an identity you end up with an identity file that is basically shareable (you still shouldn't, you should allow OpenZiti to bootstrap trust) because it only contains the url of your controller and CAs, it doesn't contain a key or cert.

For example, here's an identity I have. It uses ext-jwt-signer based auth:

{
	"ztAPI":"https://9a062ac6-0bf5-489e-9b90-726195c84a8d.production.netfoundry.io:443/",
	"ztAPIs":[
		"https://64205aef-f28e-4a48-8de5-8c0194d05844.production.netfoundry.io:443/edge/client/v1",
		"https://9a062ac6-0bf5-489e-9b90-726195c84a8d.production.netfoundry.io:443/edge/client/v1",
		"https://f9c0c436-87e8-458f-a4dd-1b25a6e60817.production.netfoundry.io:443/edge/client/v1"
	],
	"id":{
		"ca":"-----BEGIN CERTIFICATE-----\nMIIF7jCCA9agAwIBAC.....LEA1GRDdNx4/oes9ymZ\n-----END CERTIFICATE-----\n"
	}

You can see it ONLY contains the urls for the controllers and the CA bundle (which is important because we've bootstrapped the trust properly)...

For this to work you need to properly configure your signer but if you've done this for secondary auth, you'll already have it ready for primary auth...

So before when I wrote:

What you're describing is secondary auth, not primary. Put those identities in an auth policy with a required secondary external JWT signer (it can be the same signer as primary):slight_smile:

What I envision is you making an auth policy that primary auth is ext-jwt-signer with secondary auth also the same ext-jwt-signer and then you should have exactly what you're looking for. It might make more sense now though?