Skip to content

Conversation

@elmattic
Copy link
Contributor

@elmattic elmattic commented Oct 15, 2025

Summary of changes

Changes introduced in this pull request:

  • Add FOREST_JWT_DISABLE_EXP_VALIDATION env var

This PR was motivated by a situation where Lotus and Forest nodes use the same private key, and JWT expiration validation is disabled for Forest—allowing the same Auth Bearer token (created with lotus auth create-token) to work for both clients.

Reference issue to close (if applicable)

Closes

Other information and links

Change checklist

  • I have performed a self-review of my own code,
  • I have made corresponding changes to the documentation. All new code adheres to the team's documentation standards,
  • I have added tests that prove my fix is effective or that my feature works (if possible),
  • I have made sure the CHANGELOG is up-to-date. All user-facing changes should be reflected in this document.

Summary by CodeRabbit

  • New Features

    • Added FOREST_JWT_DISABLE_EXP_VALIDATION env var to optionally disable JWT expiration validation.
  • Documentation

    • Documented the new env var with type, example, use case, and security warning.
  • Behavior

    • Emits a runtime warning when JWT expiration validation is disabled.
  • Tests

    • Added tests covering tokens with and without exp and validation behavior when the toggle is enabled.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 15, 2025

Walkthrough

Adds an environment-controlled feature gate to optionally disable JWT expiration validation, makes token exp optional in Claims, updates token creation/tests to support exp = None, logs a warning at daemon startup when the gate is enabled, and documents the new FOREST_JWT_DISABLE_EXP_VALIDATION env var.

Changes

Cohort / File(s) Summary
Changelog & docs
CHANGELOG.md, docs/docs/users/reference/env_variables.md
Added FOREST_JWT_DISABLE_EXP_VALIDATION env var entry, example, use case, and security warning.
JWT validation & tests
src/auth/mod.rs
Made Claims.exp Option<usize> with serde(default); added is_env_truthy("FOREST_JWT_DISABLE_EXP_VALIDATION") check in verify_token to allow skipping exp requirement and expiration checks when set; added token-creation helper(s) and tests for tokens without exp and for disabled-exp validation behavior.
Daemon startup warning
src/daemon/mod.rs
Added is_env_truthy check in maybe_start_rpc_service and log warning when FOREST_JWT_DISABLE_EXP_VALIDATION is enabled before spawning RPC service.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant Auth as auth::verify_token
  participant Env as env (FOREST_JWT_DISABLE_EXP_VALIDATION)

  Caller->>Auth: verify_token(token)
  Auth->>Env: is_env_truthy(FOREST_JWT_DISABLE_EXP_VALIDATION)
  alt env truthy (disable exp validation)
    note right of Auth #E6F7FF: allow missing `exp` and\nskip expiration checks
    Auth-->>Caller: perform other claim checks -> success/failure
  else env falsy (normal validation)
    note right of Auth #FFF4E6: require `exp` and\ncheck expiration against now
    Auth->>Auth: if `exp` present and not expired -> success
    Auth-->>Caller: expired or missing `exp` -> failure
  end
Loading
sequenceDiagram
  participant Daemon
  participant Env as env (FOREST_JWT_DISABLE_EXP_VALIDATION)
  participant RPC as RPC service

  Daemon->>Env: is_env_truthy(FOREST_JWT_DISABLE_EXP_VALIDATION)
  alt env truthy
    Daemon->>Daemon: log warning about disabled JWT exp validation
  end
  Daemon->>RPC: spawn RPC service
  RPC-->>Daemon: started
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Suggested reviewers

  • LesnyRumcajs

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly describes the primary change of gating JWT expiration validation behind an environment variable, matching the main objective of the pull request without extraneous detail.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch elmattic/jwt-disable-exp-check

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5350497 and 09d9563.

📒 Files selected for processing (1)
  • docs/docs/users/reference/env_variables.md (2 hunks)
🧰 Additional context used
🪛 LanguageTool
docs/docs/users/reference/env_variables.md

[style] ~53-~53: ‘Whether or not’ might be wordy. Consider a shorter alternative.
Context: ... | Whether or not to disable JWT expiration validation ...

(EN_WORDINESS_PREMIUM_WHETHER_OR_NOT)


[grammar] ~80-~80: There might be a mistake here.
Context: ...ISABLE_EXP_VALIDATION` #### 🔧 Use Case Intended for controlled cross-system tok...

(QB_NEW_EN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: cargo-publish-dry-run
  • GitHub Check: Build MacOS
  • GitHub Check: Build Ubuntu
  • GitHub Check: tests-release
  • GitHub Check: tests
  • GitHub Check: Build forest binaries on Linux AMD64
  • GitHub Check: All lint checks

Comment @coderabbitai help to get the list of available commands and usage tips.

@elmattic elmattic changed the title Gate JWT expiration check behind env var Gate JWT expiration validation behind env var Oct 15, 2025
@elmattic elmattic marked this pull request as ready for review October 16, 2025 10:04
@elmattic elmattic requested a review from a team as a code owner October 16, 2025 10:04
@elmattic elmattic requested review from hanabi1224 and sudo-shashank and removed request for a team October 16, 2025 10:04
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
docs/docs/users/reference/env_variables.md (1)

53-53: Minor style suggestion: simplify wording.

The phrase "Whether or not to disable" could be shortened to "Whether to disable" for improved clarity.

-| `FOREST_JWT_DISABLE_EXP_VALIDATION`                       | 1 or true                        | empty                                          | 1                                                             | Whether or not to disable JWT expiration validation                                                                   |
+| `FOREST_JWT_DISABLE_EXP_VALIDATION`                       | 1 or true                        | empty                                          | 1                                                             | Whether to disable JWT expiration validation                                                                   |
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2f2b0d9 and c379e5f.

📒 Files selected for processing (3)
  • CHANGELOG.md (1 hunks)
  • docs/docs/users/reference/env_variables.md (1 hunks)
  • src/auth/mod.rs (4 hunks)
🧰 Additional context used
🪛 LanguageTool
docs/docs/users/reference/env_variables.md

[style] ~53-~53: ‘Whether or not’ might be wordy. Consider a shorter alternative.
Context: ... | Whether or not to disable JWT expiration validation ...

(EN_WORDINESS_PREMIUM_WHETHER_OR_NOT)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: tests-release
  • GitHub Check: tests
  • GitHub Check: Build forest binaries on Linux AMD64
  • GitHub Check: Build Ubuntu
  • GitHub Check: cargo-publish-dry-run
  • GitHub Check: Build MacOS
  • GitHub Check: All lint checks
🔇 Additional comments (4)
src/auth/mod.rs (4)

6-6: LGTM!

The import is necessary for the new environment variable check and is correctly placed.


29-30: LGTM!

Making exp optional with #[serde(default)] properly supports tokens without expiration while maintaining backward compatibility with existing tokens that have the field.


38-38: LGTM!

Correctly wraps the expiration value in Some to match the new optional type.


69-75: LGTM!

The helper function cleanly creates tokens without expiration for testing purposes. The implementation correctly sets exp: None.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/auth/mod.rs (1)

45-52: Warn when globally bypassing exp and simplify claim handling.

Emit a warning to surface the security tradeoff and simplify by mutating required_spec_claims in place.

-    let mut claims = validation.required_spec_claims.clone();
-    claims.remove("exp");
-    let buff: Vec<_> = claims.iter().collect();
-    validation.set_required_spec_claims(&buff);
-    validation.validate_exp = false;
+    tracing::warn!(
+        "JWT expiration validation is disabled via FOREST_JWT_DISABLE_EXP_VALIDATION; \
+         use only in development/testing or explicit cross-system token sharing."
+    );
+    validation.required_spec_claims.remove("exp");
+    validation.validate_exp = false;
🧹 Nitpick comments (2)
src/auth/mod.rs (2)

29-31: Avoid serializing "exp": null; omit the claim when None.

Add skip_serializing_if so tokens without exp don’t emit a null value and better mirror peers (e.g., Lotus).

-    #[serde(default)]
-    exp: Option<usize>,
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    exp: Option<usize>,

68-75: This currently encodes "exp": null; ensure the field is omitted instead.

As written, exp: None will serialize to null. If the intent is to produce tokens without the exp claim, rely on skip_serializing_if on Claims.exp (see earlier comment). No further changes to this helper needed once that attribute is added.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c379e5f and beadb81.

📒 Files selected for processing (1)
  • src/auth/mod.rs (4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: tests
  • GitHub Check: tests-release
  • GitHub Check: Build Ubuntu
  • GitHub Check: cargo-publish-dry-run
  • GitHub Check: Build MacOS
  • GitHub Check: Build forest binaries on Linux AMD64
  • GitHub Check: All lint checks
  • GitHub Check: Check
  • GitHub Check: Deploy to Cloudflare Pages
🔇 Additional comments (2)
src/auth/mod.rs (2)

6-6: LGTM on using central env helper.

Using is_env_truthy for gating is consistent and clear.


38-39: Wrapping exp in Some(...) is correct.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/auth/mod.rs (1)

128-130: Consider RAII guard for env var cleanup on panic.

The test logic is correct and properly validates both tokens without expiration and expired tokens when validation is disabled. The unsafe blocks are necessary because std::env::set_var and std::env::remove_var are inherently unsafe in Rust (they can cause data races).

However, if the test panics between setting and removing the env var, the cleanup at line 147-149 won't execute, potentially affecting subsequent tests. Consider implementing the RAII guard pattern suggested in the previous review to ensure cleanup even on panic:

struct EnvVarGuard {
    key: &'static str,
}

impl EnvVarGuard {
    fn new(key: &'static str, value: &str) -> Self {
        unsafe {
            std::env::set_var(key, value);
        }
        Self { key }
    }
}

impl Drop for EnvVarGuard {
    fn drop(&mut self) {
        unsafe {
            std::env::remove_var(self.key);
        }
    }
}

Then replace lines 128-130 and 147-149 with:

let _guard = EnvVarGuard::new("FOREST_JWT_DISABLE_EXP_VALIDATION", "1");

Note: This addresses the previous review feedback which you marked as "Done" but wasn't fully implemented.

Also applies to: 147-149

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between beadb81 and fc6956e.

📒 Files selected for processing (1)
  • src/auth/mod.rs (4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: Check
  • GitHub Check: All lint checks
  • GitHub Check: tests-release
  • GitHub Check: tests
  • GitHub Check: Build Ubuntu
  • GitHub Check: cargo-publish-dry-run
  • GitHub Check: Build MacOS
  • GitHub Check: Build forest binaries on Linux AMD64
  • GitHub Check: Deploy to Cloudflare Pages
🔇 Additional comments (5)
src/auth/mod.rs (5)

29-30: LGTM! Optional expiration field correctly implemented.

The change from usize to Option<usize> with #[serde(default)] correctly allows tokens without expiration while maintaining backward compatibility with tokens that have an expiration.


38-38: LGTM! Correctly wraps expiration in Some().

The change properly wraps the expiration timestamp in Some() to match the new Option<usize> type.


67-68: LGTM! Enables test serialization for env var safety.

The serial_test import enables the #[serial] attribute, which helps prevent concurrent tests from interfering with each other's environment variable modifications.


69-76: LGTM! Test helper correctly creates tokens without expiration.

The helper function properly constructs a token with exp: None, which is essential for testing the new optional expiration field behavior.


79-79: LGTM! Prevents test interference.

Adding #[serial] to this test prevents potential interference from the new test that manipulates environment variables, even though this test doesn't use env vars directly.

@elmattic elmattic added this pull request to the merge queue Oct 16, 2025
Merged via the queue into main with commit 15ea901 Oct 16, 2025
51 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants