Skip to content

Commit 1ff9b07

Browse files
Merge #87
87: feat: add revocations to nilauth client r=mfontanini a=mfontanini This adds an endpoint to revoke a client and another one to check if a token is revoked. See the PR in nilauth for the other side of this. Co-authored-by: Matias Fontanini <[email protected]>
2 parents d1b86ec + e300371 commit 1ff9b07

File tree

1 file changed

+103
-6
lines changed

1 file changed

+103
-6
lines changed

libs/nilauth-client/src/client.rs

Lines changed: 103 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
use async_trait::async_trait;
22
use chrono::{DateTime, Utc};
33
use nillion_chain_client::{client::NillionChainClient, transactions::TokenAmount};
4-
use nillion_nucs::k256::{
5-
ecdsa::{signature::Signer, Signature, SigningKey},
6-
sha2::{Digest, Sha256},
7-
PublicKey, SecretKey,
4+
use nillion_nucs::{
5+
builder::{ExtendTokenError, NucTokenBuildError, NucTokenBuilder},
6+
envelope::{InvalidSignature, NucEnvelopeParseError, NucTokenEnvelope},
7+
k256::{
8+
ecdsa::{signature::Signer, Signature, SigningKey},
9+
sha2::{Digest, Sha256},
10+
PublicKey, SecretKey,
11+
},
12+
token::{Did, ProofHash, TokenBody},
813
};
914
use serde::{Deserialize, Serialize};
10-
use std::time::Duration;
15+
use serde_json::json;
16+
use std::{iter, time::Duration};
1117

1218
const TOKEN_REQUEST_EXPIRATION: Duration = Duration::from_secs(60);
1319
const REQUEST_TIMEOUT: Duration = Duration::from_secs(30);
@@ -30,6 +36,15 @@ pub trait NilauthClient {
3036

3137
/// Get the cost of a subscription.
3238
async fn subscription_cost(&self) -> Result<TokenAmount, SubscriptionCostError>;
39+
40+
/// Revoke a token.
41+
async fn revoke_token(&self, token: &NucTokenEnvelope, key: &SecretKey) -> Result<(), RevokeTokenError>;
42+
43+
/// Lookup whether a token is revoked.
44+
async fn lookup_revoked_tokens(
45+
&self,
46+
envelope: &NucTokenEnvelope,
47+
) -> Result<Vec<RevokedToken>, LookupRevokedTokensError>;
3348
}
3449

3550
/// An error when requesting a token.
@@ -77,13 +92,45 @@ pub enum SubscriptionCostError {
7792
Request(#[from] reqwest::Error),
7893
}
7994

95+
/// An error when revoking a token.
96+
#[derive(Debug, thiserror::Error)]
97+
pub enum RevokeTokenError {
98+
#[error("fetching server's about: {0}")]
99+
About(#[from] AboutError),
100+
101+
#[error("requesting token: {0}")]
102+
RequestToken(#[from] RequestTokenError),
103+
104+
#[error("malformed token returned from nilauth: {0}")]
105+
MalformedAuthToken(#[from] NucEnvelopeParseError),
106+
107+
#[error("invalid signatures in token returned from nilauth: {0}")]
108+
InvalidAuthTokenSignatures(#[from] InvalidSignature),
109+
110+
#[error("cannot extend token returned from nilauth: {0}")]
111+
AuthTokenNotDelegation(#[from] ExtendTokenError),
112+
113+
#[error("building invocation: {0}")]
114+
BuildInvocation(#[from] NucTokenBuildError),
115+
116+
#[error("request: {0}")]
117+
Request(#[from] reqwest::Error),
118+
}
119+
80120
/// An error when requesting the information about a nilauth instance.
81121
#[derive(Debug, thiserror::Error)]
82122
pub enum AboutError {
83123
#[error("request: {0}")]
84124
Request(#[from] reqwest::Error),
85125
}
86126

127+
/// An error when looking up revoked tokens.
128+
#[derive(Debug, thiserror::Error)]
129+
pub enum LookupRevokedTokensError {
130+
#[error("request: {0}")]
131+
Request(#[from] reqwest::Error),
132+
}
133+
87134
/// The default nilauth client that hits the actual service.
88135
pub struct DefaultNilauthClient {
89136
client: reqwest::Client,
@@ -154,9 +201,39 @@ impl NilauthClient for DefaultNilauthClient {
154201

155202
async fn subscription_cost(&self) -> Result<TokenAmount, SubscriptionCostError> {
156203
let url = self.make_url("/api/v1/payments/cost");
157-
let response: GetCostResponse = self.client.get(url).send().await?.json().await?;
204+
let response: GetCostResponse = self.client.get(url).send().await?.error_for_status()?.json().await?;
158205
Ok(TokenAmount::Unil(response.cost_unils))
159206
}
207+
208+
async fn revoke_token(&self, token: &NucTokenEnvelope, key: &SecretKey) -> Result<(), RevokeTokenError> {
209+
let about = self.about().await?;
210+
let token = token.encode();
211+
let auth_token = self.request_token(key).await?;
212+
let auth_token = NucTokenEnvelope::decode(&auth_token)?.validate_signatures()?;
213+
// SAFETY: this can't not be an object
214+
let args = json!({"token": token}).as_object().cloned().expect("not an object");
215+
let invocation = NucTokenBuilder::extending(auth_token)?
216+
.audience(Did::new(about.public_key))
217+
.body(TokenBody::Invocation(args))
218+
.command(["nuc", "revoke"])
219+
.build(&key.into())?;
220+
let header_value = format!("Bearer {invocation}");
221+
let url = self.make_url("/api/v1/revocations/revoke");
222+
self.client.post(url).header("Authorization", header_value).send().await?.error_for_status()?;
223+
Ok(())
224+
}
225+
226+
async fn lookup_revoked_tokens(
227+
&self,
228+
envelope: &NucTokenEnvelope,
229+
) -> Result<Vec<RevokedToken>, LookupRevokedTokensError> {
230+
let hashes = iter::once(envelope.token()).chain(envelope.proofs()).map(|t| t.compute_hash()).collect();
231+
let request = LookupRevokedTokensRequest { hashes };
232+
let url = self.make_url("/api/v1/revocations/lookup");
233+
let response: LookupRevokedTokensResponse =
234+
self.client.post(url).json(&request).send().await?.error_for_status()?.json().await?;
235+
Ok(response.revoked)
236+
}
160237
}
161238

162239
/// A transaction hash.
@@ -230,3 +307,23 @@ struct GetCostResponse {
230307
// The cost in unils.
231308
cost_unils: u64,
232309
}
310+
311+
#[derive(Serialize)]
312+
struct LookupRevokedTokensRequest {
313+
hashes: Vec<ProofHash>,
314+
}
315+
316+
#[derive(Deserialize)]
317+
struct LookupRevokedTokensResponse {
318+
revoked: Vec<RevokedToken>,
319+
}
320+
321+
/// A revoked token.
322+
#[derive(Clone, Debug, Deserialize, PartialEq)]
323+
pub struct RevokedToken {
324+
/// The token hash.
325+
pub token_hash: ProofHash,
326+
327+
/// The timestamp at which the token was revoked.
328+
pub revoked_at: DateTime<Utc>,
329+
}

0 commit comments

Comments
 (0)