feat!: add dynamic authorization support#194
feat!: add dynamic authorization support#194EddieHouston wants to merge 4 commits intobitcoindevkit:masterfrom
Conversation
7e9324b to
db94491
Compare
|
concept ACK Thanks for this, adding an "authorization" header is the most common way to authenticate to a http endpoints so this should be generally useful. Appreciate the comprehensive examples and unit tests. The team is spread a bit thin so it may take some time to get it reviewed and tested. |
oleonardolima
left a comment
There was a problem hiding this comment.
cACK
I did an initial round of review. Overall, it looks good! I'll give it a try testing it.
|
@EddieHouston needs rebase |
@oleonardolima Was this still planned to release before the Electrum protocol v1.6 changes? |
I'm planning to have both under the same release, as the version negotiation is should work fine for new v1.6 and old servers. Appreciate it, if you could do a rebase. |
|
Also, you could use the opportunity to squash into a single commit, as it touches the same files. |
db94491 to
3781f4d
Compare
This comment was marked as outdated.
This comment was marked as outdated.
3781f4d to
ca27c4a
Compare
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
ca27c4a to
3590cea
Compare
@EddieHouston how hard would it be run this in regtest on CI? Since we'll probably move some stuff around for the next release, would be nice to be able to test it out there and catch any regressions. |
|
@EddieHouston can you also provide complete instructions on testing this with your servers? |
@luisschwab You can probably start from this: oleonardolima@4f6bc16 I think that having it as a full example is better than have it all in just docs, will update to the latest changes and probably push that commit, wdyt @EddieHouston |
|
@oleonardolima go ahead. While you're at it, can you squash the last 3 commits and fix formatting? |
In theory not difficult, just time. You could maybe mock up a test server that takes an API key or something. |
|
I cherry-picked my changes for the examples and squashed everything into a single commit. I changed the Adding a regtest auth infra for testing in CI can be done in a follow-up. |
oleonardolima
left a comment
There was a problem hiding this comment.
tACK 1c7ad73
I've tested using this version of the electrum-client for FullScan/Sync and broadcasting with a bdk_wallet on Testnet3. I did it for both unauthenticated and authenticated servers, and it's working as expected.
evanlinjin
left a comment
There was a problem hiding this comment.
Thanks for this work. This is only a partial review.
src/raw_client.rs
Outdated
| params.to_vec(), | ||
| ); | ||
|
|
||
| // Servers expect auth token only in the first request in batched requests |
There was a problem hiding this comment.
Unfortunately this library doesn't actually use JSON-RPC batch arrays - the "batch" here just means "write all in one go".
We need to add auth token to all requests.
There was a problem hiding this comment.
@EddieHouston Are there any issues on the server side if we choose to send the authorization within every request ?
There was a problem hiding this comment.
@EddieHouston Are there any issues on the server side if we choose to send the authorization within every request ?
yeah, it's a huge amount of extra unneeded data over the wire.
There was a problem hiding this comment.
@EddieHouston Are there any issues on the server side if we choose to send the authorization within every request ?
yeah, it's a huge amount of extra unneeded data over the wire.
Oh, got it. However, we should probably follow with above's suggestion to guarantee general compatibility.
There was a problem hiding this comment.
I don't see the problem, the original code adds the auth only on the first request in the batch, even though its using newline-delimited JSON-RPC, they are all flushed to the wire together and it has auth only on the first one as expected.
There was a problem hiding this comment.
I think we can keep as it's for now, if leads to any performance issues we can change it and release in a patch release, as it'd be internal and non-breaking.
There was a problem hiding this comment.
I think we can keep as it's for now, if leads to any performance issues we can change it and release in a patch release, as it'd be internal and non-breaking.
I updated it to use this argument in favor of only adding in the first request, if there's any other user with this usecase, which require the auth token in every request we can do it in the future without a breaking change.
- add new `AuthProvider` public type to handle an `authorization` token. - add new `Option<AuthProvider>` as `authorization_provider` in `Config`, it holds the dynamic authentication token callback provider. - add new `Option<AuthProvider>` as `auth_provider` in `RawClient`, it holds the dynamic authentication token callback provider. - add new `authorization` private field in `Request`. - add new `pub fn with_auth()` to `RawClient` in order to set the authorization provider into the client, it should be used following a builder patter, the client defaults to no authentication otherwise. - add new `.with_auth()` method to `Request`, it sets the `authorization` token, if any. - update `pub fn negotiate_protocol_version()` in `RawClient` to return the `RawClient`, this way it SHOULD no be used following a builder pattern. - update `.call()` and `.batch_call()` to use the newly added `.with_auth()`, instead of updating request's authorization field directly. BREAKING CHANGE: all changes above are API breaking changes.
- update the `RawClient` methods to expect `auth_provider` as parameter. - update both `.with_auth()` and `.negotiate_protocol_version()` to private. - always call `.with_auth()` internally when creating the `RawClient`. - always call `.negotiate_protocol_version()` internally when creating the `RawClient`. It fixes the misleading API from the previous commit, the protocol version negotiation is a mandatory step in the protocol, and shouldn't be relied on the user reading the documentation that it MUST call the `.negotiate_protocol_version()` as previously stated, now it's always called internally.
- update the `batch_call` method to only add the `authorization` token in the first `Request` of the batch. As mentioned by the PR author, the batch calls are flushed together, therefore we can have this optimization here, although the library does not use JSON-RPC batch arrays.
- add two new examples: `jwt_auth.rs` and `jwt_dynamic_auth.rs` as references on how to build a client with authentication.
|
I added two new commits that addresses: (i) the protocol version negotiation being strictly called internally, instead of leaving it up to the user (as discussed with tnull in discord). (ii) the (iii) applying the authorization token strictly in the first request of a batch call, as mentioned by @EddieHouston here. I kept it as separate commits to make clear what the changes are. -- It's now ready again for a new round of review. |
Description
This PR adds support for dynamic authorization in Electrum RPC requests.
This enables authentication with authorization-protected Electrum servers and API gateways.
Use cases:
Notes to the reviewers
The implementation uses a callback pattern rather than a static token to support dynamic scenarios like automatic token refresh. The
AuthProvideris called for each RPC request, allowing the token to be updated without reconnecting the client.Both
ConfigandRawClientstructs needed customDebugimplementations since function pointers don't implementDebug- this is handled by displaying<provider>when an auth provider is present, which prevents leaking sensitive token values in debug output.Thread safety is ensured through
Arcwrapping andSend + Syncbounds, making this safe for concurrent use across the client.Tests added:
14 new tests, all passing
Changelog notice
Added: Dynamic authorization support via
AuthProvidercallback inConfigBuilder. Enables authentication with authorization-protected Electrum servers and automatic token refresh patterns for Bearer tokens, API keys, and other authorization schemes.Checklists
All Submissions:
New Features:
Bugfixes: