Skip to content

Commit 84e9c37

Browse files
pilartomasclaude
andcommitted
feat(gateway): add audience validation to OIDC access token verification
Validates the aud claim against OAUTH_AUDIENCE when verifying JWT access tokens via JWKS. Required when OAUTH_ISSUER is set. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9555b56 commit 84e9c37

4 files changed

Lines changed: 10 additions & 4 deletions

File tree

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ AUTH_MODE=local
1313
# Set all three + NEXTAUTH_SECRET to enable multi-user OAuth login.
1414
# Redirect URI to register with your provider: {APP_URL}/api/auth/callback/oidc
1515
OAUTH_ISSUER=
16+
OAUTH_AUDIENCE=
1617
OAUTH_CLIENT_ID=
1718
OAUTH_CLIENT_SECRET=
1819

apps/gateway/src/jwks.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ pub(crate) struct AccessTokenClaims {
7070
#[derive(Clone)]
7171
pub(crate) struct JwksManager {
7272
issuer: String,
73+
audience: String,
7374
jwks_uri: String,
7475
cache: Arc<RwLock<CachedKeys>>,
7576
http_client: reqwest::Client,
@@ -84,7 +85,7 @@ impl JwksManager {
8485
/// Fetches `{issuer_url}/.well-known/openid-configuration` to discover
8586
/// the `jwks_uri`, then fetches the initial key set. Fails if the
8687
/// discovery document or JWKS cannot be fetched.
87-
pub async fn new(issuer_url: &str) -> Result<Self> {
88+
pub async fn new(issuer_url: &str, audience: String) -> Result<Self> {
8889
let http_client = reqwest::Client::new();
8990
let base = issuer_url.trim_end_matches('/');
9091

@@ -109,6 +110,7 @@ impl JwksManager {
109110

110111
Ok(Self {
111112
issuer: discovery.issuer,
113+
audience,
112114
jwks_uri: discovery.jwks_uri,
113115
cache: Arc::new(RwLock::new(CachedKeys {
114116
keys,
@@ -173,7 +175,7 @@ impl JwksManager {
173175
let mut validation = Validation::default();
174176
validation.algorithms = ACCEPTED_ALGORITHMS.to_vec();
175177
validation.set_issuer(&[&self.issuer]);
176-
validation.validate_aud = false;
178+
validation.set_audience(&[&self.audience]);
177179

178180
let token_data: TokenData<AccessTokenClaims> =
179181
decode(token, key, &validation).map_err(|e| {

apps/gateway/src/main.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,9 @@ async fn main() -> Result<()> {
148148
// so JWKS is not required for browser-based auth to work.
149149
let jwks = match std::env::var("OAUTH_ISSUER") {
150150
Ok(issuer) => {
151-
let manager = jwks::JwksManager::new(&issuer).await?;
151+
let audience = std::env::var("OAUTH_AUDIENCE")
152+
.context("OAUTH_AUDIENCE must be set when OAUTH_ISSUER is set")?;
153+
let manager = jwks::JwksManager::new(&issuer, audience).await?;
152154
Some(manager)
153155
}
154156
Err(_) => None,

turbo.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@
6262
"REDIS_TLS",
6363
"COGNITO_USER_POOL_ID",
6464
"AUTH_MODE",
65-
"OAUTH_ISSUER"
65+
"OAUTH_ISSUER",
66+
"OAUTH_AUDIENCE"
6667
]
6768
},
6869
"@onecli/gateway#check": {

0 commit comments

Comments
 (0)