apache / apache/iceberg

Core: Exchanged AuthSession refreshes with the parent's client credential, silently changing identity

Open
#17,600 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
9.2k
Forks
3.5k
Avg merge
2d 11h
Merged PRs (30d)
132

Description

### Apache Iceberg version

1.10.1 (also reproduces on `main`)

### Query engine

Trino

### Please describe the bug 🐞

When `token-exchange-enabled` is set to `false`, a session that was created by token exchange refreshes itself using the parent's client credential. The refreshed token identifies the catalog client rather than the exchanged subject, so the session changes identity with no error reported.

Configuration that reproduces it:

* a catalog configured with `credential` and `token-exchange-enabled=false`
* a `SessionContext` supplying a typed subject token such as `urn:ietf:params:oauth:token-type:jwt`, which is what Trino sends when `iceberg.rest-catalog.session=USER`

With those credentials, `OAuth2Manager.maybeCreateChildSession` skips the `token` and `credential` branches and reaches `newSessionFromTokenExchange`, so the contextual session is minted by exchanging the user's subject token.

`AuthSession.fromTokenExchange` builds that child through `fromTokenResponse`, which copies the parent configuration with `AuthConfig.builder().from(parent.config())`. That inherits both `credential` and `exchangeEnabled` from the catalog session.

On refresh, `OAuth2Util.refreshToken` branches only on `exchangeEnabled`. Since the child inherited `false`, it takes the `client_credentials` path using the inherited credential, and the catalog client's token replaces the user's token in the session. Requests continue to succeed, so there is nothing to observe apart from the change in effective principal.

For a one hour token this happens roughly 55 minutes into the session. `AuthSessionCache` uses `expireAfterAccess`, so the sessions that live long enough to refresh are exactly the ones belonging to active users.

The inherited value also reaches table sessions, because `RESTSessionCatalog` passes the contextual session as the parent when it calls `AuthManager.tableSession`.

There is currently no test coverage for this combination. The two tests in `TestRESTCatalog` that set `token-exchange-enabled=false` never create a session through `fromTokenExchange`: one passes empty context credentials, and the other passes `token`, which takes the `fromAccessToken` path instead.

Suggested fix: a session created by exchanging a subject token should be renewed the same way, independent of the catalog level flag. #13809 added the flag to control how credential derived sessions refresh, and applying it to exchange derived sessions changes which principal the session represents.

Related, and not covered by the above: `AuthSession.fromAccessToken` inherits the parent credential in the same way, so the bearer token path carries the same hazard.

Contributor guide

Open the contributing guide

Research direction

Start with AuthSession.fromTokenExchange and OAuth2Util.refreshToken, then inspect the token-exchange-disabled cases in TestRESTCatalog. Trace how the parent AuthConfig is copied and how refresh selects credentials. Add coverage for a subject-token session with token-exchange-enabled=false and verify that refresh preserves the exchanged subject identity.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
authentication, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.