dolthub / dolthub/dolt

Optionally bind authentication_dolt_jwt to the token's sub claim

Open
#11,289 2 comments 0 reactions 0 assignees View on GitHub
bug customer issue permissions sql server
Dominant language
Go
Stars
24.4k
Forks
873
Avg merge
1d 5h
Merged PRs (30d)
108

Description

# Feature request: optionally bind `authentication_dolt_jwt` to the token's `sub` claim

## Summary

When a user is created with `IDENTIFIED WITH authentication_dolt_jwt`, Dolt validates the
token's signature, issuer, audience, and expiry, and checks that the `sub` **written into the
user's identity string** matches the connecting username — but it never checks the token's own
`sub` claim against anything. As a result, any token that is validly signed by the configured
JWKS (correct `iss`/`aud`, unexpired) will authenticate **any** JWT user, regardless of which
`sub` the token was actually issued for.

I'd like an **opt-in** way to require that the token's `sub` claim equals the connecting user
(i.e. the user identity's configured `sub`), so that a token minted for one user cannot be used
to authenticate as a different user.

## Current behavior (v2.1.10)

In `go/cmd/dolt/commands/engine/jwtplugin.go`, `validateJWT` compares the identity string's
`sub` to the username:

```go
expectedClaimsMap := parseUserIdentity(identity)
sub, ok := expectedClaimsMap["sub"]
if ok && sub != username {
return false, errors.New("ValidateJWT: Subjects do not match")
}
```

`sub` here is the value from the `CREATE USER ... AS 'jwks=...,sub=...,iss=...,aud=...'` string,
not the token. The token is then validated in `go/libraries/utils/jwtauth/validate.go`
(`ValidateJWT`), which enforces the algorithm allowlist (RS256/RS512/EdDSA) and, via go-jose,
the `iss`, `aud`, and `exp` claims — but the expected claims are built as
`jwt.Expected{Issuer, Audience}` only. The token's `sub` is never placed into the expected set
and is never compared to the username. It is used solely for logging via `fields_to_log`.

Net effect: for a given JWKS/issuer/audience, all `authentication_dolt_jwt` users share a single
"any valid token authenticates" trust boundary. The token is effectively a bearer credential
whose `sub` is informational.

## Minimal repro

```sql
CREATE USER 'alice'@'%' IDENTIFIED WITH authentication_dolt_jwt
AS 'jwks=k,sub=alice,iss=my-iss,aud=my-aud';
CREATE USER 'bob'@'%' IDENTIFIED WITH authentication_dolt_jwt
AS 'jwks=k,sub=bob,iss=my-iss,aud=my-aud';
```

Mint a token with `sub=alice` (correct `iss`/`aud`, signed by the JWKS key), then connect as
**bob** presenting that token over TLS (`mysql_clear_password`). The connection succeeds and the
session has bob's privileges — even though the token's `sub` is `alice`.

## Why it matters (multi-tenant isolation)

In a multi-tenant setup, the natural design is one scoped user per tenant
(`GRANT ALL ON tenant_x.* TO 'tenant_x'@'%'`) with a token issuer that mints a per-tenant token.
The intuitive expectation is that a `tenant_x` token can only act as `tenant_x`. Today it cannot
be relied on: possession of any valid token plus the ability to choose the connecting username is
enough to authenticate as any tenant and receive that tenant's grants. Isolation therefore has to
be enforced entirely outside Dolt (a trusted proxy that fixes the username + network controls that
prevent clients from opening their own connections), rather than by the token itself. Having Dolt
optionally bind the token to the user would let the token be the isolation boundary, which is a
much smaller and more auditable trust surface.

It also affects the audit trail: because `fields_to_log` logs the token's `sub`, a replayed token
logs the token's `sub`, not the username the session actually authenticated as.

## Proposed enhancement

An opt-in check, so existing single-account setups (e.g. Hosted's read-only UI account) are
unaffected:

- **Per-user**, e.g. a flag in the identity string: `AS '...,require_sub_match=true'`, or
- **Per-JWKS-config**, e.g. a `require_subject_match: true` field on the `jwks:` server config
entry, applying to all users backed by that JWKS.

When enabled, `validateJWT` would additionally require `claims.Subject == username` (equivalently,
add `Subject` to the go-jose `jwt.Expected`). Default off to preserve current behavior.

## Alternatives considered

- **Trusted proxy fixes the username + network isolation** — what we do today. Works, but moves the
entire tenant-isolation boundary outside Dolt and depends on the proxy never letting a client
choose its own username and clients never reaching the server directly.
- **Per-issuer/audience per tenant** — a distinct `aud` (and JWKS entry) per tenant would bind
tokens to tenants via the already-checked `aud`. This works but scales poorly (a JWKS entry and
audience per tenant) and is awkward for issuers like AWS KMS. A `sub` check is the natural fit.

## Environment

- Observed on `dolthub/dolt-sql-server:2.1.10` (linux/arm64), server config with a single `jwks:`
entry, TLS + `require_secure_transport`, users created `IDENTIFIED WITH authentication_dolt_jwt`.
- Tokens: RS256 signed by an external issuer; `iss`/`aud`/`exp`/signature all validated correctly.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in go/cmd/dolt/commands/engine/jwtplugin.go at validateJWT, then read go/libraries/utils/jwtauth/validate.go and the existing identity and JWKS configuration handling. Decide how the opt-in setting is represented, preserve the current default behavior, and verify that enabled subject matching rejects a token issued for another user while valid matching tokens still authenticate.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, mysql
Domain
authentication, database, security
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.