Skip to content

Commit

Permalink
Merge pull request #333 from systemaccounting/332-crates-cognitoidp
Browse files Browse the repository at this point in the history
332 crates/cognitoidp
  • Loading branch information
mxfactorial committed Mar 13, 2024
2 parents e096a8c + b5ea5e9 commit be2f8bc
Show file tree
Hide file tree
Showing 17 changed files with 634 additions and 1,465 deletions.
92 changes: 77 additions & 15 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
resolver = "2"

members = [
"crates/cognitoidp",
"crates/httpclient",
"crates/pg",
"crates/service",
Expand Down
12 changes: 12 additions & 0 deletions crates/cognitoidp/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "cognitoidp"
version = "0.1.0"
edition = "2021"
rust-version.workspace = true

[dependencies]
jsonwebtoken = "9.2.0"
reqwest = { version = "0.11.25", features = ["json"] }
serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.114"
thiserror = "1.0.57"
71 changes: 71 additions & 0 deletions crates/cognitoidp/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use jsonwebtoken::{
decode, errors,
jwk::{Jwk, JwkSet},
Algorithm, DecodingKey, Validation,
};
use serde::Deserialize;
use thiserror::Error;

#[derive(Debug, Error)]
pub enum IdpError {
#[error("zero json web keys returned from cognito")]
ZeroJsonWebKeysReturned,
#[error(transparent)]
RequestError(#[from] reqwest::Error),
#[error("json web key not found in cognito jwk set")]
JsonWebKeyNotFound,
#[error(transparent)]
DecodingKey(#[from] errors::Error),
#[error("api auth disabled")]
ApiAuthDisabled,
}

#[derive(Deserialize, Debug)]
pub struct CognitoClaims {
// https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-id-token.html
#[serde(rename = "cognito:username")]
cognito_username: String,
}

#[derive(Deserialize, Debug)]
pub struct CognitoJwkSet(JwkSet);

impl CognitoJwkSet {
pub async fn new(cognito_jwks_uri: &str) -> Result<Self, IdpError> {
let response = reqwest::get(cognito_jwks_uri)
.await
.map_err(IdpError::RequestError)?;
let jwks = response.json::<JwkSet>().await.unwrap();
if jwks.keys.is_empty() {
return Err(IdpError::ZeroJsonWebKeysReturned);
}
Ok(Self(jwks))
}

pub fn match_jwk(&self, claimed_key_id: &str) -> Option<&Jwk> {
self.0.find(claimed_key_id)
}

pub fn pub_key(&self, claimed_key_id: &str) -> Result<DecodingKey, IdpError> {
let jwk = self
.match_jwk(claimed_key_id)
.ok_or(IdpError::JsonWebKeyNotFound)?;
DecodingKey::from_jwk(jwk).map_err(|_| IdpError::JsonWebKeyNotFound)
}

pub fn test_token(&self, token: &str) -> Result<CognitoClaims, IdpError> {
let header = jsonwebtoken::decode_header(token).map_err(IdpError::DecodingKey)?;
let jwk = self
.match_jwk(&header.kid.unwrap())
.ok_or(IdpError::JsonWebKeyNotFound)?;
let key = DecodingKey::from_jwk(jwk).map_err(|_| IdpError::JsonWebKeyNotFound)?;
// https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html#amazon-cognito-user-pools-using-tokens-step-2
let claims = decode::<CognitoClaims>(token, &key, &Validation::new(Algorithm::RS256))
.map_err(IdpError::DecodingKey)?;
Ok(claims.claims)
}

pub fn cognito_user(&self, token: &str) -> Result<String, IdpError> {
self.test_token(token).map(|claims| claims.cognito_username)
}
}
1 change: 0 additions & 1 deletion crates/pg/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ edition = "2021"
bb8 = "0.8.0"
bb8-postgres = "0.8.1"
tokio = "1.26.0"
async-trait = "0.1.73"
tokio-postgres = { version = "0.7.7", features = [
"with-chrono-0_4",
"with-geo-types-0_7",
Expand Down
Loading

0 comments on commit be2f8bc

Please sign in to comment.