use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use httpmock::MockServer;
use jwt_simple::prelude::*;
use rand::thread_rng;
use rsa::pkcs8::EncodePrivateKey;
use rsa::{traits::PublicKeyParts, RsaPrivateKey};
use serde::{Deserialize, Serialize};
use crate::Parser;
pub const KID: &str = "some-kid";
pub const CLIENT_ID: &str = "some-client-id";
pub const EMAIL: &str = "alex@kviring.com";
pub const SUB: &str = "11112222333344445555";
#[derive(Debug, Serialize, Deserialize)]
pub struct TokenClaims {
pub email: String,
}
impl TokenClaims {
pub fn new() -> JWTClaims<TokenClaims> {
let claims = TokenClaims {
email: EMAIL.to_string(),
};
Claims::with_custom_claims(claims, Duration::from_hours(2))
.with_audience(CLIENT_ID.to_string())
.with_issuer(format!("https://securetoken.google.com/{}", CLIENT_ID))
.with_subject(SUB.to_string())
}
pub fn new_expired() -> JWTClaims<TokenClaims> {
let mut claim = TokenClaims::new();
claim.expires_at = Some(Duration::from_secs(0));
claim
}
pub fn new_with_iss(iss: &str) -> JWTClaims<TokenClaims> {
TokenClaims::new().with_issuer(iss)
}
pub fn new_with_aud(aud: &str) -> JWTClaims<TokenClaims> {
TokenClaims::new().with_audience(aud)
}
}
impl Default for TokenClaims {
fn default() -> Self {
TokenClaims {
email: "".to_string(),
}
}
}
pub fn setup(claims: JWTClaims<TokenClaims>) -> (String, Parser, MockServer) {
let (token, server) = setup_public_key_server(claims);
(
token,
Parser::new_with_custom_cert_url(CLIENT_ID, server.url("/").as_str()),
server,
)
}
pub fn setup_public_key_server(claims: JWTClaims<TokenClaims>) -> (String, MockServer) {
let bits = 2048;
let private_key =
RsaPrivateKey::new(&mut thread_rng(), bits).expect("failed to generate a key");
let pem = private_key
.to_pkcs8_pem(rsa::pkcs8::LineEnding::LF)
.unwrap();
let key = RS256KeyPair::from_pem(pem.as_str())
.unwrap()
.with_key_id(KID);
let token = key.sign(claims).unwrap();
let n = URL_SAFE_NO_PAD.encode(private_key.n().to_bytes_be());
let e = URL_SAFE_NO_PAD.encode(private_key.e().to_bytes_be());
let resp = format!("{{\"keys\": [{{\"kty\": \"RSA\",\"use\": \"sig\",\"e\": \"{}\",\"n\": \"{}\",\"alg\": \"RS256\",\"kid\": \"{}\"}}]}}", e, n, KID);
let server = MockServer::start();
server.mock(|when, then| {
when.method(httpmock::Method::GET).path("/");
then.status(200)
.header(
"cache-control",
"public, max-age=24920, must-revalidate, no-transform",
)
.header("Content-Type", "application/json; charset=UTF-8")
.body(resp);
});
(token, server)
}