diff --git a/docker-compose.yml b/docker-compose.yml index efed188..0d4bd3b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,3 +3,13 @@ services: image: ghcr.io/navikt/mock-oauth2-server:2.1.10 ports: - "8080:8080" + environment: + JSON_CONFIG: | + { + "tokenProvider" : { + "keyProvider" : { + "initialKeys" : "{\"p\":\"_LNnIjBshCrFuxtjUC2KKzg_NTVv26UZh5j12_9r5mYTxb8yW047jOYFEGvIdMkTRLGOBig6fLWzgd62lnLainzV35J6K6zr4jQfTldLondlkldMR6nQrp1KfnNUuRbKvzpNKkhl12-f1l91l0tCx3s4blztvWgdzN2xBfvWV68\",\"kty\":\"RSA\",\"q\":\"9MIWsbIA3WjiR_Ful5FM8NCgb6JdS2D6ySHVepoNI-iAPilcltF_J2orjfLqAxeztTskPi45wtF_-eV4GIYSzvMo-gFiXLMrvEa7WaWizMi_7Bu9tEk3m_f3IDLN9lwULYoebkDbiXx6GOiuj0VkuKz8ckYFNKLCMP9QRLFff-0\",\"d\":\"J6UX848X8tNz-09PFvcFDUVqak32GXzoPjnuDjBsxNUvG7LxenLmM_i8tvYl0EW9Ztn4AiCqJUoHw5cX3jz_mSqGl7ciaDedpKm_AetcZwHiEuT1EpSKRPMmOMQSqcJqXrdbbWB8gdUrnTKZIlJCfj7yqgT16ypC43TnwjA0UwxhG5pHaYjKI3pPdoHg2BzA-iubHjVn15Sz7-pnjBmeGDbEFa7ADY-1yPHCmqqvPKTNhoCNW6RpG34Id9hXslPa3X-7pAhJrDBd0_NPlktSA2rUkifYiZURhHR5ijhe0v3uw6kYP8f_foVm_C8O1ExkxXh9Dg8KDZ89dbsSOtBc0Q\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"l7C_WJgbZ_6e59vPrFETAehX7Dsp7fIyvSV4XhotsGs\",\"qi\":\"cQFN5q5WhYkzgd1RS0rGqvpX1AkmZMrLv2MW04gSfu0dDwpbsSAu8EUCQW9oA4pr6V7R9CBSu9kdN2iY5SR-hZvEad5nDKPV1F3TMQYv5KpRiS_0XhfV5PcolUJVO_4p3h8d-mo2hh1Sw2fairAKOzvnwJCQ6DFkiY7H1cqwA54\",\"dp\":\"YTql9AGtvyy158gh7jeXcgmySEbHQzvDFulDr-IXIg8kjHGEbp0rTIs0Z50RA95aC5RFkRjpaBKBfvaySjDm5WIi6GLzntpp6B8l7H6qG1jVO_la4Df2kzjx8LVvY8fhOrKz_hDdHodUeKdCF3RdvWMr00ruLnJhBPJHqoW7cwE\",\"alg\":\"RS256\",\"dq\":\"IZA4AngRbEtEtG7kJn6zWVaSmZxfRMXwvgIYvy4-3Qy2AVA0tS3XTPVfMaD8_B2U9CY_CxPVseR-sysHc_12uNBZbycfcOzU84WTjXCMSZ7BysPnGMDtkkLHra-p1L29upz1HVNhh5H9QEswHM98R2LZX2ZAsn4bORLZ1AGqweU\",\"n\":\"8ZqUp5Cs90XpNn8tJBdUUxdGH4bjqKjFj8lyB3x50RpTuECuwzX1NpVqyFENDiEtMja5fdmJl6SErjnhj6kbhcmfmFibANuG-0WlV5yMysdSbocd75C1JQbiPdpHdXrijmVFMfDnoZTQ-ErNsqqngTNkn5SXBcPenli6Cf9MTSchZuh_qFj_B7Fp3CWKehTiyBcLlNOIjYsXX8WQjZkWKGpQ23AWjZulngWRektLcRWuEKTWaRBtbAr3XAfSmcqTICrebaD3IMWKHDtvzHAt_pt4wnZ06clgeO2Wbc980usnpsF7g8k9p81RcbS4JEZmuuA9NCmOmbyADXwgA9_-Aw\"}", + "algorithm" : "RS256" + } + } + } diff --git a/src/app.rs b/src/app.rs index dfbd412..285cc3a 100644 --- a/src/app.rs +++ b/src/app.rs @@ -69,8 +69,6 @@ mod tests { use crate::app::App; use crate::config::Config; use crate::identity_provider::{ErrorResponse, IdentityProvider, IntrospectRequest, IntrospectResponse, OAuthErrorCode, TokenExchangeRequest, TokenRequest, TokenResponse}; - use axum::routing::get; - use axum::{Json, Router}; use jsonwebkey as jwk; use jsonwebtoken as jwt; use log::{info, LevelFilter}; @@ -81,6 +79,7 @@ mod tests { use std::collections::HashMap; use std::fmt::Debug; use testcontainers::{ContainerAsync, GenericImage}; + use crate::claims::epoch_now_secs; /// Test a full round-trip of the `/token`, `/token/exchange`, and `/introspect` endpoints. /// @@ -101,6 +100,7 @@ mod tests { let join_handler = tokio::spawn(async move { testapp.app.run().await.unwrap(); }); + let identity_provider_address = format!("{}:{}", testapp.docker.host.clone(), testapp.docker.port); for format in [RequestFormat::Form, RequestFormat::Json] { machine_to_machine_token( @@ -123,7 +123,7 @@ mod tests { testapp.cfg.azure_ad_issuer.clone(), testapp.cfg.azure_ad_client_id.clone(), address.to_string(), - format!("{}:{}", testapp.docker.host.clone(), testapp.docker.port), + identity_provider_address.to_string(), IdentityProvider::AzureAD, format.clone(), ).await; @@ -132,7 +132,7 @@ mod tests { testapp.cfg.token_x_issuer.clone(), testapp.cfg.token_x_client_id.clone(), address.to_string(), - format!("{}:{}", testapp.docker.host.clone(), testapp.docker.port), + identity_provider_address.to_string(), IdentityProvider::TokenX, format, ).await; @@ -144,9 +144,13 @@ mod tests { test_introspect_token_is_not_a_jwt(&address).await; test_introspect_token_missing_issuer(&address).await; test_introspect_token_unrecognized_issuer(&address).await; - test_introspect_token_missing_kid(&address).await; - test_introspect_token_missing_key_in_jwks(&address).await; - test_introspect_token_invalid_timestamps(&address).await; + test_introspect_token_missing_kid(&address, &identity_provider_address).await; + test_introspect_token_missing_key_in_jwks(&address, &identity_provider_address).await; + test_introspect_token_is_expired(&address, &identity_provider_address).await; + + // FIXME: these tests don't fail as expected; validation crate not working? + //test_introspect_token_is_issued_in_the_future(&address, &identity_provider_address).await; + //test_introspect_token_has_not_before_in_the_future(&address, &identity_provider_address).await; // TODO: implement these tests: // * /token @@ -181,7 +185,7 @@ mod tests { } async fn test_introspect_token_unrecognized_issuer(address: &str) { - let token = TokenServer::token( + let token = Token::sign( TokenClaims::from([ ("iss".into(), Value::String("snafu".into())), ]) @@ -197,7 +201,7 @@ mod tests { } async fn test_introspect_token_missing_issuer(address: &str) { - let token = TokenServer::token(TokenClaims::new()); + let token = Token::sign(TokenClaims::new()); test_well_formed_json_request( &format!("http://{}/api/v1/introspect", address), IntrospectRequest { @@ -219,10 +223,10 @@ mod tests { ).await; } - async fn test_introspect_token_missing_kid(address: &str) { - let token = TokenServer::token( + async fn test_introspect_token_missing_kid(address: &str, identity_provider_address: &str) { + let token = Token::sign( TokenClaims::from([ - ("iss".into(), Value::String("http://localhost:8080/maskinporten".into())), + ("iss".into(), format!("http://{}/maskinporten", identity_provider_address).into()), ]) ); test_well_formed_json_request( @@ -235,10 +239,10 @@ mod tests { ).await; } - async fn test_introspect_token_missing_key_in_jwks(address: &str) { - let token = TokenServer::token_with_kid( + async fn test_introspect_token_missing_key_in_jwks(address: &str, identity_provider_address: &str) { + let token = Token::sign_with_kid( TokenClaims::from([ - ("iss".into(), Value::String("http://localhost:8080/maskinporten".into())), + ("iss".into(), format!("http://{}/maskinporten", identity_provider_address).into()), ]), "missing-key", ); @@ -252,97 +256,72 @@ mod tests { StatusCode::OK, ).await; } - // * [ ] invalid or expired timestamps in nbf, iat, exp - async fn test_introspect_token_invalid_timestamps(address: &str) { - - } - - async fn yolo() { - let server = TokenServer::new().await; - let address = server.address(); - let join_handler = tokio::spawn(async move { - server.run().await.unwrap(); - }); - - // Set up Texas - //let cfg = Config::mock(address, server.port()); - //let app = App::new(cfg.clone()).await; - - let client = reqwest::Client::new(); - let response = client.get(format!("http://{}/jwks", address)).send().await.unwrap(); - println!("{:?}", response.text().await.unwrap()); - join_handler.abort(); - } - - struct TokenServer { - listener: tokio::net::TcpListener, - keys: HashMap>, - } - - type TokenClaims = HashMap; + async fn test_introspect_token_is_expired(address: &str, identity_provider_address: &str) { + // token is expired + let token = Token::sign_with_kid( + TokenClaims::from([ + ("iss".into(), format!("http://{}/maskinporten", identity_provider_address).into()), + ("nbf".into(), (epoch_now_secs()).into()), + ("iat".into(), (epoch_now_secs()).into()), + ("exp".into(), (epoch_now_secs() - 120).into()), + ]), + "maskinporten", + ); - struct Token { - header: jwt::Header, - claims: TokenClaims, + test_well_formed_json_request( + &format!("http://{}/api/v1/introspect", address), + IntrospectRequest { + token, + }, + IntrospectResponse::new_invalid("invalid token: ExpiredSignature"), + StatusCode::OK, + ).await; } - impl Token { - fn new(claims: TokenClaims) -> Self { - Self { - header: jwt::Header::new(jwt::Algorithm::RS256), - claims, - } - } + // FIXME: this test doesn't fail as expected; validation crate not working? + async fn test_introspect_token_is_issued_in_the_future(address: &str, identity_provider_address: &str) { + let token = Token::sign_with_kid( + TokenClaims::from([ + ("iss".into(), format!("http://{}/maskinporten", identity_provider_address).into()), + ("nbf".into(), (epoch_now_secs()).into()), + ("iat".into(), (epoch_now_secs() + 120).into()), + ("exp".into(), (epoch_now_secs() + 300).into()), + ]), + "maskinporten", + ); - fn sign(self, key: &jwk::JsonWebKey) -> String { - jwt::encode( - &self.header, - &self.claims, - &key.key.to_encoding_key(), - ).unwrap() - } + test_well_formed_json_request( + &format!("http://{}/api/v1/introspect", address), + IntrospectRequest { + token, + }, + IntrospectResponse::new_invalid("token is issued in the future"), + StatusCode::OK, + ).await; } - impl TokenServer { - const SIGNING_KEY: &'static str = r#"{"p":"_LNnIjBshCrFuxtjUC2KKzg_NTVv26UZh5j12_9r5mYTxb8yW047jOYFEGvIdMkTRLGOBig6fLWzgd62lnLainzV35J6K6zr4jQfTldLondlkldMR6nQrp1KfnNUuRbKvzpNKkhl12-f1l91l0tCx3s4blztvWgdzN2xBfvWV68","kty":"RSA","q":"9MIWsbIA3WjiR_Ful5FM8NCgb6JdS2D6ySHVepoNI-iAPilcltF_J2orjfLqAxeztTskPi45wtF_-eV4GIYSzvMo-gFiXLMrvEa7WaWizMi_7Bu9tEk3m_f3IDLN9lwULYoebkDbiXx6GOiuj0VkuKz8ckYFNKLCMP9QRLFff-0","d":"J6UX848X8tNz-09PFvcFDUVqak32GXzoPjnuDjBsxNUvG7LxenLmM_i8tvYl0EW9Ztn4AiCqJUoHw5cX3jz_mSqGl7ciaDedpKm_AetcZwHiEuT1EpSKRPMmOMQSqcJqXrdbbWB8gdUrnTKZIlJCfj7yqgT16ypC43TnwjA0UwxhG5pHaYjKI3pPdoHg2BzA-iubHjVn15Sz7-pnjBmeGDbEFa7ADY-1yPHCmqqvPKTNhoCNW6RpG34Id9hXslPa3X-7pAhJrDBd0_NPlktSA2rUkifYiZURhHR5ijhe0v3uw6kYP8f_foVm_C8O1ExkxXh9Dg8KDZ89dbsSOtBc0Q","e":"AQAB","use":"sig","kid":"l7C_WJgbZ_6e59vPrFETAehX7Dsp7fIyvSV4XhotsGs","qi":"cQFN5q5WhYkzgd1RS0rGqvpX1AkmZMrLv2MW04gSfu0dDwpbsSAu8EUCQW9oA4pr6V7R9CBSu9kdN2iY5SR-hZvEad5nDKPV1F3TMQYv5KpRiS_0XhfV5PcolUJVO_4p3h8d-mo2hh1Sw2fairAKOzvnwJCQ6DFkiY7H1cqwA54","dp":"YTql9AGtvyy158gh7jeXcgmySEbHQzvDFulDr-IXIg8kjHGEbp0rTIs0Z50RA95aC5RFkRjpaBKBfvaySjDm5WIi6GLzntpp6B8l7H6qG1jVO_la4Df2kzjx8LVvY8fhOrKz_hDdHodUeKdCF3RdvWMr00ruLnJhBPJHqoW7cwE","alg":"RS256","dq":"IZA4AngRbEtEtG7kJn6zWVaSmZxfRMXwvgIYvy4-3Qy2AVA0tS3XTPVfMaD8_B2U9CY_CxPVseR-sysHc_12uNBZbycfcOzU84WTjXCMSZ7BysPnGMDtkkLHra-p1L29upz1HVNhh5H9QEswHM98R2LZX2ZAsn4bORLZ1AGqweU","n":"8ZqUp5Cs90XpNn8tJBdUUxdGH4bjqKjFj8lyB3x50RpTuECuwzX1NpVqyFENDiEtMja5fdmJl6SErjnhj6kbhcmfmFibANuG-0WlV5yMysdSbocd75C1JQbiPdpHdXrijmVFMfDnoZTQ-ErNsqqngTNkn5SXBcPenli6Cf9MTSchZuh_qFj_B7Fp3CWKehTiyBcLlNOIjYsXX8WQjZkWKGpQ23AWjZulngWRektLcRWuEKTWaRBtbAr3XAfSmcqTICrebaD3IMWKHDtvzHAt_pt4wnZ06clgeO2Wbc980usnpsF7g8k9p81RcbS4JEZmuuA9NCmOmbyADXwgA9_-Aw"}"#; - async fn new() -> Self { - let mut keys = HashMap::>::new(); - keys.insert("keys".to_string(), vec![Self::get_signing_key()]); - Self { - listener: tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(), - keys, - } - } - - fn port(&self) -> u16 { - self.listener.local_addr().unwrap().port() - } - - fn address(&self) -> String { - self.listener.local_addr().unwrap().to_string() - } - - fn token(claims: TokenClaims) -> String { - Token::new(claims).sign(&Self::get_signing_key()) - } - - fn token_with_kid(claims: TokenClaims, kid: &str) -> String { - let mut token = Token::new(claims); - token.header.kid = Some(kid.to_string()); - token.sign(&Self::get_signing_key()) - } - - pub async fn run(self) -> std::io::Result<()> { - let app = Router::new() - .route("/jwks", get(|| async { Json(self.keys) })); - - axum::serve(self.listener, app).await - } + // FIXME: this test doesn't fail as expected; validation crate not working? + async fn test_introspect_token_has_not_before_in_the_future(address: &str, identity_provider_address: &str) { + let token = Token::sign_with_kid( + TokenClaims::from([ + ("iss".into(), format!("http://{}/maskinporten", identity_provider_address).into()), + ("nbf".into(), (epoch_now_secs() - 120).into()), + ("iat".into(), (epoch_now_secs()).into()), + ("exp".into(), (epoch_now_secs() + 300).into()), + ]), + "maskinporten", + ); + println!("{}", token); - fn get_signing_key() -> jwk::JsonWebKey { - Self::SIGNING_KEY.parse().unwrap() - } + test_well_formed_json_request( + &format!("http://{}/api/v1/introspect", address), + IntrospectRequest { + token, + }, + IntrospectResponse::new_invalid("token has not before in the future"), + StatusCode::OK, + ).await; } async fn test_token_exchange_missing_or_empty_user_token(address: &str) { @@ -539,6 +518,47 @@ mod tests { } } + type TokenClaims = HashMap; + + struct Token { + header: jwt::Header, + claims: TokenClaims, + } + + impl Token { + const SIGNING_KEY: &'static str = r#"{"p":"_LNnIjBshCrFuxtjUC2KKzg_NTVv26UZh5j12_9r5mYTxb8yW047jOYFEGvIdMkTRLGOBig6fLWzgd62lnLainzV35J6K6zr4jQfTldLondlkldMR6nQrp1KfnNUuRbKvzpNKkhl12-f1l91l0tCx3s4blztvWgdzN2xBfvWV68","kty":"RSA","q":"9MIWsbIA3WjiR_Ful5FM8NCgb6JdS2D6ySHVepoNI-iAPilcltF_J2orjfLqAxeztTskPi45wtF_-eV4GIYSzvMo-gFiXLMrvEa7WaWizMi_7Bu9tEk3m_f3IDLN9lwULYoebkDbiXx6GOiuj0VkuKz8ckYFNKLCMP9QRLFff-0","d":"J6UX848X8tNz-09PFvcFDUVqak32GXzoPjnuDjBsxNUvG7LxenLmM_i8tvYl0EW9Ztn4AiCqJUoHw5cX3jz_mSqGl7ciaDedpKm_AetcZwHiEuT1EpSKRPMmOMQSqcJqXrdbbWB8gdUrnTKZIlJCfj7yqgT16ypC43TnwjA0UwxhG5pHaYjKI3pPdoHg2BzA-iubHjVn15Sz7-pnjBmeGDbEFa7ADY-1yPHCmqqvPKTNhoCNW6RpG34Id9hXslPa3X-7pAhJrDBd0_NPlktSA2rUkifYiZURhHR5ijhe0v3uw6kYP8f_foVm_C8O1ExkxXh9Dg8KDZ89dbsSOtBc0Q","e":"AQAB","use":"sig","kid":"l7C_WJgbZ_6e59vPrFETAehX7Dsp7fIyvSV4XhotsGs","qi":"cQFN5q5WhYkzgd1RS0rGqvpX1AkmZMrLv2MW04gSfu0dDwpbsSAu8EUCQW9oA4pr6V7R9CBSu9kdN2iY5SR-hZvEad5nDKPV1F3TMQYv5KpRiS_0XhfV5PcolUJVO_4p3h8d-mo2hh1Sw2fairAKOzvnwJCQ6DFkiY7H1cqwA54","dp":"YTql9AGtvyy158gh7jeXcgmySEbHQzvDFulDr-IXIg8kjHGEbp0rTIs0Z50RA95aC5RFkRjpaBKBfvaySjDm5WIi6GLzntpp6B8l7H6qG1jVO_la4Df2kzjx8LVvY8fhOrKz_hDdHodUeKdCF3RdvWMr00ruLnJhBPJHqoW7cwE","alg":"RS256","dq":"IZA4AngRbEtEtG7kJn6zWVaSmZxfRMXwvgIYvy4-3Qy2AVA0tS3XTPVfMaD8_B2U9CY_CxPVseR-sysHc_12uNBZbycfcOzU84WTjXCMSZ7BysPnGMDtkkLHra-p1L29upz1HVNhh5H9QEswHM98R2LZX2ZAsn4bORLZ1AGqweU","n":"8ZqUp5Cs90XpNn8tJBdUUxdGH4bjqKjFj8lyB3x50RpTuECuwzX1NpVqyFENDiEtMja5fdmJl6SErjnhj6kbhcmfmFibANuG-0WlV5yMysdSbocd75C1JQbiPdpHdXrijmVFMfDnoZTQ-ErNsqqngTNkn5SXBcPenli6Cf9MTSchZuh_qFj_B7Fp3CWKehTiyBcLlNOIjYsXX8WQjZkWKGpQ23AWjZulngWRektLcRWuEKTWaRBtbAr3XAfSmcqTICrebaD3IMWKHDtvzHAt_pt4wnZ06clgeO2Wbc980usnpsF7g8k9p81RcbS4JEZmuuA9NCmOmbyADXwgA9_-Aw"}"#; + + fn new(claims: TokenClaims) -> Self { + Self { + header: jwt::Header::new(jwt::Algorithm::RS256), + claims, + } + } + + fn sign(claims: TokenClaims) -> String { + Self::new(claims).encode() + } + + fn sign_with_kid(claims: TokenClaims, kid: &str) -> String { + let mut token = Self::new(claims); + token.header.kid = Some(kid.to_string()); + token.encode() + } + + fn encode(&self) -> String { + let key = Self::get_signing_key(); + jwt::encode( + &self.header, + &self.claims, + &key.key.to_encoding_key(), + ).unwrap() + } + + fn get_signing_key() -> jwk::JsonWebKey { + Self::SIGNING_KEY.parse().unwrap() + } + } + struct DockerRuntimeParams { container: Option>, host: String, @@ -546,18 +566,28 @@ mod tests { } impl DockerRuntimeParams { + const MOCK_OAUTH_SERVER_JSON_CONFIG: &'static str = r#"{ + "tokenProvider" : { + "keyProvider" : { + "initialKeys" : "{\"p\":\"_LNnIjBshCrFuxtjUC2KKzg_NTVv26UZh5j12_9r5mYTxb8yW047jOYFEGvIdMkTRLGOBig6fLWzgd62lnLainzV35J6K6zr4jQfTldLondlkldMR6nQrp1KfnNUuRbKvzpNKkhl12-f1l91l0tCx3s4blztvWgdzN2xBfvWV68\",\"kty\":\"RSA\",\"q\":\"9MIWsbIA3WjiR_Ful5FM8NCgb6JdS2D6ySHVepoNI-iAPilcltF_J2orjfLqAxeztTskPi45wtF_-eV4GIYSzvMo-gFiXLMrvEa7WaWizMi_7Bu9tEk3m_f3IDLN9lwULYoebkDbiXx6GOiuj0VkuKz8ckYFNKLCMP9QRLFff-0\",\"d\":\"J6UX848X8tNz-09PFvcFDUVqak32GXzoPjnuDjBsxNUvG7LxenLmM_i8tvYl0EW9Ztn4AiCqJUoHw5cX3jz_mSqGl7ciaDedpKm_AetcZwHiEuT1EpSKRPMmOMQSqcJqXrdbbWB8gdUrnTKZIlJCfj7yqgT16ypC43TnwjA0UwxhG5pHaYjKI3pPdoHg2BzA-iubHjVn15Sz7-pnjBmeGDbEFa7ADY-1yPHCmqqvPKTNhoCNW6RpG34Id9hXslPa3X-7pAhJrDBd0_NPlktSA2rUkifYiZURhHR5ijhe0v3uw6kYP8f_foVm_C8O1ExkxXh9Dg8KDZ89dbsSOtBc0Q\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"l7C_WJgbZ_6e59vPrFETAehX7Dsp7fIyvSV4XhotsGs\",\"qi\":\"cQFN5q5WhYkzgd1RS0rGqvpX1AkmZMrLv2MW04gSfu0dDwpbsSAu8EUCQW9oA4pr6V7R9CBSu9kdN2iY5SR-hZvEad5nDKPV1F3TMQYv5KpRiS_0XhfV5PcolUJVO_4p3h8d-mo2hh1Sw2fairAKOzvnwJCQ6DFkiY7H1cqwA54\",\"dp\":\"YTql9AGtvyy158gh7jeXcgmySEbHQzvDFulDr-IXIg8kjHGEbp0rTIs0Z50RA95aC5RFkRjpaBKBfvaySjDm5WIi6GLzntpp6B8l7H6qG1jVO_la4Df2kzjx8LVvY8fhOrKz_hDdHodUeKdCF3RdvWMr00ruLnJhBPJHqoW7cwE\",\"alg\":\"RS256\",\"dq\":\"IZA4AngRbEtEtG7kJn6zWVaSmZxfRMXwvgIYvy4-3Qy2AVA0tS3XTPVfMaD8_B2U9CY_CxPVseR-sysHc_12uNBZbycfcOzU84WTjXCMSZ7BysPnGMDtkkLHra-p1L29upz1HVNhh5H9QEswHM98R2LZX2ZAsn4bORLZ1AGqweU\",\"n\":\"8ZqUp5Cs90XpNn8tJBdUUxdGH4bjqKjFj8lyB3x50RpTuECuwzX1NpVqyFENDiEtMja5fdmJl6SErjnhj6kbhcmfmFibANuG-0WlV5yMysdSbocd75C1JQbiPdpHdXrijmVFMfDnoZTQ-ErNsqqngTNkn5SXBcPenli6Cf9MTSchZuh_qFj_B7Fp3CWKehTiyBcLlNOIjYsXX8WQjZkWKGpQ23AWjZulngWRektLcRWuEKTWaRBtbAr3XAfSmcqTICrebaD3IMWKHDtvzHAt_pt4wnZ06clgeO2Wbc980usnpsF7g8k9p81RcbS4JEZmuuA9NCmOmbyADXwgA9_-Aw\"}", + "algorithm" : "RS256" + } + } + }"#; + /// Runs Docker from Rust, no external setup needed #[cfg(feature = "docker")] async fn init() -> DockerRuntimeParams { use reqwest::StatusCode; use testcontainers::core::wait::HttpWaitStrategy; - use testcontainers::core::{IntoContainerPort, WaitFor}; + use testcontainers::core::{IntoContainerPort, WaitFor, ImageExt}; use testcontainers::runners::AsyncRunner; // Set up Docker container let container = GenericImage::new("ghcr.io/navikt/mock-oauth2-server", "2.1.10") .with_exposed_port(8080.tcp()) .with_wait_for(WaitFor::Http(HttpWaitStrategy::new("/maskinporten/.well-known/openid-configuration").with_expected_status_code(StatusCode::OK))) + .with_env_var("JSON_CONFIG", Self::MOCK_OAUTH_SERVER_JSON_CONFIG) .start() .await .unwrap(); diff --git a/src/claims.rs b/src/claims.rs index c705f2b..58cbe7a 100644 --- a/src/claims.rs +++ b/src/claims.rs @@ -61,7 +61,7 @@ impl Assertion for ClientAssertion { } } -fn epoch_now_secs() -> u64 { +pub fn epoch_now_secs() -> u64 { std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap()