Biscuit Authentication in Fiber
Overview of Biscuit authentication and how it's integrated into Fiber's RPC system
Biscuit Authentication in Fiber
This document provides an overview of Biscuit authentication, how it's integrated into Fiber's RPC system, and how to use it.
1. What is Biscuit Authentication?
Biscuit is a modern authorization token format designed for distributed verification. It's similar in concept to other bearer tokens like JWTs (JSON Web Tokens).
2. How Biscuit Auth Works in Fiber RPC
Biscuit is integrated into the Fiber RPC, it can be disabled when RPC is listen to private addr, but must be enabled when listen to public address. When enabled, it protects RPC endpoints by requiring a valid Biscuit token with permission for resources.
Here's a step-by-step breakdown of the process:
-
Client Request: The client sends an RPC request and includes the Biscuit token in the
AuthorizationHTTP header, formatted as aBearertoken. The token itself is Base64-encoded. -
Middleware Interception: On the server, the
BiscuitAuthMiddlewareintercepts every incoming RPC request before it reaches the actual method handler. -
Token Extraction and Verification: The middleware extracts the Base64-encoded token from the header, decodes it, and verifies its signature using the server's configured public key. If the signature is invalid, the request is immediately rejected.
-
Rule-based Authorization: If the signature is valid, the middleware proceeds to the authorization step.
- Fiber maintains a predefined set of authorization rules for each RPC method. For example, the
open_channelmethod requires a token with thewrite("channels")permission. - The middleware builds a Biscuit Authorizer. This authorizer is loaded with the rule corresponding to the RPC method being called.
- It also adds contextual facts to the authorizer, such as the current time (e.g.,
time(2023-10-27T10:00:00Z)) and any parameters from the request. This allows for policies that depend on time or specific request data.
- Fiber maintains a predefined set of authorization rules for each RPC method. For example, the
-
Policy Execution: The authorizer then evaluates the token against the loaded rules and facts. It checks if the permissions granted in the token (the "authority" block and any attenuated blocks) satisfy the requirements of the RPC method's policy.
-
Access Control:
- If the authorization check passes, the middleware forwards the request to the intended RPC method, and the operation proceeds.
- If the check fails (either due to insufficient permissions or a failed check like an expired timestamp), the middleware rejects the request with an "Unauthorized" error.
This entire process happens transparently for the RPC methods themselves. The logic is neatly contained within the middleware, ensuring that security is applied consistently across all protected endpoints.
The current rules for each RPC methods:
// Cch
rule("send_btc", r#"allow if write("cch");"#);
rule("receive_btc", r#"allow if read("cch");"#);
rule("get_cch_order", r#"allow if read("cch");"#);
// channels
rule("open_channel", r#"allow if write("channels");"#);
rule("accept_channel", r#"allow if write("channels");"#);
rule("abandon_channel", r#"allow if write("channels");"#);
rule("list_channels", r#"allow if read("channels");"#);
rule("shutdown_channel", r#"allow if write("channels");"#);
rule("update_channel", r#"allow if write("channels");"#);
// dev
rule("commitment_signed", r#"allow if write("messages");"#);
rule("add_tlc", r#"allow if write("channels");"#);
rule("remove_tlc", r#"allow if write("channels");"#);
rule(
"check_channel_shutdown",
r#"allow if write("channels");"#,
);
rule(
"submit_commitment_transaction",
r#"allow if write("chain");"#,
);
// graph
rule("graph_nodes", r#"allow if read("graph");"#);
rule("graph_channels", r#"allow if read("graph");"#);
// info
rule("node_info", r#"allow if read("node");"#);
rule("new_invoice", r#"allow if write("invoices");"#);
rule("parse_invoice", r#"allow if read("invoices");"#);
rule("get_invoice", r#"allow if read("invoices");"#);
rule("cancel_invoice", r#"allow if write("invoices");"#);
rule("settle_invoice", r#"allow if write("invoices");"#);
// payment
rule("send_payment", r#"allow if write("payments");"#);
rule("get_payment", r#"allow if read("payments");"#);
rule("build_router", r#"allow if read("payments");"#);
rule("send_payment_with_router", r#"allow if write("payments");"#);
// peer
rule("connect_peer", r#"allow if write("peers");"#);
rule("disconnect_peer", r#"allow if write("peers");"#);
rule("list_peers", r#"allow if read("peers");"#);
// watchtower
rule(
"create_watch_channel",
r#"
allow if write("watchtower");
allow if right({channel_id}, "watchtower");
"#,
);
rule(
"remove_watch_channel",
r#"
allow if write("watchtower");
allow if right({channel_id}, "watchtower");
"#,
);
rule(
"update_revocation",
r#"
allow if write("watchtower");
allow if right({channel_id}, "watchtower");
"#,
);
rule(
"update_local_settlement",
r#"
allow if write("watchtower");
allow if right({channel_id}, "watchtower");
"#,
);
rule("create_preimage", r#"allow if write("watchtower");"#);
rule("remove_preimage", r#"allow if write("watchtower");"#); 3. How to Configure Biscuit Auth in Fiber
Enabling Biscuit authentication on a Fiber node is a two-step process: first, you generate a cryptographic key pair, and second, you provide the public key to the Fiber server. This public key is used to verify the signatures of incoming Biscuit tokens, and its presence in the configuration automatically activates the authentication middleware.
a. Generate a Key Pair
To sign and verify tokens, you need an Ed25519 key pair. You can generate one using the biscuit-cli tool.
Please refer to the official Biscuit documentation for usage.
# make sure you use 0.6.0 version
cargo install biscuit-cli --vers 0.6.0-beta.2biscuit keypairThis command will output a private and a public key.
Generating a new random keypair
Private key: ed25519-private/89d6c88919e5ca326fbb8d1cbef406df08c0620575376651d53008762dc81f45
Public key: ed25519/17b172749be74276f0ed35a5d0685752684a3c5722114bba447a2f301136db79Important: The private key is a secret and should be stored securely. It is used to sign and create new authorization tokens. The public key is what you will use to configure the Fiber server.
b. Configure the Public Key
You can provide the public key to your Fiber node in one of the following ways:
Through the Configuration File
Add the public key to your Fiber configuration file (e.g., config.yml) under the rpc section:
rpc:
# ... other rpc settings
biscuit_public_key: "ed25519/17b172749be74276f0ed35a5d0685752684a3c5722114bba447a2f301136db79" # Your ed25519 public key stringUsing Command-Line Arguments
When starting the fiber-bin executable, you can pass the public key as a command-line argument:
fiber-bin --rpc-biscuit-public-key "ed25519/17b172749be74276f0ed35a5d0685752684a3c5722114bba447a2f301136db79"Via Environment Variables
You can also configure it using an environment variable:
export RPC_BISCUIT_PUBLIC_KEY="ed25519/17b172749be74276f0ed35a5d0685752684a3c5722114bba447a2f301136db79"
fiber-binIf biscuit_public_key is not set, the RPC server will not require authentication. For security, Fiber will refuse to start on a public IP address if authentication is not enabled.
4. How to Sign an Auth Token
Once your server is configured for authentication, you need to create signed Biscuit tokens to access its RPC endpoints. This is done using the private key generated by biscuit-cli.
a. Define Permissions
First, create a file (e.g., permissions.bc) to define the permissions for the token. These permissions are expressed as Datalog facts and checks, find details in the biscuit documentation.
For example, to grant read access to peers, write access to payments, and add an expiration date:
// Grant permissions for specific modules
read("peers");
write("payments");
// You can also add checks, like an expiration date.
// This check ensures the token is only valid before the specified UTC timestamp.
check if time($time), $time <= 2025-01-01T00:00:00Z;b. Generate the Token
Use the biscuit generate command with your private key and the permissions file to create the token.
biscuit generate --private-key ed25519-private/89d6c88919e5ca326fbb8d1cbef406df08c0620575376651d53008762dc81f45 permissions.bcThe command will output a long Base64-encoded string. This is your bearer token.
ErsBClEKBXBlZXJzCghwYXltZW50cxgDIgkKBwgAEgMYgAgiCQoHCAESAxiBCDImCiQKAggbEgYIBRICCAUaFgoECgIIBQoICgYggIvSuwYKBBoCCAISJAgAEiDAjoKKNTZpA61ImgoD5Q2sSbBjA3ixK1R2M65a2TOxbRpA4SF04LS5zD6OempqQObA2TTlTCANI7bZkDpl7JIecjR59GFoNtyKYak-_jXq2gDUHadj2I8SbJ0N-vN1RPslDSIiCiBR7VZ7ZJaPE4nhxUUpgpd5MXr3hqi0RfNERy3NE4KjaQ==c. Using the Token
You can now use this token in the Authorization header when making RPC calls to your Fiber node:
Authorization: Bearer ErsBClEKBXBlZXJzCghwYXltZW50cxgDIgkKBwgAEgMYgAgiCQoHCAESAxiBCDImCiQKAggbEgYIBRICCAUaFgoECgIIBQoICgYggIvSuwYKBBoCCAISJAgAEiDAjoKKNTZpA61ImgoD5Q2sSbBjA3ixK1R2M65a2TOxbRpA4SF04LS5zD6OempqQObA2TTlTCANI7bZkDpl7JIecjR59GFoNtyKYak-_jXq2gDUHadj2I8SbJ0N-vN1RPslDSIiCiBR7VZ7ZJaPE4nhxUUpgpd5MXr3hqi0RfNERy3NE4KjaQ==This token grants exactly the permissions you defined and will be successfully verified by a Fiber server configured with the corresponding public key.
5. Test Examples
The Fiber codebase contains numerous tests that demonstrate the usage of Biscuit tokens. These are excellent resources for understanding the system.
a. RPC Integration Tests
The file crates/fiber-lib/src/fiber/tests/rpc.rs contains integration-style tests for the RPC interface. Key examples include:
test_rpc_basic_with_auth: Shows how to make multiple authenticated RPC calls with a single token.test_rpc_auth_without_token: Demonstrates that an unauthenticated request to a protected endpoint is rejected.test_rpc_auth_with_invalid_token: Tests that a token signed by an incorrect private key is rejected.test_rpc_auth_with_wrong_permission: Shows that a valid token with insufficient permissions is correctly denied access.
Link to file: crates/fiber-lib/src/fiber/tests/rpc.rs
b. Unit Tests for Authorization Logic
The file crates/fiber-lib/src/rpc/biscuit.rs contains unit tests that focus specifically on the token authorization logic. These are useful for seeing how different kinds of Datalog rules are handled.
test_biscuit_auth: Basic tests for checkingreadandwritepermissions.test_biscuit_auth_channel: A more advanced example of granting permissions based on request parameters (in this case, achannel_id).test_biscuit_token_timeout: Demonstrates how to create and verify a token with an expiration date.