Hey!
I am trying to subscribe to the level2 websocket feed using a JWT from a Rust websocket client.
There is a similar question for Legacy based auth, but I felt it is best to build it using JWT to be as up to date as possible.
I have tried converting the python code JWT signing example into a Rust implementation.
Whilst I am able to genereate the JWT successfully, I appear to be getting an authentication error after sending my message and I am not sure why.
Here is my JWT generation code (mostly using the jsonwebtoken
and openssl
libraries):
pub fn generate_jwt(&self, timestamp: &u64) -> String {
debug!("Generating JWT for Websocket authentication");
let service = "public_websocket_api";
let key_name = &self.name;
let expiration = timestamp + 60;
let private_key = openssl::pkey::PKey::private_key_from_pem(&self.privateKey.as_bytes()).unwrap();
let private_key_pem = private_key.private_key_to_pem_pkcs8().unwrap();
let claims = CoinbaseClaims {
sub: key_name.to_owned(),
iss: "coinbase-cloud".to_owned(),
nbf: timestamp.to_string().to_owned(),
exp: expiration.to_string().to_owned(),
aud: vec![service.to_string()],
};
let header = Header {
typ: Some("JWT".to_string()),
alg: Algorithm::ES256,
cty: None,
jku: None,
jwk: None,
kid: Some(key_name.to_string()),
x5u: None,
x5c: None,
x5t: None,
x5t_s256: None,
};
let jwt = encode(
&header,
&claims,
&EncodingKey::from_ec_pem(&private_key_pem).unwrap(),
)
.unwrap();
debug!("Generated JWT: {:?}", jwt);
jwt
}
Note that in this case &self
is a struct
which is created based on the Trading API key .json
file via deserialization.
The message I send looks as follows:
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
let subscribe_message = format!(
r#"{{
"type": "subscribe",
"product_ids": ["{}"],
"channel": "level2",
"jwt": "{}",
"timestamp": "{}"
}}"#,
&pair,
&credentials.generate_jwt(×tamp),
×tamp.to_string()
);
What am I missing?
Also, there is no way to pass the nonce
into the Header
since this is not a field belonging to the jsonwebtoken
defined struct, but I doubt this is the issue. If it is I would need to add some more logic accordingly.