Skip to content

Commit a84e38c

Browse files
Merge pull request #598 from misaeldasilva123ms96-commits/agent/jwt-nbf-validation
fix(auth): reject JWTs before nbf
2 parents 3cbf1df + 85a7466 commit a84e38c

1 file changed

Lines changed: 42 additions & 0 deletions

File tree

backend/rust/src/observability_auth.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ impl SupabaseAuthConfig {
358358
fn validate_token(&self, token: &str) -> Result<SupabaseClaims, jsonwebtoken::errors::Error> {
359359
let mut validation = Validation::new(Algorithm::HS256);
360360
validation.validate_exp = true;
361+
validation.validate_nbf = true;
361362
validation.validate_aud = true;
362363
validation.leeway = 0;
363364
validation.set_required_spec_claims(&["exp", "iss", "aud"]);
@@ -677,6 +678,27 @@ mod tests {
677678
token_with_audience(exp_offset_seconds, Some(json!("authenticated")))
678679
}
679680

681+
fn token_with_nbf_offset(nbf_offset_seconds: i64) -> String {
682+
let now = std::time::SystemTime::now()
683+
.duration_since(std::time::UNIX_EPOCH)
684+
.expect("unix epoch")
685+
.as_secs() as i64;
686+
let claims = json!({
687+
"iss": "https://example.supabase.co/auth/v1",
688+
"sub": "operator-123",
689+
"aud": "authenticated",
690+
"exp": now + 300,
691+
"nbf": now + nbf_offset_seconds,
692+
});
693+
694+
encode(
695+
&Header::new(Algorithm::HS256),
696+
&claims,
697+
&EncodingKey::from_secret(b"test-only-high-entropy-secret-material"),
698+
)
699+
.expect("encode test jwt")
700+
}
701+
680702
fn test_router() -> Router {
681703
async fn ok_handler() -> Json<Value> {
682704
Json(json!({ "ok": true }))
@@ -767,6 +789,26 @@ mod tests {
767789
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
768790
}
769791

792+
#[tokio::test]
793+
async fn token_with_future_nbf_returns_401() {
794+
let response = test_router()
795+
.oneshot(
796+
Request::builder()
797+
.method(Method::GET)
798+
.uri("/api/observability/snapshot")
799+
.header(
800+
AUTHORIZATION,
801+
format!("Bearer {}", token_with_nbf_offset(60)),
802+
)
803+
.body(Body::empty())
804+
.expect("request"),
805+
)
806+
.await
807+
.expect("response");
808+
809+
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
810+
}
811+
770812
#[tokio::test]
771813
async fn token_without_audience_returns_401() {
772814
let response = test_router()

0 commit comments

Comments
 (0)