Skip to content

Commit d354005

Browse files
cursoragentheskew
andcommitted
fix(mcp): do not require HONE_MCP_ISSUER on local HS256 tokens
validate_mcp_hs256 was applying issuer, but hone mcp-token omits iss. Issuer stays a JWKS/AS check only so a configured HONE_MCP_ISSUER does not reject locally minted tokens. Co-authored-by: Nathan Heskew <heskew@users.noreply.github.com>
1 parent 6125bc4 commit d354005

1 file changed

Lines changed: 19 additions & 4 deletions

File tree

crates/hone-server/src/mcp/oauth.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ pub struct McpOAuthConfig {
3030
pub authorization_servers: Vec<String>,
3131
/// HS256 secret for locally minted MCP-audience JWTs (`HONE_MCP_JWT_SECRET`).
3232
pub jwt_secret: Option<String>,
33-
/// Expected `iss` when validating AS-issued JWTs.
33+
/// Expected `iss` when validating AS-issued JWKS tokens only.
34+
/// Local HS256 tokens from `hone mcp-token` omit `iss`.
3435
pub issuer: Option<String>,
3536
/// JWKS URL for RS256 tokens from an external AS (`HONE_MCP_JWKS_URL`).
3637
pub jwks_url: Option<String>,
@@ -194,13 +195,12 @@ pub fn validate_mcp_hs256(token: &str, config: &McpOAuthConfig) -> Result<(), St
194195
.filter(|s| !s.is_empty())
195196
.ok_or("MCP resource not configured")?;
196197

198+
// Local HS256 tokens (`hone mcp-token`) omit `iss`. HONE_MCP_ISSUER is
199+
// for external JWKS tokens only — applying it here rejects every mint.
197200
let mut validation = jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::HS256);
198201
validation.set_required_spec_claims(&["exp", "aud"]);
199202
validation.set_audience(&[resource]);
200203
validation.validate_exp = true;
201-
if let Some(iss) = config.issuer.as_deref().filter(|s| !s.is_empty()) {
202-
validation.set_issuer(&[iss]);
203-
}
204204

205205
let data = jsonwebtoken::decode::<McpAccessClaims>(
206206
token,
@@ -374,6 +374,21 @@ mod tests {
374374
validate_mcp_hs256(&token, &config).unwrap();
375375
}
376376

377+
#[test]
378+
fn minted_token_still_validates_when_issuer_is_set() {
379+
let resource = "http://127.0.0.1:3001/mcp";
380+
let secret = "test-mcp-jwt-secret";
381+
let token = mint_mcp_access_token(secret, resource, 60).unwrap();
382+
let config = McpOAuthConfig {
383+
resource: Some(resource.to_string()),
384+
jwt_secret: Some(secret.to_string()),
385+
issuer: Some("https://auth.example".to_string()),
386+
..Default::default()
387+
};
388+
validate_mcp_hs256(&token, &config)
389+
.expect("HONE_MCP_ISSUER must not reject locally minted HS256 tokens");
390+
}
391+
377392
#[test]
378393
fn token_for_api_audience_is_rejected_on_mcp() {
379394
let secret = "test-mcp-jwt-secret";

0 commit comments

Comments
 (0)