Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed client id not being client id, used references instead of owned strings and added static cache for apple keys #3

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ serde_json = "1.0"
hyper = { version = "0.14", features = ["http1"] }
hyper-tls = "0.5"
tokio = { version = "1", features = ["rt-multi-thread","net","macros"] }
thiserror = "1.0"
thiserror = "1.0"
lazy_static = "1.4.0"
async_once = "0.2.6"
56 changes: 36 additions & 20 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,23 @@ use jsonwebtoken::{
};
use serde::de::DeserializeOwned;
use std::collections::HashMap;
use lazy_static::lazy_static;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a totally unrelated change to the mix up of aud. lets split this off into a separate PR, I would like to argue about the API of this there then

use async_once::AsyncOnce;
use tokio::time::sleep;
use core::time::Duration;

lazy_static! {
static ref APPLE_KEYS : AsyncOnce<HashMap<String, KeyComponents>> = AsyncOnce::new(async {
loop {
if let Ok(keys) = fetch_apple_keys().await {
break keys;
} else {
sleep(Duration::from_secs(1)).await
}
}
});
}

//TODO: put verification into a struct and only fetch apple keys once in the beginning
async fn fetch_apple_keys() -> Result<HashMap<String, KeyComponents>>
{
let https = HttpsConnector::new();
Expand All @@ -51,17 +66,17 @@ async fn fetch_apple_keys() -> Result<HashMap<String, KeyComponents>>

/// decoe token with optional expiry validation
pub async fn decode_token<T: DeserializeOwned>(
token: String,
token: &str,
ignore_expire: bool,
) -> Result<TokenData<T>> {
let header = decode_header(token.as_str())?;
let header = decode_header(token)?;

let kid = match header.kid {
Some(k) => k,
None => return Err(Error::KidNotFound),
};

let pubkeys = fetch_apple_keys().await?;
let pubkeys = APPLE_KEYS.get().await;

let pubkey = match pubkeys.get(&kid) {
Some(key) => key,
Expand All @@ -71,7 +86,7 @@ pub async fn decode_token<T: DeserializeOwned>(
let mut val = Validation::new(header.alg);
val.validate_exp = !ignore_expire;
let token_data = decode::<T>(
token.as_str(),
token,
&DecodingKey::from_rsa_components(&pubkey.n, &pubkey.e)
.unwrap(),
&val,
Expand All @@ -81,8 +96,8 @@ pub async fn decode_token<T: DeserializeOwned>(
}

pub async fn validate(
client_id: String,
token: String,
client_id: &str,
token: &str,
ignore_expire: bool,
) -> Result<TokenData<Claims>> {
let token_data =
Expand All @@ -93,7 +108,7 @@ pub async fn validate(
return Err(Error::IssClaimMismatch);
}

if token_data.claims.sub != client_id {
if token_data.claims.aud != client_id {
return Err(Error::ClientIdMismatch);
}
Ok(token_data)
Expand Down Expand Up @@ -125,14 +140,15 @@ mod tests {
async fn validate_test() -> std::result::Result<(), Error> {
let user_token =
"001026.16112b36378440d995af22b268f00984.1744";
let client_id = "com.gameroasters.stack4";
let token = "eyJraWQiOiJZdXlYb1kiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmdhbWVyb2FzdGVycy5zdGFjazQiLCJleHAiOjE2MTQ1MTc1OTQsImlhdCI6MTYxNDQzMTE5NCwic3ViIjoiMDAxMDI2LjE2MTEyYjM2Mzc4NDQwZDk5NWFmMjJiMjY4ZjAwOTg0LjE3NDQiLCJjX2hhc2giOiJNNVVDdW5GdTFKNjdhdVE2LXEta093IiwiZW1haWwiOiJ6ZGZ1N2p0dXVzQHByaXZhdGVyZWxheS5hcHBsZWlkLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjoidHJ1ZSIsImlzX3ByaXZhdGVfZW1haWwiOiJ0cnVlIiwiYXV0aF90aW1lIjoxNjE0NDMxMTk0LCJub25jZV9zdXBwb3J0ZWQiOnRydWV9.GuMJfVbnEvqppwwHFZjn3GDJtB4c4rl7C4PZzyDsdyiuXcFcXq52Ti0WSJBsqtfyT2dXvYxVxebHtONSQha_9DiM5qfYTZbpDDlIXrOMy1fkfStocold_wHWavofIpoJQVUMj45HLHtjixiNE903Pho6eY2UjEUjB3aFe8txuFIMv2JsaMCYzG4-e632FKBn63SroCkLc-8b4EVV4iYqnC5AfZArXhVjUevhhlaBH0E8Az2OGEe74U2WgBvMXEilmd62Ek-uInnrpJRgYQfYXvehQ1yT3aMiIgJICTQFMDdL1KAvs6mc081lNJLFYvViWlMH-Y7E5ajtUiMApiNYsg";

let result =
validate(user_token.to_string(), token.to_string(), true)
validate(client_id, token, true)
.await?;

assert_eq!(result.claims.sub, user_token);
assert_eq!(result.claims.aud, "com.gameroasters.stack4");
assert_eq!(result.claims.aud, client_id);

Ok(())
}
Expand All @@ -142,24 +158,24 @@ mod tests {
let token = "eyJraWQiOiJlWGF1bm1MIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmdhbWVyb2FzdGVycy5zdGFjazQiLCJleHAiOjE2MzA4Mjc4MzAsImlhdCI6MTYzMDc0MTQzMCwic3ViIjoiMDAxMDI2LjE2MTEyYjM2Mzc4NDQwZDk5NWFmMjJiMjY4ZjAwOTg0LjE3NDQiLCJjX2hhc2giOiI0QjZKWTU4TmstVUJsY3dMa2VLc2lnIiwiYXV0aF90aW1lIjoxNjMwNzQxNDMwLCJub25jZV9zdXBwb3J0ZWQiOnRydWV9.iW0xk__fPD0mlh9UU-vh9VnR8yekWq64sl5re5d7UmDJxb1Fzk1Kca-hkA_Ka1LhSmKADdFW0DYEZhckqh49DgFtFdx6hM9t7guK3yrvBglhF5LAyb8NR028npxioLTTIgP_aR6Bpy5AyLQrU-yYEx2WTPYV5ln9n8vW154gZKRyl2KBlj9fS11BL_X1UFbFrL21GG_iPbB4qt5ywwTPoJ-diGN5JQzP5fk4yU4e4YmHhxJrT0NTTux2mB3lGJLa6YN-JYe_BuVV9J-sg_2r_ugTOUp3xQpfntu8xgQrY5W0oPxAPM4sibNLsye2kgPYYxfRYowc0JIjOcOd_JHDbQ";

validate(
"001026.16112b36378440d995af22b268f00984.1744".into(),
token.to_string(),
"com.gameroasters.stack4",
token,
true,
)
.await
.unwrap();
.await
.unwrap();
}

#[tokio::test]
async fn validate_expired() {
let token = "eyJraWQiOiJlWGF1bm1MIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmdhbWVyb2FzdGVycy5zdGFjazQiLCJleHAiOjE2MzA4Mjc4MzAsImlhdCI6MTYzMDc0MTQzMCwic3ViIjoiMDAxMDI2LjE2MTEyYjM2Mzc4NDQwZDk5NWFmMjJiMjY4ZjAwOTg0LjE3NDQiLCJjX2hhc2giOiI0QjZKWTU4TmstVUJsY3dMa2VLc2lnIiwiYXV0aF90aW1lIjoxNjMwNzQxNDMwLCJub25jZV9zdXBwb3J0ZWQiOnRydWV9.iW0xk__fPD0mlh9UU-vh9VnR8yekWq64sl5re5d7UmDJxb1Fzk1Kca-hkA_Ka1LhSmKADdFW0DYEZhckqh49DgFtFdx6hM9t7guK3yrvBglhF5LAyb8NR028npxioLTTIgP_aR6Bpy5AyLQrU-yYEx2WTPYV5ln9n8vW154gZKRyl2KBlj9fS11BL_X1UFbFrL21GG_iPbB4qt5ywwTPoJ-diGN5JQzP5fk4yU4e4YmHhxJrT0NTTux2mB3lGJLa6YN-JYe_BuVV9J-sg_2r_ugTOUp3xQpfntu8xgQrY5W0oPxAPM4sibNLsye2kgPYYxfRYowc0JIjOcOd_JHDbQ";

let res = validate(
"001026.16112b36378440d995af22b268f00984.1744".into(),
token.to_string(),
"com.gameroasters.stack4",
token,
false,
)
.await;
.await;

assert!(is_expired(&res));
}
Expand All @@ -169,11 +185,11 @@ mod tests {
let token = "eyJraWQiOiJlWGF1bm1MIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmdhbWVyb2FzdGVycy5zdGFjazQiLCJleHAiOjE2MzAxNzE4MTIsImlhdCI6MTYzMDA4NTQxMiwianRpIjoiQjk0T2REMDNwRnNhWWFOLUZ0djdtQSIsImV2ZW50cyI6IntcInR5cGVcIjpcImVtYWlsLWRpc2FibGVkXCIsXCJzdWJcIjpcIjAwMTAyNi4xNjExMmIzNjM3ODQ0MGQ5OTVhZjIyYjI2OGYwMDk4NC4xNzQ0XCIsXCJldmVudF90aW1lXCI6MTYzMDA4NTQwMzY0OCxcImVtYWlsXCI6XCJ6ZGZ1N2p0dXVzQHByaXZhdGVyZWxheS5hcHBsZWlkLmNvbVwiLFwiaXNfcHJpdmF0ZV9lbWFpbFwiOlwidHJ1ZVwifSJ9.SSdUM88GHqrS0QXHtaehbPxLQkAB3s1-qzcy3i2iRoSCzDhA1Q3o_FhiCbqOsbiPDOQ9aA1Z8-oAz1p3-TMfHy6QdIs1vLxBmNTe5IazNJw_7wwDZG2nr-bsKPUQldE--tK1EUFXQqQxQbfjJJE0JFEwPib2rmnb-t0mRopKMx2wg3CUlI64BHI2O8giGCbWB7UbJs2BpcUuapVShCIR7Eqxy0_ud81CUDjKzZK2CcmSRGDIk8g9pRqOHmPUFMOrDjj6_hUR9mf-xCrCedoC9f05z_yKD026A4gWGFn4pxTP8-uDTRPxcONax_vnQHBUDigYi8HXuzWorTx2ORPjaw";

let result = decode_token::<ClaimsServer2Server>(
token.to_string(),
token,
true,
)
.await
.unwrap();
.await
.unwrap();

assert_eq!(result.claims.aud, "com.gameroasters.stack4");
assert_eq!(
Expand Down