From a9b0e2f49a5ae011dd879ecdd25c5ffea55ba28e Mon Sep 17 00:00:00 2001 From: Paul-Christian Volkmer Date: Fri, 22 Dec 2023 04:51:27 +0100 Subject: [PATCH] feat: extract user ID from client token --- Cargo.toml | 1 + src/client.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 6f2f574..390246e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ serde_json = "1.0" tokio = { version = "1.35", features = ["rt-multi-thread", "macros"], default-features = false } tokio-tungstenite = { version = "0.21", features = ["connect", "rustls-tls-webpki-roots"], default-features = false } url = "2.5" +base64 = "0.21" [profile.release] opt-level = "s" diff --git a/src/client.rs b/src/client.rs index 9cf0329..86e14b4 100644 --- a/src/client.rs +++ b/src/client.rs @@ -22,9 +22,12 @@ use std::fmt::{Display, Formatter}; use std::marker::PhantomData; use std::time::Duration; +use base64::engine::general_purpose::STANDARD_NO_PAD; +use base64::Engine; use futures_util::{SinkExt, StreamExt}; use reqwest::{IntoUrl, StatusCode}; use serde::Deserialize; +use serde_json::json; use tokio::join; use tokio::sync::mpsc::Sender; use tokio_tungstenite::connect_async; @@ -41,6 +44,11 @@ struct LoginResponse { token: String, } +#[derive(Deserialize, Debug)] +struct TokenClaim { + sub: String, +} + #[derive(Deserialize, Debug)] struct MembershipResponse { #[serde(rename = "id")] @@ -252,6 +260,27 @@ impl Client { } impl Client { + /// Get user ID extracted from client token + /// + /// This method fails if the token cannot be parsed + fn get_user_id(&self) -> Result { + let token = self.token.clone().unwrap_or_default(); + let mut token_parts = token.split('.'); + + match token_parts.nth(1) { + None => Err(ParserError("Unparsable token".into())), + Some(part) => match STANDARD_NO_PAD.decode(part) { + Ok(d) => match serde_json::from_str::( + &String::from_utf8(d).unwrap_or_default(), + ) { + Ok(claim) => Ok(claim.sub), + Err(err) => Err(ParserError(format!("Unparsable token claim: {}", err))), + }, + Err(err) => Err(ParserError(format!("Unparsable token: {}", err))), + }, + } + } + /// Logout the client and discard existing token if not logged in /// /// If successful the result will be of type `Client`