What happened?
I’ve setup Pomerium core successfully with a couple of routes and custom identity provider (azure). This is in a lab-environment. One of my services (vaultwarden.pomerium.localhost) has a policy to match a group claim for a specific group-ID (f64d91fa-e526-441c-965e-8edb8b6f0a8b) and this policy works as expected after each Pomerium logout/login**.
When I remove the test user from that specific group and wait until background userdata refresh finishes, I still have access to that specific service.
Checking the verify service; I see that specific group-ID is still present in the pomerium identity jwt object.
What did you expect to happen?
I expected userdata refresh to retrieve an up-to-date (as of that moment) claims-set without the group from which the test user was removed.
The test user should be barred from accessing the protected route after background refresh of the claims when the user got removed from the group.
The verify endpoint should not advertise the group ‘f64d91fa-e526-441c-965e-8edb8b6f0a8b’ after the test user gets removed from it.
How’d it happen?
- Setup Pomerium core, see config below
- Visit https://vaultwarden.pomerium.localhost/admin, sign in, access denied
- [IDP] Add test user (myself: bert.proesmans) to group f64d91fa-e526-441c-965e-8edb8b6f0a8b
- Sign out of Pomerium
- Visit https://vaultwarden.pomerium.localhost/admin, sign in, access approved
- [IDP] Remove test user from group f64d91fa-e526-441c-965e-8edb8b6f0a8b
- Wait ~10 minutes for background refresh
- Visit https://vaultwarden.pomerium.localhost/admin, access still approved => UNEXPECTED
What’s your environment like?
- Pomerium version (retrieve with
pomerium --version): v0.32.2 - Server Operating System/Architecture/Cloud: bare-metal, docker, no persisted storage
What’s your config.yaml?
address: '[::]:443'
log_level: debug
debug_address: '[::]:6060'
envoy_admin_address: '[::]:29091'
shared_secret: jMRfy/MXk4+ndku14PUU6+i6ofcQz7vyULbQLP7NpXQ=
cookie_secret: KDBIqKTq8aJ4OuU07np7qBtT6jX37I2H5DCczzExvP8=
signing_key_file: '/pomerium/ec_private.pem'
authenticate_service_url: 'https://authenticate.pomerium.localhost'
idp_provider: 'azure'
idp_provider_url: 'https://login.microsoftonline.com/<OMITTED>/v2.0'
idp_client_id: '<OMITTED>'
idp_client_secret: '<OMITTED>'
# NOTE; Container names resolve through Docker DNS to the corresponding management IP
routes:
- from: https://verify.pomerium.localhost
to: http://pomerium-verify:8000
allow_any_authenticated_user: true
pass_identity_headers: true
- from: https://httpbin.pomerium.localhost
to: http://httpbin:80
allow_any_authenticated_user: true
pass_identity_headers: true
# WARN; Routes are matched in definition order! More specific routes must come before general routes!
- from: https://vaultwarden.pomerium.localhost
to: http://vaultwarden:80
prefix: /admin
policy:
- allow:
and:
- domain:
is: e-powerinternational.com
- claim/groups:
# Name: TEST - Pomerium Vaultwarden admin
has: 'f64d91fa-e526-441c-965e-8edb8b6f0a8b'
- from: https://vaultwarden.pomerium.localhost
to: http://vaultwarden:80
policy:
- allow:
and:
- domain:
is: e-powerinternational.com
What did you see in the logs?
<snip>
{"level":"info","service":"identity_manager","user-id":"ec150b5a-8943-4842-b2d0-dad7a44737f2","time":"2026-03-18T15:14:00Z","message":"updating user info"}
{"level":"debug","service":"identity_manager","user-id":"ec150b5a-8943-4842-b2d0-dad7a44737f2","time":"2026-03-18T15:14:01Z","message":"updating user"}
{"level":"debug","record-type":"type.googleapis.com/user.User","record-id":"ec150b5a-8943-4842-b2d0-dad7a44737f2","time":"2026-03-18T15:14:01Z","message":"put"}
{"level":"debug","service":"identity_manager","record-type":"type.googleapis.com/user.User","record-id":"ec150b5a-8943-4842-b2d0-dad7a44737f2","record-version":6,"syncer-id":"identity_manager/users","syncer-type":"type.googleapis.com/user.User","time":"2026-03-18T15:14:01Z","message":"syncer got record"}
{"level":"debug","service":"identity_manager","user-id":"ec150b5a-8943-4842-b2d0-dad7a44737f2","time":"2026-03-18T15:14:01Z","message":"user updated"}
{"level":"debug","service":"envoy","name":"http2","time":"2026-03-18T15:14:01Z","message":"[Tags: \\\"ConnectionId\\\":\\\"5\\\"] Http2Visitor: remaining data payload: 1083, stream_id: 21, end_stream: false"}
<snip>
Additional context
I’ve tried tracing the update flow to pinpoint where exactly my expectations differ, but my Go-code knowledge is weak.
The list of ids in the groups claim never updates within a Pomerium session, I have to sign-out and back in again to get an accurate up-to-date view of group membership.
**BUT there is also some kind of dictionary-key-collapse happening. After I sign-in from another browser (causing a second session to be created for the same user) concurrently, now in both sessions the service access policy evaluation uses the current(/latest as of second sign-in) group claim values.
So the verify endpoint can show different group claim values in each session, while the actual service policy matcher is consistent with one, multiple, or none of sessions’ reported groups claim.


