From 175aaecff5988c505d92b4f55264037f1be1fbf1 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Sun, 25 Jan 2026 15:44:57 +0100 Subject: [PATCH 01/32] feat(auth): authenticate workload controller --- src/console/src/auth/delegation.rs | 4 +- src/console/src/auth/register.rs | 4 +- src/console/src/impls.rs | 14 ++- src/console/src/types.rs | 9 +- src/libs/auth/src/openid/impls.rs | 6 +- src/libs/auth/src/openid/jwt/verify.rs | 93 ++++++++++++------- src/libs/auth/src/openid/types.rs | 2 +- src/libs/auth/src/openid/verify/mod.rs | 5 + .../src/openid/{verify.rs => verify/user.rs} | 42 +++++++-- src/libs/auth/src/openid/verify/workload.rs | 69 ++++++++++++++ src/libs/satellite/src/api/controllers.rs | 10 ++ src/libs/satellite/src/auth/delegation.rs | 4 +- .../satellite/src/controllers/authenticate.rs | 24 +++++ src/libs/satellite/src/controllers/mod.rs | 4 + src/libs/satellite/src/controllers/types.rs | 22 +++++ src/libs/satellite/src/user/core/impls.rs | 4 +- src/observatory/src/openid/scheduler.rs | 4 +- 17 files changed, 258 insertions(+), 62 deletions(-) create mode 100644 src/libs/auth/src/openid/verify/mod.rs rename src/libs/auth/src/openid/{verify.rs => verify/user.rs} (61%) create mode 100644 src/libs/auth/src/openid/verify/workload.rs create mode 100644 src/libs/satellite/src/controllers/authenticate.rs create mode 100644 src/libs/satellite/src/controllers/types.rs diff --git a/src/console/src/auth/delegation.rs b/src/console/src/auth/delegation.rs index 78a9fc0a96..699a9b7b22 100644 --- a/src/console/src/auth/delegation.rs +++ b/src/console/src/auth/delegation.rs @@ -16,7 +16,7 @@ pub async fn openid_prepare_delegation( args: &OpenIdPrepareDelegationArgs, providers: &OpenIdProviders, ) -> OpenIdPrepareDelegationResult { - let (credential, provider) = match openid::verify_openid_credentials_with_jwks_renewal( + let (credential, provider) = match openid::verify_user_openid_credentials_with_jwks_renewal( &args.jwt, &args.salt, providers, &AuthHeap, ) .await @@ -40,7 +40,7 @@ pub fn openid_get_delegation( args: &OpenIdGetDelegationArgs, providers: &OpenIdProviders, ) -> GetDelegationResult { - let (credential, provider) = match openid::verify_openid_credentials_with_cached_jwks( + let (credential, provider) = match openid::verify_user_openid_credentials_with_cached_jwks( &args.jwt, &args.salt, providers, &AuthHeap, ) { Ok(value) => value, diff --git a/src/console/src/auth/register.rs b/src/console/src/auth/register.rs index 5dec898413..730b455dc7 100644 --- a/src/console/src/auth/register.rs +++ b/src/console/src/auth/register.rs @@ -1,6 +1,6 @@ use crate::accounts::{get_optional_account, init_account, update_provider}; -use crate::types::state::OpenId; use crate::types::state::{Account, OpenIdData, Provider}; +use crate::types::state::{OpenId, OpenIdAuthProvider}; use candid::Principal; use junobuild_auth::delegation::types::UserKey; use junobuild_auth::openid::types::interface::OpenIdCredential; @@ -42,7 +42,7 @@ pub async fn register_account( }; let provider = Provider::OpenId(OpenId { - provider: provider.clone(), + provider: OpenIdAuthProvider::from(provider), data: provider_data, }); diff --git a/src/console/src/impls.rs b/src/console/src/impls.rs index 3697eab5de..1ab673dfcb 100644 --- a/src/console/src/impls.rs +++ b/src/console/src/impls.rs @@ -1,11 +1,14 @@ use crate::memory::manager::init_stable_state; use crate::types::ledger::{Fee, IcpPayment, IcrcPayment, IcrcPaymentKey}; -use crate::types::state::{Account, HeapState, Segment, SegmentKey, State, StorableSegmentKind}; +use crate::types::state::{ + Account, HeapState, OpenIdAuthProvider, Segment, SegmentKey, State, StorableSegmentKind, +}; use candid::Principal; use ic_cdk::api::time; use ic_ledger_types::{BlockIndex, Tokens}; use ic_stable_structures::storable::Bound; use ic_stable_structures::Storable; +use junobuild_auth::openid::types::provider::OpenIdProvider; use junobuild_shared::ledger::types::cycles::CyclesTokens; use junobuild_shared::memory::serializers::{ deserialize_from_bytes, serialize_into_bytes, serialize_to_bytes, @@ -190,3 +193,12 @@ impl Fee { } } } + +impl From<&OpenIdProvider> for OpenIdAuthProvider { + fn from(provider: &OpenIdProvider) -> Self { + match provider { + OpenIdProvider::Google => OpenIdAuthProvider::Google, + OpenIdProvider::GitHubProxy => OpenIdAuthProvider::GitHub, + } + } +} diff --git a/src/console/src/types.rs b/src/console/src/types.rs index 7af84477c8..5ecc7ae98a 100644 --- a/src/console/src/types.rs +++ b/src/console/src/types.rs @@ -4,7 +4,6 @@ pub mod state { use candid::CandidType; use ic_ledger_types::{BlockIndex, Tokens}; use ic_stable_structures::StableBTreeMap; - use junobuild_auth::openid::types::provider::OpenIdProvider; use junobuild_auth::state::types::state::AuthenticationHeapState; use junobuild_cdn::proposals::{ProposalsStable, SegmentDeploymentVersion}; use junobuild_cdn::storage::{ProposalAssetsStable, ProposalContentChunksStable}; @@ -83,10 +82,16 @@ pub mod state { #[derive(CandidType, Serialize, Deserialize, Clone)] pub struct OpenId { - pub provider: OpenIdProvider, + pub provider: OpenIdAuthProvider, pub data: OpenIdData, } + #[derive(CandidType, Serialize, Deserialize, Clone)] + pub enum OpenIdAuthProvider { + Google, + GitHub, + } + #[derive(CandidType, Serialize, Deserialize, Clone, Eq, PartialEq)] pub struct OpenIdData { pub email: Option, diff --git a/src/libs/auth/src/openid/impls.rs b/src/libs/auth/src/openid/impls.rs index 24002271f9..cb9378d775 100644 --- a/src/libs/auth/src/openid/impls.rs +++ b/src/libs/auth/src/openid/impls.rs @@ -39,14 +39,14 @@ impl OpenIdProvider { Self::Google => "https://www.googleapis.com/oauth2/v3/certs", // Swap for local development with the Juno API: // http://host.docker.internal:3000/v1/auth/certs - Self::GitHub => "https://api.juno.build/v1/auth/certs", + Self::GitHubProxy => "https://api.juno.build/v1/auth/certs", } } pub fn issuers(&self) -> &[&'static str] { match self { OpenIdProvider::Google => &["https://accounts.google.com", "accounts.google.com"], - OpenIdProvider::GitHub => &["https://api.juno.build/auth/github"], + OpenIdProvider::GitHubProxy => &["https://api.juno.build/auth/github"], } } } @@ -93,7 +93,7 @@ impl Display for OpenIdProvider { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { match self { OpenIdProvider::Google => write!(f, "Google"), - OpenIdProvider::GitHub => write!(f, "GitHub"), + OpenIdProvider::GitHubProxy => write!(f, "GitHub"), } } } diff --git a/src/libs/auth/src/openid/jwt/verify.rs b/src/libs/auth/src/openid/jwt/verify.rs index 088482f394..37d3fdc55a 100644 --- a/src/libs/auth/src/openid/jwt/verify.rs +++ b/src/libs/auth/src/openid/jwt/verify.rs @@ -7,13 +7,17 @@ fn pick_key<'a>(kid: &str, jwks: &'a [Jwk]) -> Option<&'a Jwk> { jwks.iter().find(|j| j.kid.as_deref() == Some(kid)) } -pub fn verify_openid_jwt( +pub fn verify_openid_jwt( jwt: &str, issuers: &[&str], - client_id: &str, jwks: &[Jwk], - expected_nonce: &str, -) -> Result, JwtVerifyError> { + assert_audience: Aud, + assert_no_replay: Replay, +) -> Result, JwtVerifyError> +where + Aud: FnOnce(&Claims) -> Result<(), JwtVerifyError>, + Replay: FnOnce(&Claims) -> Result<(), JwtVerifyError>, +{ // 1) Read header to get `kid` let header = decode_jwt_header(jwt).map_err(JwtVerifyError::from)?; @@ -55,16 +59,13 @@ pub fn verify_openid_jwt( let token = decode::(jwt, &key, &val).map_err(|e| JwtVerifyError::BadSig(e.to_string()))?; - // 6) Manual checks audience let c = &token.claims; - if c.aud != client_id { - return Err(JwtVerifyError::BadClaim("aud".to_string())); - } - // 7) Assert it is the expected nonce - if c.nonce.as_deref() != Some(expected_nonce) { - return Err(JwtVerifyError::BadClaim("nonce".to_string())); - } + // 6) Manual checks audience + assert_audience(c)?; + + // 7) Prevent replace attack + assert_no_replay(c)?; // 8) Assert expiration let now_ns = now_ns(); @@ -179,6 +180,20 @@ mod verify_tests { } } + fn assert_audience(claims: &Claims) -> Result<(), JwtVerifyError> { + if claims.aud != AUD_OK { + return Err(JwtVerifyError::BadClaim("aud".to_string())); + } + Ok(()) + } + + fn assert_nonce(claims: &Claims) -> Result<(), JwtVerifyError> { + if claims.nonce.as_deref() != Some(NONCE_OK) { + return Err(JwtVerifyError::BadClaim("nonce".to_string())); + } + Ok(()) + } + #[test] fn verifies_ok() { let now = now_secs(); @@ -197,9 +212,9 @@ mod verify_tests { let out = verify_openid_jwt( &token, &[ISS_GOOGLE], - AUD_OK, &[jwk_with_kid(KID_OK)], - NONCE_OK, + |claims| assert_audience(claims), + |claims| assert_nonce(claims), ) .expect("should verify"); @@ -226,9 +241,9 @@ mod verify_tests { let err = verify_openid_jwt( &token, &[ISS_GOOGLE], - AUD_OK, &[jwk_with_kid(KID_OK)], - NONCE_OK, + |claims| assert_audience(claims), + |claims| assert_nonce(claims), ) .unwrap_err(); assert!(matches!(err, JwtVerifyError::MissingKid)); @@ -252,9 +267,9 @@ mod verify_tests { let err = verify_openid_jwt( &token, &[ISS_GOOGLE], - AUD_OK, &[jwk_with_kid(KID_OK)], - NONCE_OK, + |claims| assert_audience(claims), + |claims| assert_nonce(claims), ) .unwrap_err(); assert!(matches!(err, JwtVerifyError::NoKeyForKid)); @@ -279,9 +294,9 @@ mod verify_tests { let err = verify_openid_jwt( &token, &[ISS_GOOGLE], - AUD_OK, &[jwk_with_kid(KID_OK)], - NONCE_OK, + |claims| assert_audience(claims), + |claims| assert_nonce(claims), ) .unwrap_err(); assert!(matches!(err, JwtVerifyError::BadSig(_))); @@ -305,9 +320,9 @@ mod verify_tests { let err = verify_openid_jwt( &token, &[ISS_GOOGLE], - AUD_OK, &[jwk_with_kid(KID_OK)], - NONCE_OK, + |claims| assert_audience(claims), + |claims| assert_nonce(claims), ) .unwrap_err(); assert!(matches!(err, JwtVerifyError::BadClaim(ref f) if f == "typ")); @@ -331,9 +346,9 @@ mod verify_tests { let err = verify_openid_jwt( &token, &[ISS_GOOGLE], - AUD_OK, &[jwk_with_kid(KID_OK)], - NONCE_OK, + |claims| assert_audience(claims), + |claims| assert_nonce(claims), ) .unwrap_err(); assert!(matches!(err, JwtVerifyError::BadClaim(ref f) if f == "aud")); @@ -357,9 +372,9 @@ mod verify_tests { let err = verify_openid_jwt( &token, &[ISS_GOOGLE], - AUD_OK, &[jwk_with_kid(KID_OK)], - NONCE_OK, + |claims| assert_audience(claims), + |claims| assert_nonce(claims), ) .unwrap_err(); assert!(matches!(err, JwtVerifyError::BadClaim(ref f) if f == "nonce")); @@ -384,9 +399,9 @@ mod verify_tests { let err = verify_openid_jwt( &token, &[ISS_GOOGLE], - AUD_OK, &[jwk_with_kid(KID_OK)], - NONCE_OK, + |claims| assert_audience(claims), + |claims| assert_nonce(claims), ) .unwrap_err(); assert!(matches!(err, JwtVerifyError::BadClaim(ref f) if f == "iat_future")); @@ -411,9 +426,9 @@ mod verify_tests { let err = verify_openid_jwt( &token, &[ISS_GOOGLE], - AUD_OK, &[jwk_with_kid(KID_OK)], - NONCE_OK, + |claims| assert_audience(claims), + |claims| assert_nonce(claims), ) .unwrap_err(); assert!(matches!(err, JwtVerifyError::BadClaim(ref f) if f == "iat_expired")); @@ -439,9 +454,9 @@ mod verify_tests { let err = verify_openid_jwt( &token, &[ISS_GOOGLE], - AUD_OK, &[jwk_with_kid(KID_OK)], - NONCE_OK, + |claims| assert_audience(claims), + |claims| assert_nonce(claims), ) .unwrap_err(); assert!(matches!(err, JwtVerifyError::BadSig(_))); @@ -478,8 +493,14 @@ mod verify_tests { }), }; - let err = - verify_openid_jwt(&token, &[ISS_GOOGLE], AUD_OK, &[bad_jwk], NONCE_OK).unwrap_err(); + let err = verify_openid_jwt( + &token, + &[ISS_GOOGLE], + &[bad_jwk], + |claims| assert_audience(claims), + |claims| assert_nonce(claims), + ) + .unwrap_err(); assert!(matches!(err, JwtVerifyError::BadSig(_))); } @@ -509,9 +530,9 @@ mod verify_tests { let out = verify_openid_jwt( &token, &[ISS_GOOGLE], - AUD_OK, &[jwk_with_kid(KID_OK)], - NONCE_OK, + |claims| assert_audience(claims), + |claims| assert_nonce(claims), ) .expect("should verify"); diff --git a/src/libs/auth/src/openid/types.rs b/src/libs/auth/src/openid/types.rs index b5b51b003b..0e944d8365 100644 --- a/src/libs/auth/src/openid/types.rs +++ b/src/libs/auth/src/openid/types.rs @@ -44,7 +44,7 @@ pub mod provider { )] pub enum OpenIdProvider { Google, - GitHub, + GitHubProxy, } #[derive(CandidType, Serialize, Deserialize, Clone)] diff --git a/src/libs/auth/src/openid/verify/mod.rs b/src/libs/auth/src/openid/verify/mod.rs new file mode 100644 index 0000000000..d325f64617 --- /dev/null +++ b/src/libs/auth/src/openid/verify/mod.rs @@ -0,0 +1,5 @@ +mod user; +mod workload; + +pub use user::*; +pub use workload::*; \ No newline at end of file diff --git a/src/libs/auth/src/openid/verify.rs b/src/libs/auth/src/openid/verify/user.rs similarity index 61% rename from src/libs/auth/src/openid/verify.rs rename to src/libs/auth/src/openid/verify/user.rs index ca3b90cc2b..558af934f0 100644 --- a/src/libs/auth/src/openid/verify.rs +++ b/src/libs/auth/src/openid/verify/user.rs @@ -1,5 +1,7 @@ use crate::openid::jwkset::{get_jwks, get_or_refresh_jwks}; use crate::openid::jwt::types::cert::Jwks; +use crate::openid::jwt::types::errors::JwtVerifyError; +use crate::openid::jwt::types::token::Claims; use crate::openid::jwt::{unsafe_find_jwt_provider, verify_openid_jwt}; use crate::openid::types::errors::VerifyOpenidCredentialsError; use crate::openid::types::interface::OpenIdCredential; @@ -9,15 +11,15 @@ use crate::state::types::config::{OpenIdProviderClientId, OpenIdProviders}; use crate::state::types::state::Salt; use crate::strategies::AuthHeapStrategy; -type VerifyOpenIdCredentialsResult = +type VerifyUserOpenIdCredentialsResult = Result<(OpenIdCredential, OpenIdProvider), VerifyOpenidCredentialsError>; -pub async fn verify_openid_credentials_with_jwks_renewal( +pub async fn verify_user_openid_credentials_with_jwks_renewal( jwt: &str, salt: &Salt, providers: &OpenIdProviders, auth_heap: &impl AuthHeapStrategy, -) -> VerifyOpenIdCredentialsResult { +) -> VerifyUserOpenIdCredentialsResult { let (provider, config) = unsafe_find_jwt_provider(providers, jwt) .map_err(VerifyOpenidCredentialsError::JwtFindProvider)?; @@ -28,12 +30,12 @@ pub async fn verify_openid_credentials_with_jwks_renewal( verify_openid_credentials(jwt, &jwks, &provider, &config.client_id, salt) } -pub fn verify_openid_credentials_with_cached_jwks( +pub fn verify_user_openid_credentials_with_cached_jwks( jwt: &str, salt: &Salt, providers: &OpenIdProviders, auth_heap: &impl AuthHeapStrategy, -) -> VerifyOpenIdCredentialsResult { +) -> VerifyUserOpenIdCredentialsResult { let (provider, config) = unsafe_find_jwt_provider(providers, jwt) .map_err(VerifyOpenidCredentialsError::JwtFindProvider)?; @@ -48,11 +50,33 @@ fn verify_openid_credentials( provider: &OpenIdProvider, client_id: &OpenIdProviderClientId, salt: &Salt, -) -> VerifyOpenIdCredentialsResult { - let nonce = build_nonce(salt); +) -> VerifyUserOpenIdCredentialsResult { + let assert_audience = |claims: &Claims| -> Result<(), JwtVerifyError> { + if claims.aud != client_id.as_str() { + return Err(JwtVerifyError::BadClaim("aud".to_string())); + } - let token = verify_openid_jwt(jwt, provider.issuers(), client_id, &jwks.keys, &nonce) - .map_err(VerifyOpenidCredentialsError::JwtVerify)?; + Ok(()) + }; + + let assert_no_replay = |claims: &Claims| -> Result<(), JwtVerifyError> { + let nonce = build_nonce(salt); + + if claims.nonce.as_deref() != Some(nonce.as_str()) { + return Err(JwtVerifyError::BadClaim("nonce".to_string())); + } + + Ok(()) + }; + + let token = verify_openid_jwt( + jwt, + provider.issuers(), + &jwks.keys, + &assert_audience, + &assert_no_replay, + ) + .map_err(VerifyOpenidCredentialsError::JwtVerify)?; let credential = OpenIdCredential::from(token); diff --git a/src/libs/auth/src/openid/verify/workload.rs b/src/libs/auth/src/openid/verify/workload.rs new file mode 100644 index 0000000000..4733b4ee65 --- /dev/null +++ b/src/libs/auth/src/openid/verify/workload.rs @@ -0,0 +1,69 @@ +use crate::openid::jwkset::get_or_refresh_jwks; +use crate::openid::jwt::types::cert::Jwks; +use crate::openid::jwt::types::errors::JwtVerifyError; +use crate::openid::jwt::types::token::Claims; +use crate::openid::jwt::{unsafe_find_jwt_provider, verify_openid_jwt}; +use crate::openid::types::errors::VerifyOpenidCredentialsError; +use crate::openid::types::interface::OpenIdCredential; +use crate::openid::types::provider::OpenIdProvider; +use crate::state::types::config::OpenIdProviders; +use crate::strategies::AuthHeapStrategy; + +type VerifyWorkloadOpenIdCredentialsResult = + Result<(OpenIdCredential, OpenIdProvider), VerifyOpenidCredentialsError>; + +pub async fn verify_workload_openid_credentials_with_jwks_renewal( + jwt: &str, + providers: &OpenIdProviders, + auth_heap: &impl AuthHeapStrategy, +) -> VerifyWorkloadOpenIdCredentialsResult { + let (provider, config) = unsafe_find_jwt_provider(providers, jwt) + .map_err(VerifyOpenidCredentialsError::JwtFindProvider)?; + + let jwks = get_or_refresh_jwks(&provider, jwt, auth_heap) + .await + .map_err(VerifyOpenidCredentialsError::GetOrFetchJwks)?; + + verify_openid_credentials(jwt, &jwks, &provider) +} + +fn verify_openid_credentials( + jwt: &str, + jwks: &Jwks, + provider: &OpenIdProvider, +) -> VerifyWorkloadOpenIdCredentialsResult { + let assert_audience = |claims: &Claims| -> Result<(), JwtVerifyError> { + // if claims.aud != client_id.as_str() { + // return Err(JwtVerifyError::BadClaim("aud".to_string())); + // } + + // TODO: asser github username and repo + + Ok(()) + }; + + let assert_no_replay = |claims: &Claims| -> Result<(), JwtVerifyError> { + // let nonce = build_nonce(salt); + // + // if claims.nonce.as_deref() != Some(nonce.as_str()) { + // return Err(JwtVerifyError::BadClaim("nonce".to_string())); + // } + + // TODO: assert jti + + Ok(()) + }; + + let token = verify_openid_jwt( + jwt, + provider.issuers(), + &jwks.keys, + &assert_audience, + &assert_no_replay, + ) + .map_err(VerifyOpenidCredentialsError::JwtVerify)?; + + let credential = OpenIdCredential::from(token); + + Ok((credential, provider.clone())) +} diff --git a/src/libs/satellite/src/api/controllers.rs b/src/libs/satellite/src/api/controllers.rs index d11d23cdfb..556ef1f9d4 100644 --- a/src/libs/satellite/src/api/controllers.rs +++ b/src/libs/satellite/src/api/controllers.rs @@ -1,4 +1,6 @@ +use crate::controllers::openid_authenticate_controller; use crate::controllers::store::{delete_controllers, set_controllers as set_controllers_store}; +use crate::controllers::types::AuthenticateControllerArgs; use crate::{get_admin_controllers, get_controllers}; use ic_cdk::trap; use junobuild_shared::constants::shared::MAX_NUMBER_OF_SATELLITE_CONTROLLERS; @@ -49,3 +51,11 @@ pub fn del_controllers( pub fn list_controllers() -> Controllers { get_controllers() } + +pub async fn authenticate_controller(args: AuthenticateControllerArgs) { + match args { + AuthenticateControllerArgs::OpenId(args) => { + openid_authenticate_controller(&args).await.unwrap_or_trap() + } + } +} diff --git a/src/libs/satellite/src/auth/delegation.rs b/src/libs/satellite/src/auth/delegation.rs index 78a9fc0a96..699a9b7b22 100644 --- a/src/libs/satellite/src/auth/delegation.rs +++ b/src/libs/satellite/src/auth/delegation.rs @@ -16,7 +16,7 @@ pub async fn openid_prepare_delegation( args: &OpenIdPrepareDelegationArgs, providers: &OpenIdProviders, ) -> OpenIdPrepareDelegationResult { - let (credential, provider) = match openid::verify_openid_credentials_with_jwks_renewal( + let (credential, provider) = match openid::verify_user_openid_credentials_with_jwks_renewal( &args.jwt, &args.salt, providers, &AuthHeap, ) .await @@ -40,7 +40,7 @@ pub fn openid_get_delegation( args: &OpenIdGetDelegationArgs, providers: &OpenIdProviders, ) -> GetDelegationResult { - let (credential, provider) = match openid::verify_openid_credentials_with_cached_jwks( + let (credential, provider) = match openid::verify_user_openid_credentials_with_cached_jwks( &args.jwt, &args.salt, providers, &AuthHeap, ) { Ok(value) => value, diff --git a/src/libs/satellite/src/controllers/authenticate.rs b/src/libs/satellite/src/controllers/authenticate.rs new file mode 100644 index 0000000000..6053af8e62 --- /dev/null +++ b/src/libs/satellite/src/controllers/authenticate.rs @@ -0,0 +1,24 @@ +use junobuild_auth::state::get_providers; +use crate::auth::strategy_impls::AuthHeap; +use crate::controllers::types::OpenIdAuthenticateControllerArgs; +use junobuild_auth::{delegation, openid}; +use junobuild_auth::delegation::types::PrepareDelegationError; + +pub async fn openid_authenticate_controller( + args: &OpenIdAuthenticateControllerArgs, +) -> Result<(), String> { + let providers = get_providers(&AuthHeap)?; + + let (credential, provider) = match openid::verify_workload_openid_credentials_with_jwks_renewal( + &args.jwt, &providers, &AuthHeap, + ) + .await + { + Ok(value) => value, + Err(err) => return Err(PrepareDelegationError::from(err)), + }; + + + + Ok(()) +} diff --git a/src/libs/satellite/src/controllers/mod.rs b/src/libs/satellite/src/controllers/mod.rs index 55c88cbf3d..99a8201cdf 100644 --- a/src/libs/satellite/src/controllers/mod.rs +++ b/src/libs/satellite/src/controllers/mod.rs @@ -1 +1,5 @@ +mod authenticate; pub mod store; +pub mod types; + +pub use authenticate::*; diff --git a/src/libs/satellite/src/controllers/types.rs b/src/libs/satellite/src/controllers/types.rs new file mode 100644 index 0000000000..7ad2f47de9 --- /dev/null +++ b/src/libs/satellite/src/controllers/types.rs @@ -0,0 +1,22 @@ +use candid::{CandidType, Deserialize}; +use junobuild_shared::types::state::Metadata; +use serde::Serialize; + +#[derive(CandidType, Serialize, Deserialize)] +pub enum AuthenticateControllerArgs { + OpenId(OpenIdAuthenticateControllerArgs), +} + +#[derive(CandidType, Serialize, Deserialize)] +pub struct OpenIdAuthenticateControllerArgs { + pub jwt: String, + pub metadata: Metadata, + pub max_time_to_live: Option, + pub scope: GrantableScope, +} + +#[derive(CandidType, Serialize, Deserialize, Clone)] +pub enum GrantableScope { + Write, + Submit, +} diff --git a/src/libs/satellite/src/user/core/impls.rs b/src/libs/satellite/src/user/core/impls.rs index c3bf7e3440..d300c24a23 100644 --- a/src/libs/satellite/src/user/core/impls.rs +++ b/src/libs/satellite/src/user/core/impls.rs @@ -164,7 +164,7 @@ impl From<&OpenIdProvider> for AuthProvider { fn from(provider: &OpenIdProvider) -> Self { match provider { OpenIdProvider::Google => AuthProvider::Google, - OpenIdProvider::GitHub => AuthProvider::GitHub, + OpenIdProvider::GitHubProxy => AuthProvider::GitHub, } } } @@ -344,7 +344,7 @@ mod tests { AuthProvider::Google )); assert!(matches!( - AuthProvider::from(&OpenIdProvider::GitHub), + AuthProvider::from(&OpenIdProvider::GitHubProxy), AuthProvider::GitHub )); } diff --git a/src/observatory/src/openid/scheduler.rs b/src/observatory/src/openid/scheduler.rs index be0a668c3a..3ffc530e82 100644 --- a/src/observatory/src/openid/scheduler.rs +++ b/src/observatory/src/openid/scheduler.rs @@ -9,7 +9,7 @@ use std::time::Duration; pub fn defer_restart_monitoring() { // Early spare one timer if no scheduler is enabled. - let enabled_count = [OpenIdProvider::Google, OpenIdProvider::GitHub] + let enabled_count = [OpenIdProvider::Google, OpenIdProvider::GitHubProxy] .into_iter() .filter(|provider| is_scheduler_enabled(provider)) .count(); @@ -24,7 +24,7 @@ pub fn defer_restart_monitoring() { } async fn restart_monitoring() { - for provider in [OpenIdProvider::Google, OpenIdProvider::GitHub] { + for provider in [OpenIdProvider::Google, OpenIdProvider::GitHubProxy] { schedule_certificate_update(provider, None); } } From 071fd43d2e74b332b2f8845fa70772680b4ccfbf Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Sun, 25 Jan 2026 18:37:09 +0100 Subject: [PATCH 02/32] refactor: split modules user and workload for jwt verification --- src/console/src/accounts/impls.rs | 2 +- src/console/src/auth/delegation.rs | 6 ++-- src/console/src/auth/register.rs | 2 +- src/libs/auth/src/delegation/get.rs | 2 +- src/libs/auth/src/delegation/impls.rs | 2 +- src/libs/auth/src/delegation/prepare.rs | 2 +- src/libs/auth/src/delegation/utils/seed.rs | 4 +-- src/libs/auth/src/openid/impls.rs | 28 --------------- src/libs/auth/src/openid/mod.rs | 5 ++- src/libs/auth/src/openid/types.rs | 35 ------------------- src/libs/auth/src/openid/user/impls.rs | 28 +++++++++++++++ src/libs/auth/src/openid/user/mod.rs | 5 +++ src/libs/auth/src/openid/user/types.rs | 34 ++++++++++++++++++ .../openid/{verify/user.rs => user/verify.rs} | 16 ++++----- src/libs/auth/src/openid/verify/mod.rs | 5 --- src/libs/auth/src/openid/workload/mod.rs | 4 +++ src/libs/auth/src/openid/workload/types.rs | 13 +++++++ .../workload.rs => workload/verify.rs} | 26 ++++++-------- src/libs/satellite/src/auth/delegation.rs | 6 ++-- src/libs/satellite/src/auth/register.rs | 2 +- .../satellite/src/controllers/authenticate.rs | 24 +++++++------ src/libs/satellite/src/user/core/impls.rs | 2 +- 22 files changed, 133 insertions(+), 120 deletions(-) create mode 100644 src/libs/auth/src/openid/user/impls.rs create mode 100644 src/libs/auth/src/openid/user/mod.rs create mode 100644 src/libs/auth/src/openid/user/types.rs rename src/libs/auth/src/openid/{verify/user.rs => user/verify.rs} (85%) delete mode 100644 src/libs/auth/src/openid/verify/mod.rs create mode 100644 src/libs/auth/src/openid/workload/mod.rs create mode 100644 src/libs/auth/src/openid/workload/types.rs rename src/libs/auth/src/openid/{verify/workload.rs => workload/verify.rs} (63%) diff --git a/src/console/src/accounts/impls.rs b/src/console/src/accounts/impls.rs index 9a5e527a4c..b006f6da2e 100644 --- a/src/console/src/accounts/impls.rs +++ b/src/console/src/accounts/impls.rs @@ -1,7 +1,7 @@ use crate::constants::E8S_PER_ICP; use crate::types::state::{Account, OpenIdData, Provider}; use ic_cdk::api::time; -use junobuild_auth::openid::types::interface::OpenIdCredential; +use junobuild_auth::openid::user::types::interface::OpenIdCredential; use junobuild_auth::profile::types::OpenIdProfile; use junobuild_shared::types::state::{MissionControlId, UserId}; diff --git a/src/console/src/auth/delegation.rs b/src/console/src/auth/delegation.rs index 699a9b7b22..c9ef797e3c 100644 --- a/src/console/src/auth/delegation.rs +++ b/src/console/src/auth/delegation.rs @@ -4,8 +4,8 @@ use junobuild_auth::delegation::types::{ GetDelegationError, GetDelegationResult, OpenIdGetDelegationArgs, OpenIdPrepareDelegationArgs, PrepareDelegationError, PreparedDelegation, }; -use junobuild_auth::openid::types::interface::OpenIdCredential; use junobuild_auth::openid::types::provider::OpenIdProvider; +use junobuild_auth::openid::user::types::interface::OpenIdCredential; use junobuild_auth::state::types::config::OpenIdProviders; use junobuild_auth::{delegation, openid}; @@ -16,7 +16,7 @@ pub async fn openid_prepare_delegation( args: &OpenIdPrepareDelegationArgs, providers: &OpenIdProviders, ) -> OpenIdPrepareDelegationResult { - let (credential, provider) = match openid::verify_user_openid_credentials_with_jwks_renewal( + let (credential, provider) = match openid::user::verify_openid_credentials_with_jwks_renewal( &args.jwt, &args.salt, providers, &AuthHeap, ) .await @@ -40,7 +40,7 @@ pub fn openid_get_delegation( args: &OpenIdGetDelegationArgs, providers: &OpenIdProviders, ) -> GetDelegationResult { - let (credential, provider) = match openid::verify_user_openid_credentials_with_cached_jwks( + let (credential, provider) = match openid::user::verify_openid_credentials_with_cached_jwks( &args.jwt, &args.salt, providers, &AuthHeap, ) { Ok(value) => value, diff --git a/src/console/src/auth/register.rs b/src/console/src/auth/register.rs index 730b455dc7..dcb55f6df9 100644 --- a/src/console/src/auth/register.rs +++ b/src/console/src/auth/register.rs @@ -3,8 +3,8 @@ use crate::types::state::{Account, OpenIdData, Provider}; use crate::types::state::{OpenId, OpenIdAuthProvider}; use candid::Principal; use junobuild_auth::delegation::types::UserKey; -use junobuild_auth::openid::types::interface::OpenIdCredential; use junobuild_auth::openid::types::provider::OpenIdProvider; +use junobuild_auth::openid::user::types::interface::OpenIdCredential; pub async fn register_account( public_key: &UserKey, diff --git a/src/libs/auth/src/delegation/get.rs b/src/libs/auth/src/delegation/get.rs index 29801c37e7..489bc8168e 100644 --- a/src/libs/auth/src/delegation/get.rs +++ b/src/libs/auth/src/delegation/get.rs @@ -4,8 +4,8 @@ use crate::delegation::types::{ use crate::delegation::utils::seed::calculate_seed; use crate::delegation::utils::signature::{build_signature_inputs, build_signature_msg}; use crate::delegation::utils::targets::build_targets; -use crate::openid::types::interface::{OpenIdCredential, OpenIdCredentialKey}; use crate::openid::types::provider::OpenIdProvider; +use crate::openid::user::types::interface::{OpenIdCredential, OpenIdCredentialKey}; use crate::state::get_salt; use crate::state::services::read_state; use crate::strategies::{AuthCertificateStrategy, AuthHeapStrategy}; diff --git a/src/libs/auth/src/delegation/impls.rs b/src/libs/auth/src/delegation/impls.rs index 699cd97fb3..564bee5d9a 100644 --- a/src/libs/auth/src/delegation/impls.rs +++ b/src/libs/auth/src/delegation/impls.rs @@ -1,5 +1,5 @@ use crate::delegation::types::{GetDelegationError, PrepareDelegationError}; -use crate::openid::types::errors::VerifyOpenidCredentialsError; +use crate::openid::user::types::errors::VerifyOpenidCredentialsError; impl From for GetDelegationError { fn from(e: VerifyOpenidCredentialsError) -> Self { diff --git a/src/libs/auth/src/delegation/prepare.rs b/src/libs/auth/src/delegation/prepare.rs index 9a61d458bf..408d227256 100644 --- a/src/libs/auth/src/delegation/prepare.rs +++ b/src/libs/auth/src/delegation/prepare.rs @@ -6,8 +6,8 @@ use crate::delegation::utils::duration::build_expiration; use crate::delegation::utils::seed::calculate_seed; use crate::delegation::utils::signature::{build_signature_inputs, build_signature_msg}; use crate::delegation::utils::targets::build_targets; -use crate::openid::types::interface::{OpenIdCredential, OpenIdCredentialKey}; use crate::openid::types::provider::OpenIdProvider; +use crate::openid::user::types::interface::{OpenIdCredential, OpenIdCredentialKey}; use crate::state::get_salt; use crate::state::services::mutate_state; use crate::strategies::{AuthCertificateStrategy, AuthHeapStrategy}; diff --git a/src/libs/auth/src/delegation/utils/seed.rs b/src/libs/auth/src/delegation/utils/seed.rs index 7ef1f44162..6efd35777b 100644 --- a/src/libs/auth/src/delegation/utils/seed.rs +++ b/src/libs/auth/src/delegation/utils/seed.rs @@ -1,4 +1,4 @@ -use crate::openid::types::interface::OpenIdCredentialKey; +use crate::openid::user::types::interface::OpenIdCredentialKey; use crate::state::types::state::Salt; use ic_certification::Hash; use sha2::{Digest, Sha256}; @@ -30,7 +30,7 @@ fn hash_bytes(value: impl AsRef<[u8]>) -> Hash { #[cfg(test)] mod tests { use super::calculate_seed; - use crate::openid::types::interface::OpenIdCredentialKey; + use crate::openid::user::types::interface::OpenIdCredentialKey; use crate::state::types::state::Salt; use ic_certification::Hash; use sha2::{Digest, Sha256}; diff --git a/src/libs/auth/src/openid/impls.rs b/src/libs/auth/src/openid/impls.rs index cb9378d775..064dd6e21e 100644 --- a/src/libs/auth/src/openid/impls.rs +++ b/src/libs/auth/src/openid/impls.rs @@ -1,38 +1,10 @@ use crate::openid::jwt::types::cert::Jwks; -use crate::openid::jwt::types::token::Claims; -use crate::openid::types::interface::{OpenIdCredential, OpenIdCredentialKey}; use crate::openid::types::provider::{OpenIdCertificate, OpenIdProvider}; use ic_cdk::api::time; -use jsonwebtoken::TokenData; use junobuild_shared::data::version::next_version; use junobuild_shared::types::state::{Version, Versioned}; use std::fmt::{Display, Formatter, Result as FmtResult}; -impl From> for OpenIdCredential { - fn from(token: TokenData) -> Self { - Self { - sub: token.claims.sub, - iss: token.claims.iss, - email: token.claims.email, - name: token.claims.name, - given_name: token.claims.given_name, - family_name: token.claims.family_name, - preferred_username: token.claims.preferred_username, - picture: token.claims.picture, - locale: token.claims.locale, - } - } -} - -impl<'a> From<&'a OpenIdCredential> for OpenIdCredentialKey<'a> { - fn from(credential: &'a OpenIdCredential) -> Self { - Self { - sub: &credential.sub, - iss: &credential.iss, - } - } -} - impl OpenIdProvider { pub fn jwks_url(&self) -> &'static str { match self { diff --git a/src/libs/auth/src/openid/mod.rs b/src/libs/auth/src/openid/mod.rs index bce9aa14e3..c31a2a0561 100644 --- a/src/libs/auth/src/openid/mod.rs +++ b/src/libs/auth/src/openid/mod.rs @@ -2,7 +2,6 @@ mod impls; pub mod jwkset; pub mod jwt; pub mod types; +pub mod user; mod utils; -mod verify; - -pub use verify::*; +pub mod workload; diff --git a/src/libs/auth/src/openid/types.rs b/src/libs/auth/src/openid/types.rs index 0e944d8365..b4cce5e646 100644 --- a/src/libs/auth/src/openid/types.rs +++ b/src/libs/auth/src/openid/types.rs @@ -1,38 +1,3 @@ -pub mod interface { - pub struct OpenIdCredentialKey<'a> { - pub iss: &'a String, - pub sub: &'a String, - } - - pub struct OpenIdCredential { - pub iss: String, - pub sub: String, - - pub email: Option, - pub name: Option, - pub given_name: Option, - pub family_name: Option, - pub preferred_username: Option, - pub picture: Option, - pub locale: Option, - } -} - -pub(crate) mod errors { - use crate::openid::jwkset::types::errors::GetOrRefreshJwksError; - use crate::openid::jwt::types::errors::{JwtFindProviderError, JwtVerifyError}; - use candid::{CandidType, Deserialize}; - use serde::Serialize; - - #[derive(CandidType, Serialize, Deserialize, Debug)] - pub enum VerifyOpenidCredentialsError { - GetOrFetchJwks(GetOrRefreshJwksError), - GetCachedJwks, - JwtFindProvider(JwtFindProviderError), - JwtVerify(JwtVerifyError), - } -} - pub mod provider { use crate::openid::jwt::types::cert::Jwks; use candid::{CandidType, Deserialize}; diff --git a/src/libs/auth/src/openid/user/impls.rs b/src/libs/auth/src/openid/user/impls.rs new file mode 100644 index 0000000000..0477724ab3 --- /dev/null +++ b/src/libs/auth/src/openid/user/impls.rs @@ -0,0 +1,28 @@ +use crate::openid::jwt::types::token::Claims; +use crate::openid::user::types::interface::{OpenIdCredential, OpenIdCredentialKey}; +use jsonwebtoken::TokenData; + +impl From> for OpenIdCredential { + fn from(token: TokenData) -> Self { + Self { + sub: token.claims.sub, + iss: token.claims.iss, + email: token.claims.email, + name: token.claims.name, + given_name: token.claims.given_name, + family_name: token.claims.family_name, + preferred_username: token.claims.preferred_username, + picture: token.claims.picture, + locale: token.claims.locale, + } + } +} + +impl<'a> From<&'a OpenIdCredential> for OpenIdCredentialKey<'a> { + fn from(credential: &'a OpenIdCredential) -> Self { + Self { + sub: &credential.sub, + iss: &credential.iss, + } + } +} diff --git a/src/libs/auth/src/openid/user/mod.rs b/src/libs/auth/src/openid/user/mod.rs new file mode 100644 index 0000000000..35c6668fcf --- /dev/null +++ b/src/libs/auth/src/openid/user/mod.rs @@ -0,0 +1,5 @@ +mod impls; +pub mod types; +mod verify; + +pub use verify::*; diff --git a/src/libs/auth/src/openid/user/types.rs b/src/libs/auth/src/openid/user/types.rs new file mode 100644 index 0000000000..af0991b32c --- /dev/null +++ b/src/libs/auth/src/openid/user/types.rs @@ -0,0 +1,34 @@ +pub mod interface { + pub struct OpenIdCredentialKey<'a> { + pub iss: &'a String, + pub sub: &'a String, + } + + pub struct OpenIdCredential { + pub iss: String, + pub sub: String, + + pub email: Option, + pub name: Option, + pub given_name: Option, + pub family_name: Option, + pub preferred_username: Option, + pub picture: Option, + pub locale: Option, + } +} + +pub(crate) mod errors { + use crate::openid::jwkset::types::errors::GetOrRefreshJwksError; + use crate::openid::jwt::types::errors::{JwtFindProviderError, JwtVerifyError}; + use candid::{CandidType, Deserialize}; + use serde::Serialize; + + #[derive(CandidType, Serialize, Deserialize, Debug)] + pub enum VerifyOpenidCredentialsError { + GetOrFetchJwks(GetOrRefreshJwksError), + GetCachedJwks, + JwtFindProvider(JwtFindProviderError), + JwtVerify(JwtVerifyError), + } +} diff --git a/src/libs/auth/src/openid/verify/user.rs b/src/libs/auth/src/openid/user/verify.rs similarity index 85% rename from src/libs/auth/src/openid/verify/user.rs rename to src/libs/auth/src/openid/user/verify.rs index 558af934f0..e3bfe85233 100644 --- a/src/libs/auth/src/openid/verify/user.rs +++ b/src/libs/auth/src/openid/user/verify.rs @@ -3,23 +3,23 @@ use crate::openid::jwt::types::cert::Jwks; use crate::openid::jwt::types::errors::JwtVerifyError; use crate::openid::jwt::types::token::Claims; use crate::openid::jwt::{unsafe_find_jwt_provider, verify_openid_jwt}; -use crate::openid::types::errors::VerifyOpenidCredentialsError; -use crate::openid::types::interface::OpenIdCredential; use crate::openid::types::provider::OpenIdProvider; +use crate::openid::user::types::errors::VerifyOpenidCredentialsError; +use crate::openid::user::types::interface::OpenIdCredential; use crate::openid::utils::build_nonce; use crate::state::types::config::{OpenIdProviderClientId, OpenIdProviders}; use crate::state::types::state::Salt; use crate::strategies::AuthHeapStrategy; -type VerifyUserOpenIdCredentialsResult = +type VerifyOpenIdCredentialsResult = Result<(OpenIdCredential, OpenIdProvider), VerifyOpenidCredentialsError>; -pub async fn verify_user_openid_credentials_with_jwks_renewal( +pub async fn verify_openid_credentials_with_jwks_renewal( jwt: &str, salt: &Salt, providers: &OpenIdProviders, auth_heap: &impl AuthHeapStrategy, -) -> VerifyUserOpenIdCredentialsResult { +) -> VerifyOpenIdCredentialsResult { let (provider, config) = unsafe_find_jwt_provider(providers, jwt) .map_err(VerifyOpenidCredentialsError::JwtFindProvider)?; @@ -30,12 +30,12 @@ pub async fn verify_user_openid_credentials_with_jwks_renewal( verify_openid_credentials(jwt, &jwks, &provider, &config.client_id, salt) } -pub fn verify_user_openid_credentials_with_cached_jwks( +pub fn verify_openid_credentials_with_cached_jwks( jwt: &str, salt: &Salt, providers: &OpenIdProviders, auth_heap: &impl AuthHeapStrategy, -) -> VerifyUserOpenIdCredentialsResult { +) -> VerifyOpenIdCredentialsResult { let (provider, config) = unsafe_find_jwt_provider(providers, jwt) .map_err(VerifyOpenidCredentialsError::JwtFindProvider)?; @@ -50,7 +50,7 @@ fn verify_openid_credentials( provider: &OpenIdProvider, client_id: &OpenIdProviderClientId, salt: &Salt, -) -> VerifyUserOpenIdCredentialsResult { +) -> VerifyOpenIdCredentialsResult { let assert_audience = |claims: &Claims| -> Result<(), JwtVerifyError> { if claims.aud != client_id.as_str() { return Err(JwtVerifyError::BadClaim("aud".to_string())); diff --git a/src/libs/auth/src/openid/verify/mod.rs b/src/libs/auth/src/openid/verify/mod.rs deleted file mode 100644 index d325f64617..0000000000 --- a/src/libs/auth/src/openid/verify/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod user; -mod workload; - -pub use user::*; -pub use workload::*; \ No newline at end of file diff --git a/src/libs/auth/src/openid/workload/mod.rs b/src/libs/auth/src/openid/workload/mod.rs new file mode 100644 index 0000000000..0e0c4c0cde --- /dev/null +++ b/src/libs/auth/src/openid/workload/mod.rs @@ -0,0 +1,4 @@ +mod types; +mod verify; + +pub use verify::*; diff --git a/src/libs/auth/src/openid/workload/types.rs b/src/libs/auth/src/openid/workload/types.rs new file mode 100644 index 0000000000..1f8d6b2f34 --- /dev/null +++ b/src/libs/auth/src/openid/workload/types.rs @@ -0,0 +1,13 @@ +pub(crate) mod errors { + use crate::openid::jwkset::types::errors::GetOrRefreshJwksError; + use crate::openid::jwt::types::errors::JwtVerifyError; + use candid::{CandidType, Deserialize}; + use serde::Serialize; + + #[derive(CandidType, Serialize, Deserialize, Debug)] + pub enum VerifyOpenidWorkloadCredentialsError { + GetOrFetchJwks(GetOrRefreshJwksError), + GetCachedJwks, + JwtVerify(JwtVerifyError), + } +} diff --git a/src/libs/auth/src/openid/verify/workload.rs b/src/libs/auth/src/openid/workload/verify.rs similarity index 63% rename from src/libs/auth/src/openid/verify/workload.rs rename to src/libs/auth/src/openid/workload/verify.rs index 4733b4ee65..9727dc34ea 100644 --- a/src/libs/auth/src/openid/verify/workload.rs +++ b/src/libs/auth/src/openid/workload/verify.rs @@ -2,27 +2,23 @@ use crate::openid::jwkset::get_or_refresh_jwks; use crate::openid::jwt::types::cert::Jwks; use crate::openid::jwt::types::errors::JwtVerifyError; use crate::openid::jwt::types::token::Claims; -use crate::openid::jwt::{unsafe_find_jwt_provider, verify_openid_jwt}; -use crate::openid::types::errors::VerifyOpenidCredentialsError; -use crate::openid::types::interface::OpenIdCredential; +use crate::openid::jwt::verify_openid_jwt; use crate::openid::types::provider::OpenIdProvider; -use crate::state::types::config::OpenIdProviders; +use crate::openid::user::types::interface::OpenIdCredential; +use crate::openid::workload::types::errors::VerifyOpenidWorkloadCredentialsError; use crate::strategies::AuthHeapStrategy; -type VerifyWorkloadOpenIdCredentialsResult = - Result<(OpenIdCredential, OpenIdProvider), VerifyOpenidCredentialsError>; +type VerifyOpenIdWorkloadCredentialsResult = + Result<(OpenIdCredential, OpenIdProvider), VerifyOpenidWorkloadCredentialsError>; -pub async fn verify_workload_openid_credentials_with_jwks_renewal( +pub async fn verify_openid_credentials_with_jwks_renewal( jwt: &str, - providers: &OpenIdProviders, + provider: &OpenIdProvider, auth_heap: &impl AuthHeapStrategy, -) -> VerifyWorkloadOpenIdCredentialsResult { - let (provider, config) = unsafe_find_jwt_provider(providers, jwt) - .map_err(VerifyOpenidCredentialsError::JwtFindProvider)?; - +) -> VerifyOpenIdWorkloadCredentialsResult { let jwks = get_or_refresh_jwks(&provider, jwt, auth_heap) .await - .map_err(VerifyOpenidCredentialsError::GetOrFetchJwks)?; + .map_err(VerifyOpenidWorkloadCredentialsError::GetOrFetchJwks)?; verify_openid_credentials(jwt, &jwks, &provider) } @@ -31,7 +27,7 @@ fn verify_openid_credentials( jwt: &str, jwks: &Jwks, provider: &OpenIdProvider, -) -> VerifyWorkloadOpenIdCredentialsResult { +) -> VerifyOpenIdWorkloadCredentialsResult { let assert_audience = |claims: &Claims| -> Result<(), JwtVerifyError> { // if claims.aud != client_id.as_str() { // return Err(JwtVerifyError::BadClaim("aud".to_string())); @@ -61,7 +57,7 @@ fn verify_openid_credentials( &assert_audience, &assert_no_replay, ) - .map_err(VerifyOpenidCredentialsError::JwtVerify)?; + .map_err(VerifyOpenidWorkloadCredentialsError::JwtVerify)?; let credential = OpenIdCredential::from(token); diff --git a/src/libs/satellite/src/auth/delegation.rs b/src/libs/satellite/src/auth/delegation.rs index 699a9b7b22..c9ef797e3c 100644 --- a/src/libs/satellite/src/auth/delegation.rs +++ b/src/libs/satellite/src/auth/delegation.rs @@ -4,8 +4,8 @@ use junobuild_auth::delegation::types::{ GetDelegationError, GetDelegationResult, OpenIdGetDelegationArgs, OpenIdPrepareDelegationArgs, PrepareDelegationError, PreparedDelegation, }; -use junobuild_auth::openid::types::interface::OpenIdCredential; use junobuild_auth::openid::types::provider::OpenIdProvider; +use junobuild_auth::openid::user::types::interface::OpenIdCredential; use junobuild_auth::state::types::config::OpenIdProviders; use junobuild_auth::{delegation, openid}; @@ -16,7 +16,7 @@ pub async fn openid_prepare_delegation( args: &OpenIdPrepareDelegationArgs, providers: &OpenIdProviders, ) -> OpenIdPrepareDelegationResult { - let (credential, provider) = match openid::verify_user_openid_credentials_with_jwks_renewal( + let (credential, provider) = match openid::user::verify_openid_credentials_with_jwks_renewal( &args.jwt, &args.salt, providers, &AuthHeap, ) .await @@ -40,7 +40,7 @@ pub fn openid_get_delegation( args: &OpenIdGetDelegationArgs, providers: &OpenIdProviders, ) -> GetDelegationResult { - let (credential, provider) = match openid::verify_user_openid_credentials_with_cached_jwks( + let (credential, provider) = match openid::user::verify_openid_credentials_with_cached_jwks( &args.jwt, &args.salt, providers, &AuthHeap, ) { Ok(value) => value, diff --git a/src/libs/satellite/src/auth/register.rs b/src/libs/satellite/src/auth/register.rs index 8e01852726..c9c0c5f023 100644 --- a/src/libs/satellite/src/auth/register.rs +++ b/src/libs/satellite/src/auth/register.rs @@ -7,8 +7,8 @@ use crate::user::core::types::state::{OpenIdData, ProviderData, UserData}; use crate::Doc; use candid::Principal; use junobuild_auth::delegation::types::UserKey; -use junobuild_auth::openid::types::interface::OpenIdCredential; use junobuild_auth::openid::types::provider::OpenIdProvider; +use junobuild_auth::openid::user::types::interface::OpenIdCredential; use junobuild_collections::constants::db::COLLECTION_USER_KEY; use junobuild_collections::msg::msg_db_collection_not_found; use junobuild_shared::ic::api::id; diff --git a/src/libs/satellite/src/controllers/authenticate.rs b/src/libs/satellite/src/controllers/authenticate.rs index 6053af8e62..f0fc7253ab 100644 --- a/src/libs/satellite/src/controllers/authenticate.rs +++ b/src/libs/satellite/src/controllers/authenticate.rs @@ -1,24 +1,26 @@ -use junobuild_auth::state::get_providers; use crate::auth::strategy_impls::AuthHeap; use crate::controllers::types::OpenIdAuthenticateControllerArgs; -use junobuild_auth::{delegation, openid}; use junobuild_auth::delegation::types::PrepareDelegationError; +use junobuild_auth::openid::types::provider::OpenIdProvider; +use junobuild_auth::state::get_providers; +use junobuild_auth::{delegation, openid}; pub async fn openid_authenticate_controller( args: &OpenIdAuthenticateControllerArgs, ) -> Result<(), String> { let providers = get_providers(&AuthHeap)?; - let (credential, provider) = match openid::verify_workload_openid_credentials_with_jwks_renewal( - &args.jwt, &providers, &AuthHeap, - ) + let (credential, provider) = + match openid::workload::verify_openid_credentials_with_jwks_renewal( + &args.jwt, + &OpenIdProvider::GitHubProxy, + &AuthHeap, + ) .await - { - Ok(value) => value, - Err(err) => return Err(PrepareDelegationError::from(err)), - }; - - + { + Ok(value) => value, + Err(err) => return Err(PrepareDelegationError::from(err)), + }; Ok(()) } diff --git a/src/libs/satellite/src/user/core/impls.rs b/src/libs/satellite/src/user/core/impls.rs index d300c24a23..f310229b40 100644 --- a/src/libs/satellite/src/user/core/impls.rs +++ b/src/libs/satellite/src/user/core/impls.rs @@ -9,8 +9,8 @@ use crate::user::core::types::state::{ AuthProvider, OpenIdData, ProviderData, UserData, WebAuthnData, }; use crate::{Doc, SetDoc}; -use junobuild_auth::openid::types::interface::OpenIdCredential; use junobuild_auth::openid::types::provider::OpenIdProvider; +use junobuild_auth::openid::user::types::interface::OpenIdCredential; use junobuild_auth::profile::types::{OpenIdProfile, Validated}; use junobuild_utils::encode_doc_data; From 2baab4ae3aebfea3ee9ce7005cfb0e44d6ed0bb0 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Sun, 25 Jan 2026 19:56:26 +0100 Subject: [PATCH 03/32] feat: bubble error --- src/libs/auth/src/openid/workload/mod.rs | 2 +- src/libs/auth/src/openid/workload/types.rs | 2 +- src/libs/auth/src/openid/workload/verify.rs | 10 ++---- .../satellite/src/controllers/authenticate.rs | 34 +++++++++---------- src/libs/satellite/src/controllers/types.rs | 7 ++++ 5 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/libs/auth/src/openid/workload/mod.rs b/src/libs/auth/src/openid/workload/mod.rs index 0e0c4c0cde..d9a483abcc 100644 --- a/src/libs/auth/src/openid/workload/mod.rs +++ b/src/libs/auth/src/openid/workload/mod.rs @@ -1,4 +1,4 @@ -mod types; +pub mod types; mod verify; pub use verify::*; diff --git a/src/libs/auth/src/openid/workload/types.rs b/src/libs/auth/src/openid/workload/types.rs index 1f8d6b2f34..1f7306ef36 100644 --- a/src/libs/auth/src/openid/workload/types.rs +++ b/src/libs/auth/src/openid/workload/types.rs @@ -1,4 +1,4 @@ -pub(crate) mod errors { +pub mod errors { use crate::openid::jwkset::types::errors::GetOrRefreshJwksError; use crate::openid::jwt::types::errors::JwtVerifyError; use candid::{CandidType, Deserialize}; diff --git a/src/libs/auth/src/openid/workload/verify.rs b/src/libs/auth/src/openid/workload/verify.rs index 9727dc34ea..64eaeb86dc 100644 --- a/src/libs/auth/src/openid/workload/verify.rs +++ b/src/libs/auth/src/openid/workload/verify.rs @@ -4,12 +4,10 @@ use crate::openid::jwt::types::errors::JwtVerifyError; use crate::openid::jwt::types::token::Claims; use crate::openid::jwt::verify_openid_jwt; use crate::openid::types::provider::OpenIdProvider; -use crate::openid::user::types::interface::OpenIdCredential; use crate::openid::workload::types::errors::VerifyOpenidWorkloadCredentialsError; use crate::strategies::AuthHeapStrategy; -type VerifyOpenIdWorkloadCredentialsResult = - Result<(OpenIdCredential, OpenIdProvider), VerifyOpenidWorkloadCredentialsError>; +type VerifyOpenIdWorkloadCredentialsResult = Result<(), VerifyOpenidWorkloadCredentialsError>; pub async fn verify_openid_credentials_with_jwks_renewal( jwt: &str, @@ -50,7 +48,7 @@ fn verify_openid_credentials( Ok(()) }; - let token = verify_openid_jwt( + verify_openid_jwt( jwt, provider.issuers(), &jwks.keys, @@ -59,7 +57,5 @@ fn verify_openid_credentials( ) .map_err(VerifyOpenidWorkloadCredentialsError::JwtVerify)?; - let credential = OpenIdCredential::from(token); - - Ok((credential, provider.clone())) + Ok(()) } diff --git a/src/libs/satellite/src/controllers/authenticate.rs b/src/libs/satellite/src/controllers/authenticate.rs index f0fc7253ab..b8111c515f 100644 --- a/src/libs/satellite/src/controllers/authenticate.rs +++ b/src/libs/satellite/src/controllers/authenticate.rs @@ -1,26 +1,24 @@ use crate::auth::strategy_impls::AuthHeap; -use crate::controllers::types::OpenIdAuthenticateControllerArgs; -use junobuild_auth::delegation::types::PrepareDelegationError; +use crate::controllers::types::{AuthenticateControllerError, OpenIdAuthenticateControllerArgs}; +use junobuild_auth::openid; use junobuild_auth::openid::types::provider::OpenIdProvider; -use junobuild_auth::state::get_providers; -use junobuild_auth::{delegation, openid}; + +pub type OpenIdAuthenticateControllerResult = Result<(), AuthenticateControllerError>; pub async fn openid_authenticate_controller( args: &OpenIdAuthenticateControllerArgs, -) -> Result<(), String> { - let providers = get_providers(&AuthHeap)?; - - let (credential, provider) = - match openid::workload::verify_openid_credentials_with_jwks_renewal( - &args.jwt, - &OpenIdProvider::GitHubProxy, - &AuthHeap, - ) - .await - { - Ok(value) => value, - Err(err) => return Err(PrepareDelegationError::from(err)), - }; +) -> OpenIdAuthenticateControllerResult { + match openid::workload::verify_openid_credentials_with_jwks_renewal( + &args.jwt, + // TODO: GitHubActions + &OpenIdProvider::GitHubProxy, + &AuthHeap, + ) + .await + { + Ok(value) => value, + Err(err) => return Err(AuthenticateControllerError::VerifyOpenIdCredentials(err)), + }; Ok(()) } diff --git a/src/libs/satellite/src/controllers/types.rs b/src/libs/satellite/src/controllers/types.rs index 7ad2f47de9..fbc5671a3b 100644 --- a/src/libs/satellite/src/controllers/types.rs +++ b/src/libs/satellite/src/controllers/types.rs @@ -1,4 +1,6 @@ use candid::{CandidType, Deserialize}; +use junobuild_auth::delegation::types::PrepareDelegationError; +use junobuild_auth::openid::workload::types::errors::VerifyOpenidWorkloadCredentialsError; use junobuild_shared::types::state::Metadata; use serde::Serialize; @@ -20,3 +22,8 @@ pub enum GrantableScope { Write, Submit, } + +#[derive(CandidType, Serialize, Deserialize)] +pub enum AuthenticateControllerError { + VerifyOpenIdCredentials(VerifyOpenidWorkloadCredentialsError), +} From 8e7b6efecbec878de73afe78034d0dd52512159f Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 28 Jan 2026 09:02:46 +0100 Subject: [PATCH 04/32] feat: actions --- src/console/src/impls.rs | 14 +++++++---- src/libs/auth/src/openid/impls.rs | 5 +++- src/libs/auth/src/openid/types.rs | 1 + src/libs/satellite/src/api/controllers.rs | 6 +++-- src/libs/satellite/src/auth/register.rs | 2 +- .../satellite/src/controllers/authenticate.rs | 20 ++++++++-------- src/libs/satellite/src/controllers/types.rs | 3 ++- src/libs/satellite/src/user/core/impls.rs | 23 ++++++++++++------- 8 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/console/src/impls.rs b/src/console/src/impls.rs index 1ab673dfcb..b46f85a3e8 100644 --- a/src/console/src/impls.rs +++ b/src/console/src/impls.rs @@ -194,11 +194,17 @@ impl Fee { } } -impl From<&OpenIdProvider> for OpenIdAuthProvider { - fn from(provider: &OpenIdProvider) -> Self { +impl TryFrom<&OpenIdProvider> for OpenIdAuthProvider { + type Error = String; + + fn try_from(provider: &OpenIdProvider) -> Result { match provider { - OpenIdProvider::Google => OpenIdAuthProvider::Google, - OpenIdProvider::GitHubProxy => OpenIdAuthProvider::GitHub, + OpenIdProvider::Google => Ok(OpenIdAuthProvider::Google), + OpenIdProvider::GitHubProxy => Ok(OpenIdAuthProvider::GitHub), + _ => Err(format!( + "{:?} is not supported for user authentication", + provider + )), } } } diff --git a/src/libs/auth/src/openid/impls.rs b/src/libs/auth/src/openid/impls.rs index 064dd6e21e..c13a7a7c61 100644 --- a/src/libs/auth/src/openid/impls.rs +++ b/src/libs/auth/src/openid/impls.rs @@ -12,6 +12,7 @@ impl OpenIdProvider { // Swap for local development with the Juno API: // http://host.docker.internal:3000/v1/auth/certs Self::GitHubProxy => "https://api.juno.build/v1/auth/certs", + Self::GitHubActions => "https://token.actions.githubusercontent.com/.well-known/jwks", } } @@ -19,6 +20,7 @@ impl OpenIdProvider { match self { OpenIdProvider::Google => &["https://accounts.google.com", "accounts.google.com"], OpenIdProvider::GitHubProxy => &["https://api.juno.build/auth/github"], + OpenIdProvider::GitHubActions => &["https://token.actions.githubusercontent.com"], } } } @@ -65,7 +67,8 @@ impl Display for OpenIdProvider { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { match self { OpenIdProvider::Google => write!(f, "Google"), - OpenIdProvider::GitHubProxy => write!(f, "GitHub"), + OpenIdProvider::GitHubProxy => write!(f, "GitHub Proxy"), + OpenIdProvider::GitHubActions => write!(f, "GitHub Actions"), } } } diff --git a/src/libs/auth/src/openid/types.rs b/src/libs/auth/src/openid/types.rs index b4cce5e646..cf6d15655d 100644 --- a/src/libs/auth/src/openid/types.rs +++ b/src/libs/auth/src/openid/types.rs @@ -10,6 +10,7 @@ pub mod provider { pub enum OpenIdProvider { Google, GitHubProxy, + GitHubActions, } #[derive(CandidType, Serialize, Deserialize, Clone)] diff --git a/src/libs/satellite/src/api/controllers.rs b/src/libs/satellite/src/api/controllers.rs index 556ef1f9d4..6df4f040d7 100644 --- a/src/libs/satellite/src/api/controllers.rs +++ b/src/libs/satellite/src/api/controllers.rs @@ -1,6 +1,6 @@ use crate::controllers::openid_authenticate_controller; use crate::controllers::store::{delete_controllers, set_controllers as set_controllers_store}; -use crate::controllers::types::AuthenticateControllerArgs; +use crate::controllers::types::{AuthenticateControllerArgs, OpenIdAuthenticateControllerResult}; use crate::{get_admin_controllers, get_controllers}; use ic_cdk::trap; use junobuild_shared::constants::shared::MAX_NUMBER_OF_SATELLITE_CONTROLLERS; @@ -52,7 +52,9 @@ pub fn list_controllers() -> Controllers { get_controllers() } -pub async fn authenticate_controller(args: AuthenticateControllerArgs) { +pub async fn authenticate_controller( + args: AuthenticateControllerArgs, +) -> OpenIdAuthenticateControllerResult { match args { AuthenticateControllerArgs::OpenId(args) => { openid_authenticate_controller(&args).await.unwrap_or_trap() diff --git a/src/libs/satellite/src/auth/register.rs b/src/libs/satellite/src/auth/register.rs index c9c0c5f023..2eb33b00c8 100644 --- a/src/libs/satellite/src/auth/register.rs +++ b/src/libs/satellite/src/auth/register.rs @@ -83,7 +83,7 @@ pub fn register_user( // Create or update the user. let user_data: UserData = UserData { banned, - provider: Some(provider.into()), + provider: Some(provider.try_into()?), provider_data: Some(ProviderData::OpenId(provider_data)), }; diff --git a/src/libs/satellite/src/controllers/authenticate.rs b/src/libs/satellite/src/controllers/authenticate.rs index b8111c515f..e5d41ccdd4 100644 --- a/src/libs/satellite/src/controllers/authenticate.rs +++ b/src/libs/satellite/src/controllers/authenticate.rs @@ -1,24 +1,24 @@ use crate::auth::strategy_impls::AuthHeap; -use crate::controllers::types::{AuthenticateControllerError, OpenIdAuthenticateControllerArgs}; +use crate::controllers::types::{ + AuthenticateControllerError, OpenIdAuthenticateControllerArgs, + OpenIdAuthenticateControllerResult, +}; use junobuild_auth::openid; use junobuild_auth::openid::types::provider::OpenIdProvider; -pub type OpenIdAuthenticateControllerResult = Result<(), AuthenticateControllerError>; - pub async fn openid_authenticate_controller( args: &OpenIdAuthenticateControllerArgs, -) -> OpenIdAuthenticateControllerResult { - match openid::workload::verify_openid_credentials_with_jwks_renewal( +) -> Result { + let result = match openid::workload::verify_openid_credentials_with_jwks_renewal( &args.jwt, - // TODO: GitHubActions - &OpenIdProvider::GitHubProxy, + &OpenIdProvider::GitHubActions, &AuthHeap, ) .await { - Ok(value) => value, - Err(err) => return Err(AuthenticateControllerError::VerifyOpenIdCredentials(err)), + Ok(_) => Ok(()), + Err(err) => Err(AuthenticateControllerError::VerifyOpenIdCredentials(err)), }; - Ok(()) + Ok(result) } diff --git a/src/libs/satellite/src/controllers/types.rs b/src/libs/satellite/src/controllers/types.rs index fbc5671a3b..058eb09cbd 100644 --- a/src/libs/satellite/src/controllers/types.rs +++ b/src/libs/satellite/src/controllers/types.rs @@ -1,5 +1,4 @@ use candid::{CandidType, Deserialize}; -use junobuild_auth::delegation::types::PrepareDelegationError; use junobuild_auth::openid::workload::types::errors::VerifyOpenidWorkloadCredentialsError; use junobuild_shared::types::state::Metadata; use serde::Serialize; @@ -27,3 +26,5 @@ pub enum GrantableScope { pub enum AuthenticateControllerError { VerifyOpenIdCredentials(VerifyOpenidWorkloadCredentialsError), } + +pub type OpenIdAuthenticateControllerResult = Result<(), AuthenticateControllerError>; diff --git a/src/libs/satellite/src/user/core/impls.rs b/src/libs/satellite/src/user/core/impls.rs index f310229b40..c8b19cc7cf 100644 --- a/src/libs/satellite/src/user/core/impls.rs +++ b/src/libs/satellite/src/user/core/impls.rs @@ -160,11 +160,17 @@ impl From<&OpenIdCredential> for OpenIdData { } } -impl From<&OpenIdProvider> for AuthProvider { - fn from(provider: &OpenIdProvider) -> Self { +impl TryFrom<&OpenIdProvider> for AuthProvider { + type Error = String; + + fn try_from(provider: &OpenIdProvider) -> Result { match provider { - OpenIdProvider::Google => AuthProvider::Google, - OpenIdProvider::GitHubProxy => AuthProvider::GitHub, + OpenIdProvider::Google => Ok(AuthProvider::Google), + OpenIdProvider::GitHubProxy => Ok(AuthProvider::GitHub), + _ => Err(format!( + "{:?} is not supported for user authentication", + provider + )), } } } @@ -340,12 +346,13 @@ mod tests { #[test] fn test_openid_provider_to_auth_provider() { assert!(matches!( - AuthProvider::from(&OpenIdProvider::Google), - AuthProvider::Google + AuthProvider::try_from(&OpenIdProvider::Google), + Ok(AuthProvider::Google) )); assert!(matches!( - AuthProvider::from(&OpenIdProvider::GitHubProxy), - AuthProvider::GitHub + AuthProvider::try_from(&OpenIdProvider::GitHubProxy), + Ok(AuthProvider::GitHub) )); + assert!(AuthProvider::try_from(&OpenIdProvider::GitHubActions).is_err()); } } From e43ac030ba3ec42285961c618eda6b0ba174dd08 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 28 Jan 2026 09:19:06 +0100 Subject: [PATCH 05/32] feat: set controller --- .../satellite/src/controllers/authenticate.rs | 36 ++++++++++++++++++- .../satellite/src/controllers/constants.rs | 7 ++++ src/libs/satellite/src/controllers/impls.rs | 11 ++++++ src/libs/satellite/src/controllers/mod.rs | 2 ++ src/libs/satellite/src/controllers/types.rs | 5 +-- 5 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 src/libs/satellite/src/controllers/constants.rs create mode 100644 src/libs/satellite/src/controllers/impls.rs diff --git a/src/libs/satellite/src/controllers/authenticate.rs b/src/libs/satellite/src/controllers/authenticate.rs index e5d41ccdd4..3d0242c0ac 100644 --- a/src/libs/satellite/src/controllers/authenticate.rs +++ b/src/libs/satellite/src/controllers/authenticate.rs @@ -1,10 +1,17 @@ use crate::auth::strategy_impls::AuthHeap; +use crate::controllers::constants::{DEFAULT_CONTROLLER_DURATION_NS, MAX_CONTROLLER_DURATION_NS}; +use crate::controllers::store::set_controllers; use crate::controllers::types::{ AuthenticateControllerError, OpenIdAuthenticateControllerArgs, OpenIdAuthenticateControllerResult, }; +use ic_cdk::api::time; use junobuild_auth::openid; use junobuild_auth::openid::types::provider::OpenIdProvider; +use junobuild_shared::segments::controllers::assert_controllers; +use junobuild_shared::types::interface::SetController; +use junobuild_shared::types::state::ControllerId; +use std::cmp::min; pub async fn openid_authenticate_controller( args: &OpenIdAuthenticateControllerArgs, @@ -16,9 +23,36 @@ pub async fn openid_authenticate_controller( ) .await { - Ok(_) => Ok(()), + Ok(_) => { + authenticate_controller(args)?; + Ok(()) + } Err(err) => Err(AuthenticateControllerError::VerifyOpenIdCredentials(err)), }; Ok(result) } + +fn authenticate_controller(args: &OpenIdAuthenticateControllerArgs) -> Result<(), String> { + let controllers: [ControllerId; 1] = [args.controller_id.clone()]; + + assert_controllers(&controllers)?; + + // TODO: Assert do not exist + + let expires_at = min( + args.max_time_to_live + .unwrap_or(DEFAULT_CONTROLLER_DURATION_NS), + MAX_CONTROLLER_DURATION_NS, + ); + + let controller: SetController = SetController { + scope: args.scope.clone().into(), + metadata: args.metadata.clone(), + expires_at: Some(time().saturating_add(expires_at)), + }; + + set_controllers(&controllers, &controller); + + Ok(()) +} diff --git a/src/libs/satellite/src/controllers/constants.rs b/src/libs/satellite/src/controllers/constants.rs new file mode 100644 index 0000000000..5e2a474a40 --- /dev/null +++ b/src/libs/satellite/src/controllers/constants.rs @@ -0,0 +1,7 @@ +const MINUTE_NS: u64 = 60 * 1_000_000_000; + +// 10 minutes in nanoseconds +pub const DEFAULT_CONTROLLER_DURATION_NS: u64 = 10 * MINUTE_NS; + +// The maximum duration for a workload controller +pub const MAX_CONTROLLER_DURATION_NS: u64 = 60 * MINUTE_NS; diff --git a/src/libs/satellite/src/controllers/impls.rs b/src/libs/satellite/src/controllers/impls.rs new file mode 100644 index 0000000000..cc82bf1169 --- /dev/null +++ b/src/libs/satellite/src/controllers/impls.rs @@ -0,0 +1,11 @@ +use crate::controllers::types::GrantableScope; +use junobuild_shared::types::state::ControllerScope; + +impl From for ControllerScope { + fn from(scope: GrantableScope) -> Self { + match scope { + GrantableScope::Write => ControllerScope::Write, + GrantableScope::Submit => ControllerScope::Submit, + } + } +} diff --git a/src/libs/satellite/src/controllers/mod.rs b/src/libs/satellite/src/controllers/mod.rs index 99a8201cdf..a1406ae0cf 100644 --- a/src/libs/satellite/src/controllers/mod.rs +++ b/src/libs/satellite/src/controllers/mod.rs @@ -1,4 +1,6 @@ mod authenticate; +mod constants; +mod impls; pub mod store; pub mod types; diff --git a/src/libs/satellite/src/controllers/types.rs b/src/libs/satellite/src/controllers/types.rs index 058eb09cbd..ca2908e855 100644 --- a/src/libs/satellite/src/controllers/types.rs +++ b/src/libs/satellite/src/controllers/types.rs @@ -1,6 +1,6 @@ use candid::{CandidType, Deserialize}; use junobuild_auth::openid::workload::types::errors::VerifyOpenidWorkloadCredentialsError; -use junobuild_shared::types::state::Metadata; +use junobuild_shared::types::state::{ControllerId, Metadata}; use serde::Serialize; #[derive(CandidType, Serialize, Deserialize)] @@ -11,9 +11,10 @@ pub enum AuthenticateControllerArgs { #[derive(CandidType, Serialize, Deserialize)] pub struct OpenIdAuthenticateControllerArgs { pub jwt: String, + pub controller_id: ControllerId, + pub scope: GrantableScope, pub metadata: Metadata, pub max_time_to_live: Option, - pub scope: GrantableScope, } #[derive(CandidType, Serialize, Deserialize, Clone)] From 3164d87d8f2f870db8af8df0208078a13102f58d Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 28 Jan 2026 09:22:33 +0100 Subject: [PATCH 06/32] feat: return result --- src/libs/satellite/src/api/controllers.rs | 4 +--- .../satellite/src/controllers/authenticate.rs | 16 ++++++---------- src/libs/satellite/src/controllers/types.rs | 1 + 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/libs/satellite/src/api/controllers.rs b/src/libs/satellite/src/api/controllers.rs index 6df4f040d7..916acb9bbe 100644 --- a/src/libs/satellite/src/api/controllers.rs +++ b/src/libs/satellite/src/api/controllers.rs @@ -56,8 +56,6 @@ pub async fn authenticate_controller( args: AuthenticateControllerArgs, ) -> OpenIdAuthenticateControllerResult { match args { - AuthenticateControllerArgs::OpenId(args) => { - openid_authenticate_controller(&args).await.unwrap_or_trap() - } + AuthenticateControllerArgs::OpenId(args) => openid_authenticate_controller(&args).await, } } diff --git a/src/libs/satellite/src/controllers/authenticate.rs b/src/libs/satellite/src/controllers/authenticate.rs index 3d0242c0ac..1c70614da9 100644 --- a/src/libs/satellite/src/controllers/authenticate.rs +++ b/src/libs/satellite/src/controllers/authenticate.rs @@ -15,25 +15,21 @@ use std::cmp::min; pub async fn openid_authenticate_controller( args: &OpenIdAuthenticateControllerArgs, -) -> Result { - let result = match openid::workload::verify_openid_credentials_with_jwks_renewal( +) -> OpenIdAuthenticateControllerResult { + match openid::workload::verify_openid_credentials_with_jwks_renewal( &args.jwt, &OpenIdProvider::GitHubActions, &AuthHeap, ) .await { - Ok(_) => { - authenticate_controller(args)?; - Ok(()) - } + Ok(_) => register_controller(args) + .map_err(|err| AuthenticateControllerError::RegisterController(err.to_string())), Err(err) => Err(AuthenticateControllerError::VerifyOpenIdCredentials(err)), - }; - - Ok(result) + } } -fn authenticate_controller(args: &OpenIdAuthenticateControllerArgs) -> Result<(), String> { +fn register_controller(args: &OpenIdAuthenticateControllerArgs) -> Result<(), String> { let controllers: [ControllerId; 1] = [args.controller_id.clone()]; assert_controllers(&controllers)?; diff --git a/src/libs/satellite/src/controllers/types.rs b/src/libs/satellite/src/controllers/types.rs index ca2908e855..a5877b710f 100644 --- a/src/libs/satellite/src/controllers/types.rs +++ b/src/libs/satellite/src/controllers/types.rs @@ -26,6 +26,7 @@ pub enum GrantableScope { #[derive(CandidType, Serialize, Deserialize)] pub enum AuthenticateControllerError { VerifyOpenIdCredentials(VerifyOpenidWorkloadCredentialsError), + RegisterController(String), } pub type OpenIdAuthenticateControllerResult = Result<(), AuthenticateControllerError>; From bde258c2fac6c64e49a3ba5b3f3fd6928f257c8d Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 28 Jan 2026 09:29:14 +0100 Subject: [PATCH 07/32] feat: try_from --- src/console/src/auth/register.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/console/src/auth/register.rs b/src/console/src/auth/register.rs index dcb55f6df9..1552677139 100644 --- a/src/console/src/auth/register.rs +++ b/src/console/src/auth/register.rs @@ -42,7 +42,7 @@ pub async fn register_account( }; let provider = Provider::OpenId(OpenId { - provider: OpenIdAuthProvider::from(provider), + provider: OpenIdAuthProvider::try_from(provider)?, data: provider_data, }); From da25168661c1f24627010fc1c993507abda5f6d8 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 28 Jan 2026 09:40:07 +0100 Subject: [PATCH 08/32] feat: did --- src/console/console.did | 5 +++-- src/declarations/console/console.did.d.ts | 5 +++-- .../console/console.factory.certified.did.js | 9 +++++++-- src/declarations/console/console.factory.did.js | 9 +++++++-- src/declarations/console/console.factory.did.mjs | 9 +++++++-- src/declarations/observatory/observatory.did.d.ts | 2 +- .../observatory/observatory.factory.certified.did.js | 5 +++-- src/declarations/observatory/observatory.factory.did.js | 5 +++-- src/declarations/observatory/observatory.factory.did.mjs | 5 +++-- src/declarations/satellite/satellite.did.d.ts | 2 +- .../satellite/satellite.factory.certified.did.js | 5 +++-- src/declarations/satellite/satellite.factory.did.js | 5 +++-- src/declarations/satellite/satellite.factory.did.mjs | 5 +++-- src/declarations/sputnik/sputnik.did.d.ts | 2 +- .../sputnik/sputnik.factory.certified.did.js | 5 +++-- src/declarations/sputnik/sputnik.factory.did.js | 5 +++-- src/libs/satellite/satellite.did | 2 +- src/observatory/observatory.did | 2 +- src/satellite/satellite.did | 2 +- src/sputnik/sputnik.did | 2 +- .../declarations/test_satellite/test_satellite.did.d.ts | 2 +- .../test_satellite.factory.certified.did.js | 5 +++-- .../test_satellite/test_satellite.factory.did.js | 5 +++-- src/tests/fixtures/test_satellite/test_satellite.did | 2 +- 24 files changed, 66 insertions(+), 39 deletions(-) diff --git a/src/console/console.did b/src/console/console.did index 152d748729..b43c903d61 100644 --- a/src/console/console.did +++ b/src/console/console.did @@ -223,7 +223,8 @@ type ListSegmentsArgs = record { segment_kind : opt StorableSegmentKind; }; type Memory = variant { Heap; Stable }; -type OpenId = record { provider : OpenIdProvider; data : OpenIdData }; +type OpenId = record { provider : OpenIdAuthProvider; data : OpenIdData }; +type OpenIdAuthProvider = variant { GitHub; Google }; type OpenIdData = record { name : opt text; locale : opt text; @@ -244,7 +245,7 @@ type OpenIdPrepareDelegationArgs = record { session_key : blob; salt : blob; }; -type OpenIdProvider = variant { GitHub; Google }; +type OpenIdProvider = variant { GitHubActions; Google; GitHubProxy }; type OpenIdProviderConfig = record { delegation : opt OpenIdProviderDelegationConfig; client_id : text; diff --git a/src/declarations/console/console.did.d.ts b/src/declarations/console/console.did.d.ts index c084e35f32..88cdde866c 100644 --- a/src/declarations/console/console.did.d.ts +++ b/src/declarations/console/console.did.d.ts @@ -277,9 +277,10 @@ export interface ListSegmentsArgs { } export type Memory = { Heap: null } | { Stable: null }; export interface OpenId { - provider: OpenIdProvider; + provider: OpenIdAuthProvider; data: OpenIdData; } +export type OpenIdAuthProvider = { GitHub: null } | { Google: null }; export interface OpenIdData { name: [] | [string]; locale: [] | [string]; @@ -300,7 +301,7 @@ export interface OpenIdPrepareDelegationArgs { session_key: Uint8Array; salt: Uint8Array; } -export type OpenIdProvider = { GitHub: null } | { Google: null }; +export type OpenIdProvider = { GitHubActions: null } | { Google: null } | { GitHubProxy: null }; export interface OpenIdProviderConfig { delegation: [] | [OpenIdProviderDelegationConfig]; client_id: string; diff --git a/src/declarations/console/console.factory.certified.did.js b/src/declarations/console/console.factory.certified.did.js index ef34c851dc..3b28c5d410 100644 --- a/src/declarations/console/console.factory.certified.did.js +++ b/src/declarations/console/console.factory.certified.did.js @@ -24,7 +24,7 @@ export const idlFactory = ({ IDL }) => { user_key: IDL.Vec(IDL.Nat8), expiration: IDL.Nat64 }); - const OpenIdProvider = IDL.Variant({ + const OpenIdAuthProvider = IDL.Variant({ GitHub: IDL.Null, Google: IDL.Null }); @@ -38,7 +38,7 @@ export const idlFactory = ({ IDL }) => { preferred_username: IDL.Opt(IDL.Text) }); const OpenId = IDL.Record({ - provider: OpenIdProvider, + provider: OpenIdAuthProvider, data: OpenIdData }); const Provider = IDL.Variant({ @@ -133,6 +133,11 @@ export const idlFactory = ({ IDL }) => { const DeleteProposalAssets = IDL.Record({ proposal_ids: IDL.Vec(IDL.Nat) }); + const OpenIdProvider = IDL.Variant({ + GitHubActions: IDL.Null, + Google: IDL.Null, + GitHubProxy: IDL.Null + }); const OpenIdProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), max_time_to_live: IDL.Opt(IDL.Nat64) diff --git a/src/declarations/console/console.factory.did.js b/src/declarations/console/console.factory.did.js index 250efe59ac..7c2917ef44 100644 --- a/src/declarations/console/console.factory.did.js +++ b/src/declarations/console/console.factory.did.js @@ -24,7 +24,7 @@ export const idlFactory = ({ IDL }) => { user_key: IDL.Vec(IDL.Nat8), expiration: IDL.Nat64 }); - const OpenIdProvider = IDL.Variant({ + const OpenIdAuthProvider = IDL.Variant({ GitHub: IDL.Null, Google: IDL.Null }); @@ -38,7 +38,7 @@ export const idlFactory = ({ IDL }) => { preferred_username: IDL.Opt(IDL.Text) }); const OpenId = IDL.Record({ - provider: OpenIdProvider, + provider: OpenIdAuthProvider, data: OpenIdData }); const Provider = IDL.Variant({ @@ -133,6 +133,11 @@ export const idlFactory = ({ IDL }) => { const DeleteProposalAssets = IDL.Record({ proposal_ids: IDL.Vec(IDL.Nat) }); + const OpenIdProvider = IDL.Variant({ + GitHubActions: IDL.Null, + Google: IDL.Null, + GitHubProxy: IDL.Null + }); const OpenIdProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), max_time_to_live: IDL.Opt(IDL.Nat64) diff --git a/src/declarations/console/console.factory.did.mjs b/src/declarations/console/console.factory.did.mjs index 250efe59ac..7c2917ef44 100644 --- a/src/declarations/console/console.factory.did.mjs +++ b/src/declarations/console/console.factory.did.mjs @@ -24,7 +24,7 @@ export const idlFactory = ({ IDL }) => { user_key: IDL.Vec(IDL.Nat8), expiration: IDL.Nat64 }); - const OpenIdProvider = IDL.Variant({ + const OpenIdAuthProvider = IDL.Variant({ GitHub: IDL.Null, Google: IDL.Null }); @@ -38,7 +38,7 @@ export const idlFactory = ({ IDL }) => { preferred_username: IDL.Opt(IDL.Text) }); const OpenId = IDL.Record({ - provider: OpenIdProvider, + provider: OpenIdAuthProvider, data: OpenIdData }); const Provider = IDL.Variant({ @@ -133,6 +133,11 @@ export const idlFactory = ({ IDL }) => { const DeleteProposalAssets = IDL.Record({ proposal_ids: IDL.Vec(IDL.Nat) }); + const OpenIdProvider = IDL.Variant({ + GitHubActions: IDL.Null, + Google: IDL.Null, + GitHubProxy: IDL.Null + }); const OpenIdProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), max_time_to_live: IDL.Opt(IDL.Nat64) diff --git a/src/declarations/observatory/observatory.did.d.ts b/src/declarations/observatory/observatory.did.d.ts index 4884af3d3d..f63c9b80bf 100644 --- a/src/declarations/observatory/observatory.did.d.ts +++ b/src/declarations/observatory/observatory.did.d.ts @@ -106,7 +106,7 @@ export interface OpenIdCertificate { created_at: bigint; version: [] | [bigint]; } -export type OpenIdProvider = { GitHub: null } | { Google: null }; +export type OpenIdProvider = { GitHubActions: null } | { Google: null } | { GitHubProxy: null }; export interface RateConfig { max_tokens: bigint; time_per_token_ns: bigint; diff --git a/src/declarations/observatory/observatory.factory.certified.did.js b/src/declarations/observatory/observatory.factory.certified.did.js index c414d71f0e..3272635431 100644 --- a/src/declarations/observatory/observatory.factory.certified.did.js +++ b/src/declarations/observatory/observatory.factory.certified.did.js @@ -21,8 +21,9 @@ export const idlFactory = ({ IDL }) => { failed: IDL.Nat64 }); const OpenIdProvider = IDL.Variant({ - GitHub: IDL.Null, - Google: IDL.Null + GitHubActions: IDL.Null, + Google: IDL.Null, + GitHubProxy: IDL.Null }); const GetOpenIdCertificateArgs = IDL.Record({ provider: OpenIdProvider }); const JwkType = IDL.Variant({ diff --git a/src/declarations/observatory/observatory.factory.did.js b/src/declarations/observatory/observatory.factory.did.js index 2984066a23..bee4e67af4 100644 --- a/src/declarations/observatory/observatory.factory.did.js +++ b/src/declarations/observatory/observatory.factory.did.js @@ -21,8 +21,9 @@ export const idlFactory = ({ IDL }) => { failed: IDL.Nat64 }); const OpenIdProvider = IDL.Variant({ - GitHub: IDL.Null, - Google: IDL.Null + GitHubActions: IDL.Null, + Google: IDL.Null, + GitHubProxy: IDL.Null }); const GetOpenIdCertificateArgs = IDL.Record({ provider: OpenIdProvider }); const JwkType = IDL.Variant({ diff --git a/src/declarations/observatory/observatory.factory.did.mjs b/src/declarations/observatory/observatory.factory.did.mjs index 2984066a23..bee4e67af4 100644 --- a/src/declarations/observatory/observatory.factory.did.mjs +++ b/src/declarations/observatory/observatory.factory.did.mjs @@ -21,8 +21,9 @@ export const idlFactory = ({ IDL }) => { failed: IDL.Nat64 }); const OpenIdProvider = IDL.Variant({ - GitHub: IDL.Null, - Google: IDL.Null + GitHubActions: IDL.Null, + Google: IDL.Null, + GitHubProxy: IDL.Null }); const GetOpenIdCertificateArgs = IDL.Record({ provider: OpenIdProvider }); const JwkType = IDL.Variant({ diff --git a/src/declarations/satellite/satellite.did.d.ts b/src/declarations/satellite/satellite.did.d.ts index dde06ba223..a51b94415b 100644 --- a/src/declarations/satellite/satellite.did.d.ts +++ b/src/declarations/satellite/satellite.did.d.ts @@ -270,7 +270,7 @@ export interface OpenIdPrepareDelegationArgs { session_key: Uint8Array; salt: Uint8Array; } -export type OpenIdProvider = { GitHub: null } | { Google: null }; +export type OpenIdProvider = { GitHubActions: null } | { Google: null } | { GitHubProxy: null }; export interface OpenIdProviderConfig { delegation: [] | [OpenIdProviderDelegationConfig]; client_id: string; diff --git a/src/declarations/satellite/satellite.factory.certified.did.js b/src/declarations/satellite/satellite.factory.certified.did.js index b5c665b978..00743eabe6 100644 --- a/src/declarations/satellite/satellite.factory.certified.did.js +++ b/src/declarations/satellite/satellite.factory.certified.did.js @@ -159,8 +159,9 @@ export const idlFactory = ({ IDL }) => { version: IDL.Opt(IDL.Nat64) }); const OpenIdProvider = IDL.Variant({ - GitHub: IDL.Null, - Google: IDL.Null + GitHubActions: IDL.Null, + Google: IDL.Null, + GitHubProxy: IDL.Null }); const OpenIdProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), diff --git a/src/declarations/satellite/satellite.factory.did.js b/src/declarations/satellite/satellite.factory.did.js index ba851264b2..0161a96157 100644 --- a/src/declarations/satellite/satellite.factory.did.js +++ b/src/declarations/satellite/satellite.factory.did.js @@ -159,8 +159,9 @@ export const idlFactory = ({ IDL }) => { version: IDL.Opt(IDL.Nat64) }); const OpenIdProvider = IDL.Variant({ - GitHub: IDL.Null, - Google: IDL.Null + GitHubActions: IDL.Null, + Google: IDL.Null, + GitHubProxy: IDL.Null }); const OpenIdProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), diff --git a/src/declarations/satellite/satellite.factory.did.mjs b/src/declarations/satellite/satellite.factory.did.mjs index ba851264b2..0161a96157 100644 --- a/src/declarations/satellite/satellite.factory.did.mjs +++ b/src/declarations/satellite/satellite.factory.did.mjs @@ -159,8 +159,9 @@ export const idlFactory = ({ IDL }) => { version: IDL.Opt(IDL.Nat64) }); const OpenIdProvider = IDL.Variant({ - GitHub: IDL.Null, - Google: IDL.Null + GitHubActions: IDL.Null, + Google: IDL.Null, + GitHubProxy: IDL.Null }); const OpenIdProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), diff --git a/src/declarations/sputnik/sputnik.did.d.ts b/src/declarations/sputnik/sputnik.did.d.ts index dde06ba223..a51b94415b 100644 --- a/src/declarations/sputnik/sputnik.did.d.ts +++ b/src/declarations/sputnik/sputnik.did.d.ts @@ -270,7 +270,7 @@ export interface OpenIdPrepareDelegationArgs { session_key: Uint8Array; salt: Uint8Array; } -export type OpenIdProvider = { GitHub: null } | { Google: null }; +export type OpenIdProvider = { GitHubActions: null } | { Google: null } | { GitHubProxy: null }; export interface OpenIdProviderConfig { delegation: [] | [OpenIdProviderDelegationConfig]; client_id: string; diff --git a/src/declarations/sputnik/sputnik.factory.certified.did.js b/src/declarations/sputnik/sputnik.factory.certified.did.js index b5c665b978..00743eabe6 100644 --- a/src/declarations/sputnik/sputnik.factory.certified.did.js +++ b/src/declarations/sputnik/sputnik.factory.certified.did.js @@ -159,8 +159,9 @@ export const idlFactory = ({ IDL }) => { version: IDL.Opt(IDL.Nat64) }); const OpenIdProvider = IDL.Variant({ - GitHub: IDL.Null, - Google: IDL.Null + GitHubActions: IDL.Null, + Google: IDL.Null, + GitHubProxy: IDL.Null }); const OpenIdProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), diff --git a/src/declarations/sputnik/sputnik.factory.did.js b/src/declarations/sputnik/sputnik.factory.did.js index ba851264b2..0161a96157 100644 --- a/src/declarations/sputnik/sputnik.factory.did.js +++ b/src/declarations/sputnik/sputnik.factory.did.js @@ -159,8 +159,9 @@ export const idlFactory = ({ IDL }) => { version: IDL.Opt(IDL.Nat64) }); const OpenIdProvider = IDL.Variant({ - GitHub: IDL.Null, - Google: IDL.Null + GitHubActions: IDL.Null, + Google: IDL.Null, + GitHubProxy: IDL.Null }); const OpenIdProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), diff --git a/src/libs/satellite/satellite.did b/src/libs/satellite/satellite.did index 7814ee0c99..363df45340 100644 --- a/src/libs/satellite/satellite.did +++ b/src/libs/satellite/satellite.did @@ -221,7 +221,7 @@ type OpenIdPrepareDelegationArgs = record { session_key : blob; salt : blob; }; -type OpenIdProvider = variant { GitHub; Google }; +type OpenIdProvider = variant { GitHubActions; Google; GitHubProxy }; type OpenIdProviderConfig = record { delegation : opt OpenIdProviderDelegationConfig; client_id : text; diff --git a/src/observatory/observatory.did b/src/observatory/observatory.did index 8d9d593ffb..a7621c5b5c 100644 --- a/src/observatory/observatory.did +++ b/src/observatory/observatory.did @@ -68,7 +68,7 @@ type OpenIdCertificate = record { created_at : nat64; version : opt nat64; }; -type OpenIdProvider = variant { GitHub; Google }; +type OpenIdProvider = variant { GitHubActions; Google; GitHubProxy }; type RateConfig = record { max_tokens : nat64; time_per_token_ns : nat64 }; type RateKind = variant { OpenIdCertificateRequests }; type Segment = record { diff --git a/src/satellite/satellite.did b/src/satellite/satellite.did index 8d8d97afff..9e4fec0266 100644 --- a/src/satellite/satellite.did +++ b/src/satellite/satellite.did @@ -223,7 +223,7 @@ type OpenIdPrepareDelegationArgs = record { session_key : blob; salt : blob; }; -type OpenIdProvider = variant { GitHub; Google }; +type OpenIdProvider = variant { GitHubActions; Google; GitHubProxy }; type OpenIdProviderConfig = record { delegation : opt OpenIdProviderDelegationConfig; client_id : text; diff --git a/src/sputnik/sputnik.did b/src/sputnik/sputnik.did index 06751195a6..4eb0e68586 100644 --- a/src/sputnik/sputnik.did +++ b/src/sputnik/sputnik.did @@ -223,7 +223,7 @@ type OpenIdPrepareDelegationArgs = record { session_key : blob; salt : blob; }; -type OpenIdProvider = variant { GitHub; Google }; +type OpenIdProvider = variant { GitHubActions; Google; GitHubProxy }; type OpenIdProviderConfig = record { delegation : opt OpenIdProviderDelegationConfig; client_id : text; diff --git a/src/tests/declarations/test_satellite/test_satellite.did.d.ts b/src/tests/declarations/test_satellite/test_satellite.did.d.ts index 9ee13bd161..cc7fbe2464 100644 --- a/src/tests/declarations/test_satellite/test_satellite.did.d.ts +++ b/src/tests/declarations/test_satellite/test_satellite.did.d.ts @@ -270,7 +270,7 @@ export interface OpenIdPrepareDelegationArgs { session_key: Uint8Array; salt: Uint8Array; } -export type OpenIdProvider = { GitHub: null } | { Google: null }; +export type OpenIdProvider = { GitHubActions: null } | { Google: null } | { GitHubProxy: null }; export interface OpenIdProviderConfig { delegation: [] | [OpenIdProviderDelegationConfig]; client_id: string; diff --git a/src/tests/declarations/test_satellite/test_satellite.factory.certified.did.js b/src/tests/declarations/test_satellite/test_satellite.factory.certified.did.js index a6e62ab11a..8fa34b8bfa 100644 --- a/src/tests/declarations/test_satellite/test_satellite.factory.certified.did.js +++ b/src/tests/declarations/test_satellite/test_satellite.factory.certified.did.js @@ -159,8 +159,9 @@ export const idlFactory = ({ IDL }) => { version: IDL.Opt(IDL.Nat64) }); const OpenIdProvider = IDL.Variant({ - GitHub: IDL.Null, - Google: IDL.Null + GitHubActions: IDL.Null, + Google: IDL.Null, + GitHubProxy: IDL.Null }); const OpenIdProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), diff --git a/src/tests/declarations/test_satellite/test_satellite.factory.did.js b/src/tests/declarations/test_satellite/test_satellite.factory.did.js index 13c363c35e..c91ec13f54 100644 --- a/src/tests/declarations/test_satellite/test_satellite.factory.did.js +++ b/src/tests/declarations/test_satellite/test_satellite.factory.did.js @@ -159,8 +159,9 @@ export const idlFactory = ({ IDL }) => { version: IDL.Opt(IDL.Nat64) }); const OpenIdProvider = IDL.Variant({ - GitHub: IDL.Null, - Google: IDL.Null + GitHubActions: IDL.Null, + Google: IDL.Null, + GitHubProxy: IDL.Null }); const OpenIdProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), diff --git a/src/tests/fixtures/test_satellite/test_satellite.did b/src/tests/fixtures/test_satellite/test_satellite.did index 5990623871..61bee19770 100644 --- a/src/tests/fixtures/test_satellite/test_satellite.did +++ b/src/tests/fixtures/test_satellite/test_satellite.did @@ -223,7 +223,7 @@ type OpenIdPrepareDelegationArgs = record { session_key : blob; salt : blob; }; -type OpenIdProvider = variant { GitHub; Google }; +type OpenIdProvider = variant { GitHubActions; Google; GitHubProxy }; type OpenIdProviderConfig = record { delegation : opt OpenIdProviderDelegationConfig; client_id : text; From 0d069ca551e41dadaf31a189dc5d1f68fa9a4dd9 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 28 Jan 2026 10:48:57 +0100 Subject: [PATCH 09/32] feat: auth id provider --- src/console/src/auth/delegation.rs | 9 ++-- src/console/src/auth/register.rs | 8 ++-- src/console/src/impls.rs | 18 +------- src/console/src/types.rs | 7 +-- src/libs/auth/src/delegation/get.rs | 6 +-- src/libs/auth/src/delegation/prepare.rs | 8 ++-- .../auth/src/delegation/utils/duration.rs | 4 +- src/libs/auth/src/delegation/utils/targets.rs | 4 +- src/libs/auth/src/openid/jwt/provider.rs | 41 +++++++++-------- src/libs/auth/src/openid/user/types.rs | 4 ++ src/libs/auth/src/openid/user/verify.rs | 26 ++++++----- src/libs/auth/src/state/impls.rs | 45 ++++++++++++++++++- src/libs/auth/src/state/store.rs | 4 +- src/libs/auth/src/state/types.rs | 23 ++++++---- src/libs/satellite/src/auth/delegation.rs | 8 ++-- src/libs/satellite/src/auth/register.rs | 5 ++- src/libs/satellite/src/user/core/impls.rs | 17 +++---- 17 files changed, 133 insertions(+), 104 deletions(-) diff --git a/src/console/src/auth/delegation.rs b/src/console/src/auth/delegation.rs index c9ef797e3c..1e6647c23d 100644 --- a/src/console/src/auth/delegation.rs +++ b/src/console/src/auth/delegation.rs @@ -4,17 +4,16 @@ use junobuild_auth::delegation::types::{ GetDelegationError, GetDelegationResult, OpenIdGetDelegationArgs, OpenIdPrepareDelegationArgs, PrepareDelegationError, PreparedDelegation, }; -use junobuild_auth::openid::types::provider::OpenIdProvider; use junobuild_auth::openid::user::types::interface::OpenIdCredential; -use junobuild_auth::state::types::config::OpenIdProviders; +use junobuild_auth::state::types::config::{OpenIdAuthProvider, OpenIdAuthProviders}; use junobuild_auth::{delegation, openid}; pub type OpenIdPrepareDelegationResult = - Result<(PreparedDelegation, OpenIdProvider, OpenIdCredential), PrepareDelegationError>; + Result<(PreparedDelegation, OpenIdAuthProvider, OpenIdCredential), PrepareDelegationError>; pub async fn openid_prepare_delegation( args: &OpenIdPrepareDelegationArgs, - providers: &OpenIdProviders, + providers: &OpenIdAuthProviders, ) -> OpenIdPrepareDelegationResult { let (credential, provider) = match openid::user::verify_openid_credentials_with_jwks_renewal( &args.jwt, &args.salt, providers, &AuthHeap, @@ -38,7 +37,7 @@ pub async fn openid_prepare_delegation( pub fn openid_get_delegation( args: &OpenIdGetDelegationArgs, - providers: &OpenIdProviders, + providers: &OpenIdAuthProviders, ) -> GetDelegationResult { let (credential, provider) = match openid::user::verify_openid_credentials_with_cached_jwks( &args.jwt, &args.salt, providers, &AuthHeap, diff --git a/src/console/src/auth/register.rs b/src/console/src/auth/register.rs index 1552677139..339cc272ec 100644 --- a/src/console/src/auth/register.rs +++ b/src/console/src/auth/register.rs @@ -1,14 +1,14 @@ use crate::accounts::{get_optional_account, init_account, update_provider}; use crate::types::state::{Account, OpenIdData, Provider}; -use crate::types::state::{OpenId, OpenIdAuthProvider}; +use crate::types::state::{OpenId}; use candid::Principal; use junobuild_auth::delegation::types::UserKey; -use junobuild_auth::openid::types::provider::OpenIdProvider; use junobuild_auth::openid::user::types::interface::OpenIdCredential; +use junobuild_auth::state::types::config::OpenIdAuthProvider; pub async fn register_account( public_key: &UserKey, - provider: &OpenIdProvider, + provider: &OpenIdAuthProvider, credential: &OpenIdCredential, ) -> Result { let user_id = Principal::self_authenticating(public_key); @@ -42,7 +42,7 @@ pub async fn register_account( }; let provider = Provider::OpenId(OpenId { - provider: OpenIdAuthProvider::try_from(provider)?, + provider: provider.clone(), data: provider_data, }); diff --git a/src/console/src/impls.rs b/src/console/src/impls.rs index b46f85a3e8..5be57ff4b4 100644 --- a/src/console/src/impls.rs +++ b/src/console/src/impls.rs @@ -1,14 +1,13 @@ use crate::memory::manager::init_stable_state; use crate::types::ledger::{Fee, IcpPayment, IcrcPayment, IcrcPaymentKey}; use crate::types::state::{ - Account, HeapState, OpenIdAuthProvider, Segment, SegmentKey, State, StorableSegmentKind, + Account, HeapState, Segment, SegmentKey, State, StorableSegmentKind, }; use candid::Principal; use ic_cdk::api::time; use ic_ledger_types::{BlockIndex, Tokens}; use ic_stable_structures::storable::Bound; use ic_stable_structures::Storable; -use junobuild_auth::openid::types::provider::OpenIdProvider; use junobuild_shared::ledger::types::cycles::CyclesTokens; use junobuild_shared::memory::serializers::{ deserialize_from_bytes, serialize_into_bytes, serialize_to_bytes, @@ -193,18 +192,3 @@ impl Fee { } } } - -impl TryFrom<&OpenIdProvider> for OpenIdAuthProvider { - type Error = String; - - fn try_from(provider: &OpenIdProvider) -> Result { - match provider { - OpenIdProvider::Google => Ok(OpenIdAuthProvider::Google), - OpenIdProvider::GitHubProxy => Ok(OpenIdAuthProvider::GitHub), - _ => Err(format!( - "{:?} is not supported for user authentication", - provider - )), - } - } -} diff --git a/src/console/src/types.rs b/src/console/src/types.rs index 5ecc7ae98a..566879a2db 100644 --- a/src/console/src/types.rs +++ b/src/console/src/types.rs @@ -17,6 +17,7 @@ pub mod state { use junobuild_storage::types::state::StorageHeapState; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; + use junobuild_auth::state::types::config::OpenIdAuthProvider; pub type Accounts = HashMap; pub type IcpPayments = HashMap; @@ -86,12 +87,6 @@ pub mod state { pub data: OpenIdData, } - #[derive(CandidType, Serialize, Deserialize, Clone)] - pub enum OpenIdAuthProvider { - Google, - GitHub, - } - #[derive(CandidType, Serialize, Deserialize, Clone, Eq, PartialEq)] pub struct OpenIdData { pub email: Option, diff --git a/src/libs/auth/src/delegation/get.rs b/src/libs/auth/src/delegation/get.rs index 489bc8168e..3e97864fba 100644 --- a/src/libs/auth/src/delegation/get.rs +++ b/src/libs/auth/src/delegation/get.rs @@ -4,18 +4,18 @@ use crate::delegation::types::{ use crate::delegation::utils::seed::calculate_seed; use crate::delegation::utils::signature::{build_signature_inputs, build_signature_msg}; use crate::delegation::utils::targets::build_targets; -use crate::openid::types::provider::OpenIdProvider; use crate::openid::user::types::interface::{OpenIdCredential, OpenIdCredentialKey}; use crate::state::get_salt; use crate::state::services::read_state; use crate::strategies::{AuthCertificateStrategy, AuthHeapStrategy}; use serde_bytes::ByteBuf; +use crate::state::types::config::OpenIdAuthProvider; pub fn openid_get_delegation( session_key: &SessionKey, expiration: Timestamp, credential: &OpenIdCredential, - provider: &OpenIdProvider, + provider: &OpenIdAuthProvider, auth_heap: &impl AuthHeapStrategy, certificate: &impl AuthCertificateStrategy, ) -> GetDelegationResult { @@ -33,7 +33,7 @@ pub fn get_delegation( session_key: &SessionKey, expiration: Timestamp, key: &OpenIdCredentialKey, - provider: &OpenIdProvider, + provider: &OpenIdAuthProvider, auth_heap: &impl AuthHeapStrategy, certificate: &impl AuthCertificateStrategy, ) -> GetDelegationResult { diff --git a/src/libs/auth/src/delegation/prepare.rs b/src/libs/auth/src/delegation/prepare.rs index 408d227256..d7007d064b 100644 --- a/src/libs/auth/src/delegation/prepare.rs +++ b/src/libs/auth/src/delegation/prepare.rs @@ -6,7 +6,7 @@ use crate::delegation::utils::duration::build_expiration; use crate::delegation::utils::seed::calculate_seed; use crate::delegation::utils::signature::{build_signature_inputs, build_signature_msg}; use crate::delegation::utils::targets::build_targets; -use crate::openid::types::provider::OpenIdProvider; +use crate::state::types::config::OpenIdAuthProvider; use crate::openid::user::types::interface::{OpenIdCredential, OpenIdCredentialKey}; use crate::state::get_salt; use crate::state::services::mutate_state; @@ -18,7 +18,7 @@ use serde_bytes::ByteBuf; pub fn openid_prepare_delegation( session_key: &SessionKey, credential: &OpenIdCredential, - provider: &OpenIdProvider, + provider: &OpenIdAuthProvider, auth_heap: &impl AuthHeapStrategy, certificate: &impl AuthCertificateStrategy, ) -> PrepareDelegationResult { @@ -36,7 +36,7 @@ pub fn openid_prepare_delegation( fn prepare_delegation( session_key: &SessionKey, key: &OpenIdCredentialKey, - provider: &OpenIdProvider, + provider: &OpenIdAuthProvider, auth_heap: &impl AuthHeapStrategy, certificate: &impl AuthCertificateStrategy, ) -> PrepareDelegationResult { @@ -60,7 +60,7 @@ fn prepare_delegation( fn add_delegation_signature( session_key: &PublicKey, expiration: Timestamp, - provider: &OpenIdProvider, + provider: &OpenIdAuthProvider, seed: &[u8], auth_heap: &impl AuthHeapStrategy, ) { diff --git a/src/libs/auth/src/delegation/utils/duration.rs b/src/libs/auth/src/delegation/utils/duration.rs index 587f842741..74a4fbb9a2 100644 --- a/src/libs/auth/src/delegation/utils/duration.rs +++ b/src/libs/auth/src/delegation/utils/duration.rs @@ -1,11 +1,11 @@ use crate::delegation::constants::{DEFAULT_EXPIRATION_PERIOD_NS, MAX_EXPIRATION_PERIOD_NS}; -use crate::openid::types::provider::OpenIdProvider; use crate::state::get_config; use crate::strategies::AuthHeapStrategy; use ic_cdk::api::time; use std::cmp::min; +use crate::state::types::config::OpenIdAuthProvider; -pub fn build_expiration(provider: &OpenIdProvider, auth_heap: &impl AuthHeapStrategy) -> u64 { +pub fn build_expiration(provider: &OpenIdAuthProvider, auth_heap: &impl AuthHeapStrategy) -> u64 { let max_time_to_live = get_config(auth_heap) .as_ref() .and_then(|config| config.openid.as_ref()) diff --git a/src/libs/auth/src/delegation/utils/targets.rs b/src/libs/auth/src/delegation/utils/targets.rs index 2d837cc43d..7fca708b00 100644 --- a/src/libs/auth/src/delegation/utils/targets.rs +++ b/src/libs/auth/src/delegation/utils/targets.rs @@ -1,8 +1,8 @@ use crate::delegation::types::DelegationTargets; -use crate::openid::types::provider::OpenIdProvider; use crate::state::get_config; use crate::strategies::AuthHeapStrategy; use junobuild_shared::ic::api::id; +use crate::state::types::config::OpenIdAuthProvider; // By default, and for security reasons, we restrict delegation to the authentication module // that created it. Developers can opt out (allow any targets) or define their own @@ -19,7 +19,7 @@ use junobuild_shared::ic::api::id; // Moreover, there is unlikely to be a valid use case where a delegation generated by the Satellite // should target no canister at all. pub fn build_targets( - provider: &OpenIdProvider, + provider: &OpenIdAuthProvider, auth_heap: &impl AuthHeapStrategy, ) -> Option { get_config(auth_heap) diff --git a/src/libs/auth/src/openid/jwt/provider.rs b/src/libs/auth/src/openid/jwt/provider.rs index 42f5cbebde..2913a4b2ff 100644 --- a/src/libs/auth/src/openid/jwt/provider.rs +++ b/src/libs/auth/src/openid/jwt/provider.rs @@ -1,16 +1,15 @@ use crate::openid::jwt::header::decode_jwt_header; use crate::openid::jwt::types::errors::JwtFindProviderError; use crate::openid::jwt::types::token::UnsafeClaims; -use crate::openid::types::provider::OpenIdProvider; -use crate::state::types::config::{OpenIdProviderConfig, OpenIdProviders}; +use crate::state::types::config::{OpenIdProviderAuthConfig, OpenIdAuthProviders, OpenIdAuthProvider}; use jsonwebtoken::dangerous; /// ⚠️ **Warning:** This function decodes the JWT payload *without verifying its signature*. /// Use only to inspect claims (e.g., `iss`) before performing a verified decode. -pub fn unsafe_find_jwt_provider<'a>( - providers: &'a OpenIdProviders, +pub fn unsafe_find_jwt_auth_provider<'a>( + providers: &'a OpenIdAuthProviders, jwt: &str, -) -> Result<(OpenIdProvider, &'a OpenIdProviderConfig), JwtFindProviderError> { +) -> Result<(OpenIdAuthProvider, &'a OpenIdProviderAuthConfig), JwtFindProviderError> { // 1) Header sanity check decode_jwt_header(jwt).map_err(JwtFindProviderError::from)?; @@ -33,10 +32,10 @@ pub fn unsafe_find_jwt_provider<'a>( #[cfg(test)] mod tests { - use super::unsafe_find_jwt_provider; + use super::unsafe_find_jwt_auth_provider; use crate::openid::jwt::types::errors::JwtFindProviderError; use crate::openid::types::provider::OpenIdProvider; - use crate::state::types::config::{OpenIdProviderConfig, OpenIdProviders}; + use crate::state::types::config::{OpenIdProviderAuthConfig, OpenIdAuthProviders, OpenIdAuthProvider}; use base64::engine::general_purpose::URL_SAFE_NO_PAD; use base64::Engine; use serde_json::json; @@ -51,11 +50,11 @@ mod tests { format!("{h}.{p}.{s}") } - fn providers_with_google() -> OpenIdProviders { + fn providers_with_google() -> OpenIdAuthProviders { let mut map = BTreeMap::new(); map.insert( - OpenIdProvider::Google, - OpenIdProviderConfig { + OpenIdAuthProvider::Google, + OpenIdProviderAuthConfig { client_id: "client-123".into(), delegation: None, }, @@ -71,9 +70,9 @@ mod tests { let provs = providers_with_google(); let (provider, cfg) = - unsafe_find_jwt_provider(&provs, &jwt).expect("should match provider"); + unsafe_find_jwt_auth_provider(&provs, &jwt).expect("should match provider"); - assert_eq!(provider, OpenIdProvider::Google); + assert_eq!(provider, OpenIdAuthProvider::Google); assert_eq!(cfg.client_id, "client-123"); } @@ -84,8 +83,8 @@ mod tests { let provs = providers_with_google(); let (provider, _) = - unsafe_find_jwt_provider(&provs, &jwt).expect("should match even without typ"); - assert_eq!(provider, OpenIdProvider::Google); + unsafe_find_jwt_auth_provider(&provs, &jwt).expect("should match even without typ"); + assert_eq!(provider, OpenIdAuthProvider::Google); } #[test] @@ -94,7 +93,7 @@ mod tests { let jwt = jwt_with(json!({"alg":"HS256","typ":"JWT"}), json!({"iss": iss})); let provs = providers_with_google(); - let err = unsafe_find_jwt_provider(&provs, &jwt).unwrap_err(); + let err = unsafe_find_jwt_auth_provider(&provs, &jwt).unwrap_err(); match err { JwtFindProviderError::BadClaim(f) => assert_eq!(f, "alg"), @@ -111,7 +110,7 @@ mod tests { ); let provs = providers_with_google(); - let err = unsafe_find_jwt_provider(&provs, &jwt).unwrap_err(); + let err = unsafe_find_jwt_auth_provider(&provs, &jwt).unwrap_err(); match err { JwtFindProviderError::BadClaim(f) => assert_eq!(f, "typ"), @@ -123,7 +122,7 @@ mod tests { fn returns_no_matching_provider_when_issuer_missing() { let jwt = jwt_with(json!({"alg":"RS256","typ":"JWT"}), json!({})); let provs = providers_with_google(); - let err = unsafe_find_jwt_provider(&provs, &jwt).unwrap_err(); + let err = unsafe_find_jwt_auth_provider(&provs, &jwt).unwrap_err(); assert!(matches!(err, JwtFindProviderError::NoMatchingProvider)); } @@ -134,7 +133,7 @@ mod tests { json!({"iss":"https://unknown.example.com"}), ); let provs = providers_with_google(); - let err = unsafe_find_jwt_provider(&provs, &jwt).unwrap_err(); + let err = unsafe_find_jwt_auth_provider(&provs, &jwt).unwrap_err(); assert!(matches!(err, JwtFindProviderError::NoMatchingProvider)); } @@ -142,7 +141,7 @@ mod tests { fn malformed_token_is_badsig() { let jwt = "definitely-not-a-jwt"; let provs = providers_with_google(); - let err = unsafe_find_jwt_provider(&provs, jwt).unwrap_err(); + let err = unsafe_find_jwt_auth_provider(&provs, jwt).unwrap_err(); assert!(matches!(err, JwtFindProviderError::BadSig(_))); } @@ -155,7 +154,7 @@ mod tests { let jwt = format!("{h}.{p}.{s}"); let provs = providers_with_google(); - let err = unsafe_find_jwt_provider(&provs, &jwt).unwrap_err(); + let err = unsafe_find_jwt_auth_provider(&provs, &jwt).unwrap_err(); assert!(matches!(err, JwtFindProviderError::BadSig(_))); } @@ -163,7 +162,7 @@ mod tests { fn empty_iss_is_no_match() { let jwt = jwt_with(json!({"alg":"RS256","typ":"JWT"}), json!({"iss": ""})); let provs = providers_with_google(); - let err = unsafe_find_jwt_provider(&provs, &jwt).unwrap_err(); + let err = unsafe_find_jwt_auth_provider(&provs, &jwt).unwrap_err(); assert!(matches!(err, JwtFindProviderError::NoMatchingProvider)); } } diff --git a/src/libs/auth/src/openid/user/types.rs b/src/libs/auth/src/openid/user/types.rs index af0991b32c..97f7cd6a2b 100644 --- a/src/libs/auth/src/openid/user/types.rs +++ b/src/libs/auth/src/openid/user/types.rs @@ -32,3 +32,7 @@ pub(crate) mod errors { JwtVerify(JwtVerifyError), } } + +pub mod provider { + +} \ No newline at end of file diff --git a/src/libs/auth/src/openid/user/verify.rs b/src/libs/auth/src/openid/user/verify.rs index e3bfe85233..381ab7bff1 100644 --- a/src/libs/auth/src/openid/user/verify.rs +++ b/src/libs/auth/src/openid/user/verify.rs @@ -2,53 +2,57 @@ use crate::openid::jwkset::{get_jwks, get_or_refresh_jwks}; use crate::openid::jwt::types::cert::Jwks; use crate::openid::jwt::types::errors::JwtVerifyError; use crate::openid::jwt::types::token::Claims; -use crate::openid::jwt::{unsafe_find_jwt_provider, verify_openid_jwt}; +use crate::openid::jwt::{unsafe_find_jwt_auth_provider, verify_openid_jwt}; use crate::openid::types::provider::OpenIdProvider; use crate::openid::user::types::errors::VerifyOpenidCredentialsError; use crate::openid::user::types::interface::OpenIdCredential; use crate::openid::utils::build_nonce; -use crate::state::types::config::{OpenIdProviderClientId, OpenIdProviders}; +use crate::state::types::config::{OpenIdAuthProvider, OpenIdAuthProviderClientId, OpenIdAuthProviders}; use crate::state::types::state::Salt; use crate::strategies::AuthHeapStrategy; type VerifyOpenIdCredentialsResult = - Result<(OpenIdCredential, OpenIdProvider), VerifyOpenidCredentialsError>; + Result<(OpenIdCredential, OpenIdAuthProvider), VerifyOpenidCredentialsError>; pub async fn verify_openid_credentials_with_jwks_renewal( jwt: &str, salt: &Salt, - providers: &OpenIdProviders, + providers: &OpenIdAuthProviders, auth_heap: &impl AuthHeapStrategy, ) -> VerifyOpenIdCredentialsResult { - let (provider, config) = unsafe_find_jwt_provider(providers, jwt) + let (auth_provider, config) = unsafe_find_jwt_auth_provider(providers, jwt) .map_err(VerifyOpenidCredentialsError::JwtFindProvider)?; + let provider: OpenIdProvider = (&auth_provider).into(); + let jwks = get_or_refresh_jwks(&provider, jwt, auth_heap) .await .map_err(VerifyOpenidCredentialsError::GetOrFetchJwks)?; - verify_openid_credentials(jwt, &jwks, &provider, &config.client_id, salt) + verify_openid_credentials(jwt, &jwks, &auth_provider, &config.client_id, salt) } pub fn verify_openid_credentials_with_cached_jwks( jwt: &str, salt: &Salt, - providers: &OpenIdProviders, + providers: &OpenIdAuthProviders, auth_heap: &impl AuthHeapStrategy, ) -> VerifyOpenIdCredentialsResult { - let (provider, config) = unsafe_find_jwt_provider(providers, jwt) + let (auth_provider, config) = unsafe_find_jwt_auth_provider(providers, jwt) .map_err(VerifyOpenidCredentialsError::JwtFindProvider)?; + let provider: OpenIdProvider = (&auth_provider).into(); + let jwks = get_jwks(&provider, auth_heap).ok_or(VerifyOpenidCredentialsError::GetCachedJwks)?; - verify_openid_credentials(jwt, &jwks, &provider, &config.client_id, salt) + verify_openid_credentials(jwt, &jwks, &auth_provider, &config.client_id, salt) } fn verify_openid_credentials( jwt: &str, jwks: &Jwks, - provider: &OpenIdProvider, - client_id: &OpenIdProviderClientId, + provider: &OpenIdAuthProvider, + client_id: &OpenIdAuthProviderClientId, salt: &Salt, ) -> VerifyOpenIdCredentialsResult { let assert_audience = |claims: &Claims| -> Result<(), JwtVerifyError> { diff --git a/src/libs/auth/src/state/impls.rs b/src/libs/auth/src/state/impls.rs index bba2acce0f..b7f46bace6 100644 --- a/src/libs/auth/src/state/impls.rs +++ b/src/libs/auth/src/state/impls.rs @@ -1,5 +1,5 @@ -use crate::openid::types::provider::OpenIdCertificate; -use crate::state::types::config::AuthenticationConfig; +use crate::openid::types::provider::{OpenIdCertificate, OpenIdProvider}; +use crate::state::types::config::{AuthenticationConfig, OpenIdAuthProvider}; use crate::state::types::interface::SetAuthenticationConfig; use crate::state::types::state::{OpenIdCachedCertificate, OpenIdLastFetchAttempt}; use ic_cdk::api::time; @@ -74,3 +74,44 @@ impl OpenIdCachedCertificate { self.last_fetch_attempt.streak_count = 0; } } + + +impl TryFrom<&OpenIdProvider> for OpenIdAuthProvider { + type Error = String; + + fn try_from(provider: &OpenIdProvider) -> Result { + match provider { + OpenIdProvider::Google => Ok(OpenIdAuthProvider::Google), + OpenIdProvider::GitHubProxy => Ok(OpenIdAuthProvider::GitHub), + _ => Err(format!( + "{:?} is not supported for user authentication", + provider + )), + } + } +} + +impl From<&OpenIdAuthProvider> for OpenIdProvider { + fn from(auth_provider: &OpenIdAuthProvider) -> Self { + match auth_provider { + OpenIdAuthProvider::Google => OpenIdProvider::Google, + OpenIdAuthProvider::GitHub => OpenIdProvider::GitHubProxy, + } + } +} + +impl OpenIdAuthProvider { + pub fn jwks_url(&self) -> &'static str { + match self { + Self::Google => OpenIdProvider::Google.jwks_url(), + Self::GitHub => OpenIdProvider::GitHubProxy.jwks_url(), + } + } + + pub fn issuers(&self) -> &[&'static str] { + match self { + Self::Google => OpenIdProvider::Google.issuers(), + Self::GitHub => OpenIdProvider::GitHubProxy.issuers(), + } + } +} \ No newline at end of file diff --git a/src/libs/auth/src/state/store.rs b/src/libs/auth/src/state/store.rs index 445a38d8a5..1f6ea6ca28 100644 --- a/src/libs/auth/src/state/store.rs +++ b/src/libs/auth/src/state/store.rs @@ -2,7 +2,7 @@ use crate::errors::{JUNO_AUTH_ERROR_NOT_CONFIGURED, JUNO_AUTH_ERROR_OPENID_DISAB use crate::state::assert::assert_set_config; use crate::state::heap::get_config; use crate::state::heap::insert_config; -use crate::state::types::config::{AuthenticationConfig, OpenIdProviders}; +use crate::state::types::config::{AuthenticationConfig, OpenIdAuthProviders}; use crate::state::types::interface::SetAuthenticationConfig; use crate::state::{get_salt, insert_salt}; use crate::strategies::AuthHeapStrategy; @@ -46,7 +46,7 @@ pub async fn init_salt(auth_heap: &impl AuthHeapStrategy) -> Result<(), String> Ok(()) } -pub fn get_providers(auth_heap: &impl AuthHeapStrategy) -> Result { +pub fn get_providers(auth_heap: &impl AuthHeapStrategy) -> Result { let config = get_config(auth_heap).ok_or(JUNO_AUTH_ERROR_NOT_CONFIGURED.to_string())?; let openid = config .openid diff --git a/src/libs/auth/src/state/types.rs b/src/libs/auth/src/state/types.rs index abc4278382..84500f1337 100644 --- a/src/libs/auth/src/state/types.rs +++ b/src/libs/auth/src/state/types.rs @@ -53,7 +53,6 @@ pub(crate) mod runtime_state { pub mod config { use crate::delegation::types::DelegationTargets; - use crate::openid::types::provider::OpenIdProvider; use candid::{CandidType, Deserialize, Principal}; use junobuild_shared::types::core::DomainName; use junobuild_shared::types::state::{Timestamp, Version}; @@ -72,7 +71,7 @@ pub mod config { #[derive(Default, CandidType, Serialize, Deserialize, Clone)] pub struct AuthenticationConfigOpenId { - pub providers: OpenIdProviders, + pub providers: OpenIdAuthProviders, pub observatory_id: Option, } @@ -87,18 +86,26 @@ pub mod config { pub allowed_callers: Vec, } - pub type OpenIdProviders = BTreeMap; + #[derive( + CandidType, Serialize, Deserialize, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug, + )] + pub enum OpenIdAuthProvider { + Google, + GitHub, + } + + pub type OpenIdAuthProviders = BTreeMap; - pub type OpenIdProviderClientId = String; + pub type OpenIdAuthProviderClientId = String; #[derive(Default, CandidType, Serialize, Deserialize, Clone, Debug)] - pub struct OpenIdProviderConfig { - pub client_id: OpenIdProviderClientId, - pub delegation: Option, + pub struct OpenIdProviderAuthConfig { + pub client_id: OpenIdAuthProviderClientId, + pub delegation: Option, } #[derive(Default, CandidType, Serialize, Deserialize, Clone, Debug)] - pub struct OpenIdProviderDelegationConfig { + pub struct OpenIdAuthProviderDelegationConfig { pub targets: Option, pub max_time_to_live: Option, } diff --git a/src/libs/satellite/src/auth/delegation.rs b/src/libs/satellite/src/auth/delegation.rs index c9ef797e3c..0c28126f6f 100644 --- a/src/libs/satellite/src/auth/delegation.rs +++ b/src/libs/satellite/src/auth/delegation.rs @@ -6,15 +6,15 @@ use junobuild_auth::delegation::types::{ }; use junobuild_auth::openid::types::provider::OpenIdProvider; use junobuild_auth::openid::user::types::interface::OpenIdCredential; -use junobuild_auth::state::types::config::OpenIdProviders; +use junobuild_auth::state::types::config::{OpenIdAuthProvider, OpenIdAuthProviders}; use junobuild_auth::{delegation, openid}; pub type OpenIdPrepareDelegationResult = - Result<(PreparedDelegation, OpenIdProvider, OpenIdCredential), PrepareDelegationError>; + Result<(PreparedDelegation, OpenIdAuthProvider, OpenIdCredential), PrepareDelegationError>; pub async fn openid_prepare_delegation( args: &OpenIdPrepareDelegationArgs, - providers: &OpenIdProviders, + providers: &OpenIdAuthProviders, ) -> OpenIdPrepareDelegationResult { let (credential, provider) = match openid::user::verify_openid_credentials_with_jwks_renewal( &args.jwt, &args.salt, providers, &AuthHeap, @@ -38,7 +38,7 @@ pub async fn openid_prepare_delegation( pub fn openid_get_delegation( args: &OpenIdGetDelegationArgs, - providers: &OpenIdProviders, + providers: &OpenIdAuthProviders, ) -> GetDelegationResult { let (credential, provider) = match openid::user::verify_openid_credentials_with_cached_jwks( &args.jwt, &args.salt, providers, &AuthHeap, diff --git a/src/libs/satellite/src/auth/register.rs b/src/libs/satellite/src/auth/register.rs index 2eb33b00c8..353ef27f33 100644 --- a/src/libs/satellite/src/auth/register.rs +++ b/src/libs/satellite/src/auth/register.rs @@ -9,6 +9,7 @@ use candid::Principal; use junobuild_auth::delegation::types::UserKey; use junobuild_auth::openid::types::provider::OpenIdProvider; use junobuild_auth::openid::user::types::interface::OpenIdCredential; +use junobuild_auth::state::types::config::OpenIdAuthProvider; use junobuild_collections::constants::db::COLLECTION_USER_KEY; use junobuild_collections::msg::msg_db_collection_not_found; use junobuild_shared::ic::api::id; @@ -16,7 +17,7 @@ use junobuild_utils::decode_doc_data; pub fn register_user( public_key: &UserKey, - provider: &OpenIdProvider, + provider: &OpenIdAuthProvider, credential: &OpenIdCredential, ) -> Result { let user_collection = COLLECTION_USER_KEY.to_string(); @@ -83,7 +84,7 @@ pub fn register_user( // Create or update the user. let user_data: UserData = UserData { banned, - provider: Some(provider.try_into()?), + provider: Some(provider.into()), provider_data: Some(ProviderData::OpenId(provider_data)), }; diff --git a/src/libs/satellite/src/user/core/impls.rs b/src/libs/satellite/src/user/core/impls.rs index c8b19cc7cf..2b6ab33a1e 100644 --- a/src/libs/satellite/src/user/core/impls.rs +++ b/src/libs/satellite/src/user/core/impls.rs @@ -12,6 +12,7 @@ use crate::{Doc, SetDoc}; use junobuild_auth::openid::types::provider::OpenIdProvider; use junobuild_auth::openid::user::types::interface::OpenIdCredential; use junobuild_auth::profile::types::{OpenIdProfile, Validated}; +use junobuild_auth::state::types::config::OpenIdAuthProvider; use junobuild_utils::encode_doc_data; impl Validated for WebAuthnData { @@ -160,17 +161,11 @@ impl From<&OpenIdCredential> for OpenIdData { } } -impl TryFrom<&OpenIdProvider> for AuthProvider { - type Error = String; - - fn try_from(provider: &OpenIdProvider) -> Result { - match provider { - OpenIdProvider::Google => Ok(AuthProvider::Google), - OpenIdProvider::GitHubProxy => Ok(AuthProvider::GitHub), - _ => Err(format!( - "{:?} is not supported for user authentication", - provider - )), +impl From<&OpenIdAuthProvider> for AuthProvider { + fn from(auth_provider: &OpenIdAuthProvider) -> Self { + match auth_provider { + OpenIdAuthProvider::Google => AuthProvider::Google, + OpenIdAuthProvider::GitHub => AuthProvider::GitHub, } } } From 7eb171c8d264d89c242109178823e9ac776550d4 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 28 Jan 2026 10:55:07 +0100 Subject: [PATCH 10/32] feat: more and rename type --- src/console/src/auth/delegation.rs | 13 ++++-- src/console/src/auth/register.rs | 6 +-- src/console/src/impls.rs | 4 +- src/console/src/types.rs | 4 +- src/libs/auth/src/delegation/get.rs | 6 +-- src/libs/auth/src/delegation/prepare.rs | 8 ++-- .../auth/src/delegation/utils/duration.rs | 7 ++- src/libs/auth/src/delegation/utils/targets.rs | 4 +- src/libs/auth/src/openid/jwt/provider.rs | 15 ++++--- src/libs/auth/src/openid/user/impls.rs | 42 +++++++++++++++++ src/libs/auth/src/openid/user/types.rs | 14 +++++- src/libs/auth/src/openid/user/verify.rs | 7 +-- src/libs/auth/src/state/impls.rs | 45 +------------------ src/libs/auth/src/state/types.rs | 11 +---- src/libs/satellite/src/auth/delegation.rs | 14 ++++-- src/libs/satellite/src/auth/register.rs | 5 +-- src/libs/satellite/src/user/core/impls.rs | 10 ++--- 17 files changed, 118 insertions(+), 97 deletions(-) diff --git a/src/console/src/auth/delegation.rs b/src/console/src/auth/delegation.rs index 1e6647c23d..5867de2d03 100644 --- a/src/console/src/auth/delegation.rs +++ b/src/console/src/auth/delegation.rs @@ -5,11 +5,18 @@ use junobuild_auth::delegation::types::{ PrepareDelegationError, PreparedDelegation, }; use junobuild_auth::openid::user::types::interface::OpenIdCredential; -use junobuild_auth::state::types::config::{OpenIdAuthProvider, OpenIdAuthProviders}; +use junobuild_auth::openid::user::types::provider::OpenIdDelegationProvider; +use junobuild_auth::state::types::config::OpenIdAuthProviders; use junobuild_auth::{delegation, openid}; -pub type OpenIdPrepareDelegationResult = - Result<(PreparedDelegation, OpenIdAuthProvider, OpenIdCredential), PrepareDelegationError>; +pub type OpenIdPrepareDelegationResult = Result< + ( + PreparedDelegation, + OpenIdDelegationProvider, + OpenIdCredential, + ), + PrepareDelegationError, +>; pub async fn openid_prepare_delegation( args: &OpenIdPrepareDelegationArgs, diff --git a/src/console/src/auth/register.rs b/src/console/src/auth/register.rs index 339cc272ec..6429ff7261 100644 --- a/src/console/src/auth/register.rs +++ b/src/console/src/auth/register.rs @@ -1,14 +1,14 @@ use crate::accounts::{get_optional_account, init_account, update_provider}; +use crate::types::state::OpenId; use crate::types::state::{Account, OpenIdData, Provider}; -use crate::types::state::{OpenId}; use candid::Principal; use junobuild_auth::delegation::types::UserKey; use junobuild_auth::openid::user::types::interface::OpenIdCredential; -use junobuild_auth::state::types::config::OpenIdAuthProvider; +use junobuild_auth::openid::user::types::provider::OpenIdDelegationProvider; pub async fn register_account( public_key: &UserKey, - provider: &OpenIdAuthProvider, + provider: &OpenIdDelegationProvider, credential: &OpenIdCredential, ) -> Result { let user_id = Principal::self_authenticating(public_key); diff --git a/src/console/src/impls.rs b/src/console/src/impls.rs index 5be57ff4b4..3697eab5de 100644 --- a/src/console/src/impls.rs +++ b/src/console/src/impls.rs @@ -1,8 +1,6 @@ use crate::memory::manager::init_stable_state; use crate::types::ledger::{Fee, IcpPayment, IcrcPayment, IcrcPaymentKey}; -use crate::types::state::{ - Account, HeapState, Segment, SegmentKey, State, StorableSegmentKind, -}; +use crate::types::state::{Account, HeapState, Segment, SegmentKey, State, StorableSegmentKind}; use candid::Principal; use ic_cdk::api::time; use ic_ledger_types::{BlockIndex, Tokens}; diff --git a/src/console/src/types.rs b/src/console/src/types.rs index 566879a2db..5f53fb0d6d 100644 --- a/src/console/src/types.rs +++ b/src/console/src/types.rs @@ -4,6 +4,7 @@ pub mod state { use candid::CandidType; use ic_ledger_types::{BlockIndex, Tokens}; use ic_stable_structures::StableBTreeMap; + use junobuild_auth::openid::user::types::provider::OpenIdDelegationProvider; use junobuild_auth::state::types::state::AuthenticationHeapState; use junobuild_cdn::proposals::{ProposalsStable, SegmentDeploymentVersion}; use junobuild_cdn::storage::{ProposalAssetsStable, ProposalContentChunksStable}; @@ -17,7 +18,6 @@ pub mod state { use junobuild_storage::types::state::StorageHeapState; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; - use junobuild_auth::state::types::config::OpenIdAuthProvider; pub type Accounts = HashMap; pub type IcpPayments = HashMap; @@ -83,7 +83,7 @@ pub mod state { #[derive(CandidType, Serialize, Deserialize, Clone)] pub struct OpenId { - pub provider: OpenIdAuthProvider, + pub provider: OpenIdDelegationProvider, pub data: OpenIdData, } diff --git a/src/libs/auth/src/delegation/get.rs b/src/libs/auth/src/delegation/get.rs index 3e97864fba..9a530b9c99 100644 --- a/src/libs/auth/src/delegation/get.rs +++ b/src/libs/auth/src/delegation/get.rs @@ -5,17 +5,17 @@ use crate::delegation::utils::seed::calculate_seed; use crate::delegation::utils::signature::{build_signature_inputs, build_signature_msg}; use crate::delegation::utils::targets::build_targets; use crate::openid::user::types::interface::{OpenIdCredential, OpenIdCredentialKey}; +use crate::openid::user::types::provider::OpenIdDelegationProvider; use crate::state::get_salt; use crate::state::services::read_state; use crate::strategies::{AuthCertificateStrategy, AuthHeapStrategy}; use serde_bytes::ByteBuf; -use crate::state::types::config::OpenIdAuthProvider; pub fn openid_get_delegation( session_key: &SessionKey, expiration: Timestamp, credential: &OpenIdCredential, - provider: &OpenIdAuthProvider, + provider: &OpenIdDelegationProvider, auth_heap: &impl AuthHeapStrategy, certificate: &impl AuthCertificateStrategy, ) -> GetDelegationResult { @@ -33,7 +33,7 @@ pub fn get_delegation( session_key: &SessionKey, expiration: Timestamp, key: &OpenIdCredentialKey, - provider: &OpenIdAuthProvider, + provider: &OpenIdDelegationProvider, auth_heap: &impl AuthHeapStrategy, certificate: &impl AuthCertificateStrategy, ) -> GetDelegationResult { diff --git a/src/libs/auth/src/delegation/prepare.rs b/src/libs/auth/src/delegation/prepare.rs index d7007d064b..e277c3c989 100644 --- a/src/libs/auth/src/delegation/prepare.rs +++ b/src/libs/auth/src/delegation/prepare.rs @@ -6,8 +6,8 @@ use crate::delegation::utils::duration::build_expiration; use crate::delegation::utils::seed::calculate_seed; use crate::delegation::utils::signature::{build_signature_inputs, build_signature_msg}; use crate::delegation::utils::targets::build_targets; -use crate::state::types::config::OpenIdAuthProvider; use crate::openid::user::types::interface::{OpenIdCredential, OpenIdCredentialKey}; +use crate::openid::user::types::provider::OpenIdDelegationProvider; use crate::state::get_salt; use crate::state::services::mutate_state; use crate::strategies::{AuthCertificateStrategy, AuthHeapStrategy}; @@ -18,7 +18,7 @@ use serde_bytes::ByteBuf; pub fn openid_prepare_delegation( session_key: &SessionKey, credential: &OpenIdCredential, - provider: &OpenIdAuthProvider, + provider: &OpenIdDelegationProvider, auth_heap: &impl AuthHeapStrategy, certificate: &impl AuthCertificateStrategy, ) -> PrepareDelegationResult { @@ -36,7 +36,7 @@ pub fn openid_prepare_delegation( fn prepare_delegation( session_key: &SessionKey, key: &OpenIdCredentialKey, - provider: &OpenIdAuthProvider, + provider: &OpenIdDelegationProvider, auth_heap: &impl AuthHeapStrategy, certificate: &impl AuthCertificateStrategy, ) -> PrepareDelegationResult { @@ -60,7 +60,7 @@ fn prepare_delegation( fn add_delegation_signature( session_key: &PublicKey, expiration: Timestamp, - provider: &OpenIdAuthProvider, + provider: &OpenIdDelegationProvider, seed: &[u8], auth_heap: &impl AuthHeapStrategy, ) { diff --git a/src/libs/auth/src/delegation/utils/duration.rs b/src/libs/auth/src/delegation/utils/duration.rs index 74a4fbb9a2..f3a9ed56a3 100644 --- a/src/libs/auth/src/delegation/utils/duration.rs +++ b/src/libs/auth/src/delegation/utils/duration.rs @@ -1,11 +1,14 @@ use crate::delegation::constants::{DEFAULT_EXPIRATION_PERIOD_NS, MAX_EXPIRATION_PERIOD_NS}; +use crate::openid::user::types::provider::OpenIdDelegationProvider; use crate::state::get_config; use crate::strategies::AuthHeapStrategy; use ic_cdk::api::time; use std::cmp::min; -use crate::state::types::config::OpenIdAuthProvider; -pub fn build_expiration(provider: &OpenIdAuthProvider, auth_heap: &impl AuthHeapStrategy) -> u64 { +pub fn build_expiration( + provider: &OpenIdDelegationProvider, + auth_heap: &impl AuthHeapStrategy, +) -> u64 { let max_time_to_live = get_config(auth_heap) .as_ref() .and_then(|config| config.openid.as_ref()) diff --git a/src/libs/auth/src/delegation/utils/targets.rs b/src/libs/auth/src/delegation/utils/targets.rs index 7fca708b00..8eb7eee257 100644 --- a/src/libs/auth/src/delegation/utils/targets.rs +++ b/src/libs/auth/src/delegation/utils/targets.rs @@ -1,8 +1,8 @@ use crate::delegation::types::DelegationTargets; +use crate::openid::user::types::provider::OpenIdDelegationProvider; use crate::state::get_config; use crate::strategies::AuthHeapStrategy; use junobuild_shared::ic::api::id; -use crate::state::types::config::OpenIdAuthProvider; // By default, and for security reasons, we restrict delegation to the authentication module // that created it. Developers can opt out (allow any targets) or define their own @@ -19,7 +19,7 @@ use crate::state::types::config::OpenIdAuthProvider; // Moreover, there is unlikely to be a valid use case where a delegation generated by the Satellite // should target no canister at all. pub fn build_targets( - provider: &OpenIdAuthProvider, + provider: &OpenIdDelegationProvider, auth_heap: &impl AuthHeapStrategy, ) -> Option { get_config(auth_heap) diff --git a/src/libs/auth/src/openid/jwt/provider.rs b/src/libs/auth/src/openid/jwt/provider.rs index 2913a4b2ff..72a387d569 100644 --- a/src/libs/auth/src/openid/jwt/provider.rs +++ b/src/libs/auth/src/openid/jwt/provider.rs @@ -1,7 +1,8 @@ use crate::openid::jwt::header::decode_jwt_header; use crate::openid::jwt::types::errors::JwtFindProviderError; use crate::openid::jwt::types::token::UnsafeClaims; -use crate::state::types::config::{OpenIdProviderAuthConfig, OpenIdAuthProviders, OpenIdAuthProvider}; +use crate::openid::user::types::provider::OpenIdDelegationProvider; +use crate::state::types::config::{OpenIdAuthProviders, OpenIdProviderAuthConfig}; use jsonwebtoken::dangerous; /// ⚠️ **Warning:** This function decodes the JWT payload *without verifying its signature*. @@ -9,7 +10,7 @@ use jsonwebtoken::dangerous; pub fn unsafe_find_jwt_auth_provider<'a>( providers: &'a OpenIdAuthProviders, jwt: &str, -) -> Result<(OpenIdAuthProvider, &'a OpenIdProviderAuthConfig), JwtFindProviderError> { +) -> Result<(OpenIdDelegationProvider, &'a OpenIdProviderAuthConfig), JwtFindProviderError> { // 1) Header sanity check decode_jwt_header(jwt).map_err(JwtFindProviderError::from)?; @@ -34,8 +35,8 @@ pub fn unsafe_find_jwt_auth_provider<'a>( mod tests { use super::unsafe_find_jwt_auth_provider; use crate::openid::jwt::types::errors::JwtFindProviderError; - use crate::openid::types::provider::OpenIdProvider; - use crate::state::types::config::{OpenIdProviderAuthConfig, OpenIdAuthProviders, OpenIdAuthProvider}; + use crate::openid::user::types::provider::OpenIdDelegationProvider; + use crate::state::types::config::{OpenIdAuthProviders, OpenIdProviderAuthConfig}; use base64::engine::general_purpose::URL_SAFE_NO_PAD; use base64::Engine; use serde_json::json; @@ -53,7 +54,7 @@ mod tests { fn providers_with_google() -> OpenIdAuthProviders { let mut map = BTreeMap::new(); map.insert( - OpenIdAuthProvider::Google, + OpenIdDelegationProvider::Google, OpenIdProviderAuthConfig { client_id: "client-123".into(), delegation: None, @@ -72,7 +73,7 @@ mod tests { let (provider, cfg) = unsafe_find_jwt_auth_provider(&provs, &jwt).expect("should match provider"); - assert_eq!(provider, OpenIdAuthProvider::Google); + assert_eq!(provider, OpenIdDelegationProvider::Google); assert_eq!(cfg.client_id, "client-123"); } @@ -84,7 +85,7 @@ mod tests { let provs = providers_with_google(); let (provider, _) = unsafe_find_jwt_auth_provider(&provs, &jwt).expect("should match even without typ"); - assert_eq!(provider, OpenIdAuthProvider::Google); + assert_eq!(provider, OpenIdDelegationProvider::Google); } #[test] diff --git a/src/libs/auth/src/openid/user/impls.rs b/src/libs/auth/src/openid/user/impls.rs index 0477724ab3..7196eff2e5 100644 --- a/src/libs/auth/src/openid/user/impls.rs +++ b/src/libs/auth/src/openid/user/impls.rs @@ -1,5 +1,7 @@ use crate::openid::jwt::types::token::Claims; +use crate::openid::types::provider::OpenIdProvider; use crate::openid::user::types::interface::{OpenIdCredential, OpenIdCredentialKey}; +use crate::openid::user::types::provider::OpenIdDelegationProvider; use jsonwebtoken::TokenData; impl From> for OpenIdCredential { @@ -26,3 +28,43 @@ impl<'a> From<&'a OpenIdCredential> for OpenIdCredentialKey<'a> { } } } + +impl TryFrom<&OpenIdProvider> for OpenIdDelegationProvider { + type Error = String; + + fn try_from(provider: &OpenIdProvider) -> Result { + match provider { + OpenIdProvider::Google => Ok(OpenIdDelegationProvider::Google), + OpenIdProvider::GitHubProxy => Ok(OpenIdDelegationProvider::GitHub), + _ => Err(format!( + "{:?} is not supported for user authentication", + provider + )), + } + } +} + +impl From<&OpenIdDelegationProvider> for OpenIdProvider { + fn from(auth_provider: &OpenIdDelegationProvider) -> Self { + match auth_provider { + OpenIdDelegationProvider::Google => OpenIdProvider::Google, + OpenIdDelegationProvider::GitHub => OpenIdProvider::GitHubProxy, + } + } +} + +impl OpenIdDelegationProvider { + pub fn jwks_url(&self) -> &'static str { + match self { + Self::Google => OpenIdProvider::Google.jwks_url(), + Self::GitHub => OpenIdProvider::GitHubProxy.jwks_url(), + } + } + + pub fn issuers(&self) -> &[&'static str] { + match self { + Self::Google => OpenIdProvider::Google.issuers(), + Self::GitHub => OpenIdProvider::GitHubProxy.issuers(), + } + } +} diff --git a/src/libs/auth/src/openid/user/types.rs b/src/libs/auth/src/openid/user/types.rs index 97f7cd6a2b..5af20ee0a9 100644 --- a/src/libs/auth/src/openid/user/types.rs +++ b/src/libs/auth/src/openid/user/types.rs @@ -1,3 +1,6 @@ +use candid::{CandidType, Deserialize}; +use serde::Serialize; + pub mod interface { pub struct OpenIdCredentialKey<'a> { pub iss: &'a String, @@ -34,5 +37,14 @@ pub(crate) mod errors { } pub mod provider { + use candid::{CandidType, Deserialize}; + use serde::Serialize; -} \ No newline at end of file + #[derive( + CandidType, Serialize, Deserialize, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug, + )] + pub enum OpenIdDelegationProvider { + Google, + GitHub, + } +} diff --git a/src/libs/auth/src/openid/user/verify.rs b/src/libs/auth/src/openid/user/verify.rs index 381ab7bff1..fa97648e22 100644 --- a/src/libs/auth/src/openid/user/verify.rs +++ b/src/libs/auth/src/openid/user/verify.rs @@ -6,13 +6,14 @@ use crate::openid::jwt::{unsafe_find_jwt_auth_provider, verify_openid_jwt}; use crate::openid::types::provider::OpenIdProvider; use crate::openid::user::types::errors::VerifyOpenidCredentialsError; use crate::openid::user::types::interface::OpenIdCredential; +use crate::openid::user::types::provider::OpenIdDelegationProvider; use crate::openid::utils::build_nonce; -use crate::state::types::config::{OpenIdAuthProvider, OpenIdAuthProviderClientId, OpenIdAuthProviders}; +use crate::state::types::config::{OpenIdAuthProviderClientId, OpenIdAuthProviders}; use crate::state::types::state::Salt; use crate::strategies::AuthHeapStrategy; type VerifyOpenIdCredentialsResult = - Result<(OpenIdCredential, OpenIdAuthProvider), VerifyOpenidCredentialsError>; + Result<(OpenIdCredential, OpenIdDelegationProvider), VerifyOpenidCredentialsError>; pub async fn verify_openid_credentials_with_jwks_renewal( jwt: &str, @@ -51,7 +52,7 @@ pub fn verify_openid_credentials_with_cached_jwks( fn verify_openid_credentials( jwt: &str, jwks: &Jwks, - provider: &OpenIdAuthProvider, + provider: &OpenIdDelegationProvider, client_id: &OpenIdAuthProviderClientId, salt: &Salt, ) -> VerifyOpenIdCredentialsResult { diff --git a/src/libs/auth/src/state/impls.rs b/src/libs/auth/src/state/impls.rs index b7f46bace6..bba2acce0f 100644 --- a/src/libs/auth/src/state/impls.rs +++ b/src/libs/auth/src/state/impls.rs @@ -1,5 +1,5 @@ -use crate::openid::types::provider::{OpenIdCertificate, OpenIdProvider}; -use crate::state::types::config::{AuthenticationConfig, OpenIdAuthProvider}; +use crate::openid::types::provider::OpenIdCertificate; +use crate::state::types::config::AuthenticationConfig; use crate::state::types::interface::SetAuthenticationConfig; use crate::state::types::state::{OpenIdCachedCertificate, OpenIdLastFetchAttempt}; use ic_cdk::api::time; @@ -74,44 +74,3 @@ impl OpenIdCachedCertificate { self.last_fetch_attempt.streak_count = 0; } } - - -impl TryFrom<&OpenIdProvider> for OpenIdAuthProvider { - type Error = String; - - fn try_from(provider: &OpenIdProvider) -> Result { - match provider { - OpenIdProvider::Google => Ok(OpenIdAuthProvider::Google), - OpenIdProvider::GitHubProxy => Ok(OpenIdAuthProvider::GitHub), - _ => Err(format!( - "{:?} is not supported for user authentication", - provider - )), - } - } -} - -impl From<&OpenIdAuthProvider> for OpenIdProvider { - fn from(auth_provider: &OpenIdAuthProvider) -> Self { - match auth_provider { - OpenIdAuthProvider::Google => OpenIdProvider::Google, - OpenIdAuthProvider::GitHub => OpenIdProvider::GitHubProxy, - } - } -} - -impl OpenIdAuthProvider { - pub fn jwks_url(&self) -> &'static str { - match self { - Self::Google => OpenIdProvider::Google.jwks_url(), - Self::GitHub => OpenIdProvider::GitHubProxy.jwks_url(), - } - } - - pub fn issuers(&self) -> &[&'static str] { - match self { - Self::Google => OpenIdProvider::Google.issuers(), - Self::GitHub => OpenIdProvider::GitHubProxy.issuers(), - } - } -} \ No newline at end of file diff --git a/src/libs/auth/src/state/types.rs b/src/libs/auth/src/state/types.rs index 84500f1337..f8db817b2b 100644 --- a/src/libs/auth/src/state/types.rs +++ b/src/libs/auth/src/state/types.rs @@ -53,6 +53,7 @@ pub(crate) mod runtime_state { pub mod config { use crate::delegation::types::DelegationTargets; + use crate::openid::user::types::provider::OpenIdDelegationProvider; use candid::{CandidType, Deserialize, Principal}; use junobuild_shared::types::core::DomainName; use junobuild_shared::types::state::{Timestamp, Version}; @@ -86,15 +87,7 @@ pub mod config { pub allowed_callers: Vec, } - #[derive( - CandidType, Serialize, Deserialize, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug, - )] - pub enum OpenIdAuthProvider { - Google, - GitHub, - } - - pub type OpenIdAuthProviders = BTreeMap; + pub type OpenIdAuthProviders = BTreeMap; pub type OpenIdAuthProviderClientId = String; diff --git a/src/libs/satellite/src/auth/delegation.rs b/src/libs/satellite/src/auth/delegation.rs index 0c28126f6f..5867de2d03 100644 --- a/src/libs/satellite/src/auth/delegation.rs +++ b/src/libs/satellite/src/auth/delegation.rs @@ -4,13 +4,19 @@ use junobuild_auth::delegation::types::{ GetDelegationError, GetDelegationResult, OpenIdGetDelegationArgs, OpenIdPrepareDelegationArgs, PrepareDelegationError, PreparedDelegation, }; -use junobuild_auth::openid::types::provider::OpenIdProvider; use junobuild_auth::openid::user::types::interface::OpenIdCredential; -use junobuild_auth::state::types::config::{OpenIdAuthProvider, OpenIdAuthProviders}; +use junobuild_auth::openid::user::types::provider::OpenIdDelegationProvider; +use junobuild_auth::state::types::config::OpenIdAuthProviders; use junobuild_auth::{delegation, openid}; -pub type OpenIdPrepareDelegationResult = - Result<(PreparedDelegation, OpenIdAuthProvider, OpenIdCredential), PrepareDelegationError>; +pub type OpenIdPrepareDelegationResult = Result< + ( + PreparedDelegation, + OpenIdDelegationProvider, + OpenIdCredential, + ), + PrepareDelegationError, +>; pub async fn openid_prepare_delegation( args: &OpenIdPrepareDelegationArgs, diff --git a/src/libs/satellite/src/auth/register.rs b/src/libs/satellite/src/auth/register.rs index 353ef27f33..308d577780 100644 --- a/src/libs/satellite/src/auth/register.rs +++ b/src/libs/satellite/src/auth/register.rs @@ -7,9 +7,8 @@ use crate::user::core::types::state::{OpenIdData, ProviderData, UserData}; use crate::Doc; use candid::Principal; use junobuild_auth::delegation::types::UserKey; -use junobuild_auth::openid::types::provider::OpenIdProvider; use junobuild_auth::openid::user::types::interface::OpenIdCredential; -use junobuild_auth::state::types::config::OpenIdAuthProvider; +use junobuild_auth::openid::user::types::provider::OpenIdDelegationProvider; use junobuild_collections::constants::db::COLLECTION_USER_KEY; use junobuild_collections::msg::msg_db_collection_not_found; use junobuild_shared::ic::api::id; @@ -17,7 +16,7 @@ use junobuild_utils::decode_doc_data; pub fn register_user( public_key: &UserKey, - provider: &OpenIdAuthProvider, + provider: &OpenIdDelegationProvider, credential: &OpenIdCredential, ) -> Result { let user_collection = COLLECTION_USER_KEY.to_string(); diff --git a/src/libs/satellite/src/user/core/impls.rs b/src/libs/satellite/src/user/core/impls.rs index 2b6ab33a1e..4c9d374c56 100644 --- a/src/libs/satellite/src/user/core/impls.rs +++ b/src/libs/satellite/src/user/core/impls.rs @@ -11,8 +11,8 @@ use crate::user::core::types::state::{ use crate::{Doc, SetDoc}; use junobuild_auth::openid::types::provider::OpenIdProvider; use junobuild_auth::openid::user::types::interface::OpenIdCredential; +use junobuild_auth::openid::user::types::provider::OpenIdDelegationProvider; use junobuild_auth::profile::types::{OpenIdProfile, Validated}; -use junobuild_auth::state::types::config::OpenIdAuthProvider; use junobuild_utils::encode_doc_data; impl Validated for WebAuthnData { @@ -161,11 +161,11 @@ impl From<&OpenIdCredential> for OpenIdData { } } -impl From<&OpenIdAuthProvider> for AuthProvider { - fn from(auth_provider: &OpenIdAuthProvider) -> Self { +impl From<&OpenIdDelegationProvider> for AuthProvider { + fn from(auth_provider: &OpenIdDelegationProvider) -> Self { match auth_provider { - OpenIdAuthProvider::Google => AuthProvider::Google, - OpenIdAuthProvider::GitHub => AuthProvider::GitHub, + OpenIdDelegationProvider::Google => AuthProvider::Google, + OpenIdDelegationProvider::GitHub => AuthProvider::GitHub, } } } From 9caae395c64fbd7a4e4b72f82cee35b455f548cc Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 28 Jan 2026 10:58:15 +0100 Subject: [PATCH 11/32] fea: rename better meaning --- src/console/src/accounts/impls.rs | 2 +- src/console/src/auth/delegation.rs | 34 ++++++++++--------- src/console/src/auth/register.rs | 4 +-- src/console/src/types.rs | 2 +- src/libs/auth/src/delegation/get.rs | 4 +-- src/libs/auth/src/delegation/impls.rs | 2 +- src/libs/auth/src/delegation/prepare.rs | 4 +-- .../auth/src/delegation/utils/duration.rs | 2 +- src/libs/auth/src/delegation/utils/seed.rs | 4 +-- src/libs/auth/src/delegation/utils/targets.rs | 2 +- .../openid/{workload => automation}/mod.rs | 0 .../openid/{workload => automation}/types.rs | 2 +- .../openid/{workload => automation}/verify.rs | 12 +++---- .../src/openid/{user => delegation}/impls.rs | 4 +-- .../src/openid/{user => delegation}/mod.rs | 0 .../src/openid/{user => delegation}/types.rs | 0 .../src/openid/{user => delegation}/verify.rs | 14 ++++---- src/libs/auth/src/openid/jwt/provider.rs | 4 +-- src/libs/auth/src/openid/mod.rs | 4 +-- src/libs/auth/src/state/types.rs | 2 +- src/libs/satellite/src/auth/delegation.rs | 34 ++++++++++--------- src/libs/satellite/src/auth/register.rs | 4 +-- .../satellite/src/controllers/authenticate.rs | 2 +- .../satellite/src/controllers/constants.rs | 2 +- src/libs/satellite/src/controllers/types.rs | 4 +-- src/libs/satellite/src/user/core/impls.rs | 4 +-- 26 files changed, 78 insertions(+), 74 deletions(-) rename src/libs/auth/src/openid/{workload => automation}/mod.rs (100%) rename src/libs/auth/src/openid/{workload => automation}/types.rs (87%) rename src/libs/auth/src/openid/{workload => automation}/verify.rs (77%) rename src/libs/auth/src/openid/{user => delegation}/impls.rs (92%) rename src/libs/auth/src/openid/{user => delegation}/mod.rs (100%) rename src/libs/auth/src/openid/{user => delegation}/types.rs (100%) rename src/libs/auth/src/openid/{user => delegation}/verify.rs (87%) diff --git a/src/console/src/accounts/impls.rs b/src/console/src/accounts/impls.rs index b006f6da2e..516dac591f 100644 --- a/src/console/src/accounts/impls.rs +++ b/src/console/src/accounts/impls.rs @@ -1,7 +1,7 @@ use crate::constants::E8S_PER_ICP; use crate::types::state::{Account, OpenIdData, Provider}; use ic_cdk::api::time; -use junobuild_auth::openid::user::types::interface::OpenIdCredential; +use junobuild_auth::openid::delegation::types::interface::OpenIdCredential; use junobuild_auth::profile::types::OpenIdProfile; use junobuild_shared::types::state::{MissionControlId, UserId}; diff --git a/src/console/src/auth/delegation.rs b/src/console/src/auth/delegation.rs index 5867de2d03..b259366fce 100644 --- a/src/console/src/auth/delegation.rs +++ b/src/console/src/auth/delegation.rs @@ -4,8 +4,8 @@ use junobuild_auth::delegation::types::{ GetDelegationError, GetDelegationResult, OpenIdGetDelegationArgs, OpenIdPrepareDelegationArgs, PrepareDelegationError, PreparedDelegation, }; -use junobuild_auth::openid::user::types::interface::OpenIdCredential; -use junobuild_auth::openid::user::types::provider::OpenIdDelegationProvider; +use junobuild_auth::openid::delegation::types::interface::OpenIdCredential; +use junobuild_auth::openid::delegation::types::provider::OpenIdDelegationProvider; use junobuild_auth::state::types::config::OpenIdAuthProviders; use junobuild_auth::{delegation, openid}; @@ -22,14 +22,15 @@ pub async fn openid_prepare_delegation( args: &OpenIdPrepareDelegationArgs, providers: &OpenIdAuthProviders, ) -> OpenIdPrepareDelegationResult { - let (credential, provider) = match openid::user::verify_openid_credentials_with_jwks_renewal( - &args.jwt, &args.salt, providers, &AuthHeap, - ) - .await - { - Ok(value) => value, - Err(err) => return Err(PrepareDelegationError::from(err)), - }; + let (credential, provider) = + match openid::delegation::verify_openid_credentials_with_jwks_renewal( + &args.jwt, &args.salt, providers, &AuthHeap, + ) + .await + { + Ok(value) => value, + Err(err) => return Err(PrepareDelegationError::from(err)), + }; let result = delegation::openid_prepare_delegation( &args.session_key, @@ -46,12 +47,13 @@ pub fn openid_get_delegation( args: &OpenIdGetDelegationArgs, providers: &OpenIdAuthProviders, ) -> GetDelegationResult { - let (credential, provider) = match openid::user::verify_openid_credentials_with_cached_jwks( - &args.jwt, &args.salt, providers, &AuthHeap, - ) { - Ok(value) => value, - Err(err) => return Err(GetDelegationError::from(err)), - }; + let (credential, provider) = + match openid::delegation::verify_openid_credentials_with_cached_jwks( + &args.jwt, &args.salt, providers, &AuthHeap, + ) { + Ok(value) => value, + Err(err) => return Err(GetDelegationError::from(err)), + }; delegation::openid_get_delegation( &args.session_key, diff --git a/src/console/src/auth/register.rs b/src/console/src/auth/register.rs index 6429ff7261..4da81b00c6 100644 --- a/src/console/src/auth/register.rs +++ b/src/console/src/auth/register.rs @@ -3,8 +3,8 @@ use crate::types::state::OpenId; use crate::types::state::{Account, OpenIdData, Provider}; use candid::Principal; use junobuild_auth::delegation::types::UserKey; -use junobuild_auth::openid::user::types::interface::OpenIdCredential; -use junobuild_auth::openid::user::types::provider::OpenIdDelegationProvider; +use junobuild_auth::openid::delegation::types::interface::OpenIdCredential; +use junobuild_auth::openid::delegation::types::provider::OpenIdDelegationProvider; pub async fn register_account( public_key: &UserKey, diff --git a/src/console/src/types.rs b/src/console/src/types.rs index 5f53fb0d6d..70eb514de2 100644 --- a/src/console/src/types.rs +++ b/src/console/src/types.rs @@ -4,7 +4,7 @@ pub mod state { use candid::CandidType; use ic_ledger_types::{BlockIndex, Tokens}; use ic_stable_structures::StableBTreeMap; - use junobuild_auth::openid::user::types::provider::OpenIdDelegationProvider; + use junobuild_auth::openid::delegation::types::provider::OpenIdDelegationProvider; use junobuild_auth::state::types::state::AuthenticationHeapState; use junobuild_cdn::proposals::{ProposalsStable, SegmentDeploymentVersion}; use junobuild_cdn::storage::{ProposalAssetsStable, ProposalContentChunksStable}; diff --git a/src/libs/auth/src/delegation/get.rs b/src/libs/auth/src/delegation/get.rs index 9a530b9c99..2117577734 100644 --- a/src/libs/auth/src/delegation/get.rs +++ b/src/libs/auth/src/delegation/get.rs @@ -4,8 +4,8 @@ use crate::delegation::types::{ use crate::delegation::utils::seed::calculate_seed; use crate::delegation::utils::signature::{build_signature_inputs, build_signature_msg}; use crate::delegation::utils::targets::build_targets; -use crate::openid::user::types::interface::{OpenIdCredential, OpenIdCredentialKey}; -use crate::openid::user::types::provider::OpenIdDelegationProvider; +use crate::openid::delegation::types::interface::{OpenIdCredential, OpenIdCredentialKey}; +use crate::openid::delegation::types::provider::OpenIdDelegationProvider; use crate::state::get_salt; use crate::state::services::read_state; use crate::strategies::{AuthCertificateStrategy, AuthHeapStrategy}; diff --git a/src/libs/auth/src/delegation/impls.rs b/src/libs/auth/src/delegation/impls.rs index 564bee5d9a..dbdfc2a9ec 100644 --- a/src/libs/auth/src/delegation/impls.rs +++ b/src/libs/auth/src/delegation/impls.rs @@ -1,5 +1,5 @@ use crate::delegation::types::{GetDelegationError, PrepareDelegationError}; -use crate::openid::user::types::errors::VerifyOpenidCredentialsError; +use crate::openid::delegation::types::errors::VerifyOpenidCredentialsError; impl From for GetDelegationError { fn from(e: VerifyOpenidCredentialsError) -> Self { diff --git a/src/libs/auth/src/delegation/prepare.rs b/src/libs/auth/src/delegation/prepare.rs index e277c3c989..6889ba555f 100644 --- a/src/libs/auth/src/delegation/prepare.rs +++ b/src/libs/auth/src/delegation/prepare.rs @@ -6,8 +6,8 @@ use crate::delegation::utils::duration::build_expiration; use crate::delegation::utils::seed::calculate_seed; use crate::delegation::utils::signature::{build_signature_inputs, build_signature_msg}; use crate::delegation::utils::targets::build_targets; -use crate::openid::user::types::interface::{OpenIdCredential, OpenIdCredentialKey}; -use crate::openid::user::types::provider::OpenIdDelegationProvider; +use crate::openid::delegation::types::interface::{OpenIdCredential, OpenIdCredentialKey}; +use crate::openid::delegation::types::provider::OpenIdDelegationProvider; use crate::state::get_salt; use crate::state::services::mutate_state; use crate::strategies::{AuthCertificateStrategy, AuthHeapStrategy}; diff --git a/src/libs/auth/src/delegation/utils/duration.rs b/src/libs/auth/src/delegation/utils/duration.rs index f3a9ed56a3..7a7ca03d3a 100644 --- a/src/libs/auth/src/delegation/utils/duration.rs +++ b/src/libs/auth/src/delegation/utils/duration.rs @@ -1,5 +1,5 @@ use crate::delegation::constants::{DEFAULT_EXPIRATION_PERIOD_NS, MAX_EXPIRATION_PERIOD_NS}; -use crate::openid::user::types::provider::OpenIdDelegationProvider; +use crate::openid::delegation::types::provider::OpenIdDelegationProvider; use crate::state::get_config; use crate::strategies::AuthHeapStrategy; use ic_cdk::api::time; diff --git a/src/libs/auth/src/delegation/utils/seed.rs b/src/libs/auth/src/delegation/utils/seed.rs index 6efd35777b..3911f4dbfc 100644 --- a/src/libs/auth/src/delegation/utils/seed.rs +++ b/src/libs/auth/src/delegation/utils/seed.rs @@ -1,4 +1,4 @@ -use crate::openid::user::types::interface::OpenIdCredentialKey; +use crate::openid::delegation::types::interface::OpenIdCredentialKey; use crate::state::types::state::Salt; use ic_certification::Hash; use sha2::{Digest, Sha256}; @@ -30,7 +30,7 @@ fn hash_bytes(value: impl AsRef<[u8]>) -> Hash { #[cfg(test)] mod tests { use super::calculate_seed; - use crate::openid::user::types::interface::OpenIdCredentialKey; + use crate::openid::delegation::types::interface::OpenIdCredentialKey; use crate::state::types::state::Salt; use ic_certification::Hash; use sha2::{Digest, Sha256}; diff --git a/src/libs/auth/src/delegation/utils/targets.rs b/src/libs/auth/src/delegation/utils/targets.rs index 8eb7eee257..7704204e0f 100644 --- a/src/libs/auth/src/delegation/utils/targets.rs +++ b/src/libs/auth/src/delegation/utils/targets.rs @@ -1,5 +1,5 @@ use crate::delegation::types::DelegationTargets; -use crate::openid::user::types::provider::OpenIdDelegationProvider; +use crate::openid::delegation::types::provider::OpenIdDelegationProvider; use crate::state::get_config; use crate::strategies::AuthHeapStrategy; use junobuild_shared::ic::api::id; diff --git a/src/libs/auth/src/openid/workload/mod.rs b/src/libs/auth/src/openid/automation/mod.rs similarity index 100% rename from src/libs/auth/src/openid/workload/mod.rs rename to src/libs/auth/src/openid/automation/mod.rs diff --git a/src/libs/auth/src/openid/workload/types.rs b/src/libs/auth/src/openid/automation/types.rs similarity index 87% rename from src/libs/auth/src/openid/workload/types.rs rename to src/libs/auth/src/openid/automation/types.rs index 1f7306ef36..9fd4d03ff1 100644 --- a/src/libs/auth/src/openid/workload/types.rs +++ b/src/libs/auth/src/openid/automation/types.rs @@ -5,7 +5,7 @@ pub mod errors { use serde::Serialize; #[derive(CandidType, Serialize, Deserialize, Debug)] - pub enum VerifyOpenidWorkloadCredentialsError { + pub enum VerifyOpenidAutomationCredentialsError { GetOrFetchJwks(GetOrRefreshJwksError), GetCachedJwks, JwtVerify(JwtVerifyError), diff --git a/src/libs/auth/src/openid/workload/verify.rs b/src/libs/auth/src/openid/automation/verify.rs similarity index 77% rename from src/libs/auth/src/openid/workload/verify.rs rename to src/libs/auth/src/openid/automation/verify.rs index 64eaeb86dc..20236f718c 100644 --- a/src/libs/auth/src/openid/workload/verify.rs +++ b/src/libs/auth/src/openid/automation/verify.rs @@ -1,22 +1,22 @@ +use crate::openid::automation::types::errors::VerifyOpenidAutomationCredentialsError; use crate::openid::jwkset::get_or_refresh_jwks; use crate::openid::jwt::types::cert::Jwks; use crate::openid::jwt::types::errors::JwtVerifyError; use crate::openid::jwt::types::token::Claims; use crate::openid::jwt::verify_openid_jwt; use crate::openid::types::provider::OpenIdProvider; -use crate::openid::workload::types::errors::VerifyOpenidWorkloadCredentialsError; use crate::strategies::AuthHeapStrategy; -type VerifyOpenIdWorkloadCredentialsResult = Result<(), VerifyOpenidWorkloadCredentialsError>; +type VerifyOpenIdAutomationCredentialsResult = Result<(), VerifyOpenidAutomationCredentialsError>; pub async fn verify_openid_credentials_with_jwks_renewal( jwt: &str, provider: &OpenIdProvider, auth_heap: &impl AuthHeapStrategy, -) -> VerifyOpenIdWorkloadCredentialsResult { +) -> VerifyOpenIdAutomationCredentialsResult { let jwks = get_or_refresh_jwks(&provider, jwt, auth_heap) .await - .map_err(VerifyOpenidWorkloadCredentialsError::GetOrFetchJwks)?; + .map_err(VerifyOpenidAutomationCredentialsError::GetOrFetchJwks)?; verify_openid_credentials(jwt, &jwks, &provider) } @@ -25,7 +25,7 @@ fn verify_openid_credentials( jwt: &str, jwks: &Jwks, provider: &OpenIdProvider, -) -> VerifyOpenIdWorkloadCredentialsResult { +) -> VerifyOpenIdAutomationCredentialsResult { let assert_audience = |claims: &Claims| -> Result<(), JwtVerifyError> { // if claims.aud != client_id.as_str() { // return Err(JwtVerifyError::BadClaim("aud".to_string())); @@ -55,7 +55,7 @@ fn verify_openid_credentials( &assert_audience, &assert_no_replay, ) - .map_err(VerifyOpenidWorkloadCredentialsError::JwtVerify)?; + .map_err(VerifyOpenidAutomationCredentialsError::JwtVerify)?; Ok(()) } diff --git a/src/libs/auth/src/openid/user/impls.rs b/src/libs/auth/src/openid/delegation/impls.rs similarity index 92% rename from src/libs/auth/src/openid/user/impls.rs rename to src/libs/auth/src/openid/delegation/impls.rs index 7196eff2e5..e42041d995 100644 --- a/src/libs/auth/src/openid/user/impls.rs +++ b/src/libs/auth/src/openid/delegation/impls.rs @@ -1,7 +1,7 @@ +use crate::openid::delegation::types::interface::{OpenIdCredential, OpenIdCredentialKey}; +use crate::openid::delegation::types::provider::OpenIdDelegationProvider; use crate::openid::jwt::types::token::Claims; use crate::openid::types::provider::OpenIdProvider; -use crate::openid::user::types::interface::{OpenIdCredential, OpenIdCredentialKey}; -use crate::openid::user::types::provider::OpenIdDelegationProvider; use jsonwebtoken::TokenData; impl From> for OpenIdCredential { diff --git a/src/libs/auth/src/openid/user/mod.rs b/src/libs/auth/src/openid/delegation/mod.rs similarity index 100% rename from src/libs/auth/src/openid/user/mod.rs rename to src/libs/auth/src/openid/delegation/mod.rs diff --git a/src/libs/auth/src/openid/user/types.rs b/src/libs/auth/src/openid/delegation/types.rs similarity index 100% rename from src/libs/auth/src/openid/user/types.rs rename to src/libs/auth/src/openid/delegation/types.rs diff --git a/src/libs/auth/src/openid/user/verify.rs b/src/libs/auth/src/openid/delegation/verify.rs similarity index 87% rename from src/libs/auth/src/openid/user/verify.rs rename to src/libs/auth/src/openid/delegation/verify.rs index fa97648e22..1078079e4d 100644 --- a/src/libs/auth/src/openid/user/verify.rs +++ b/src/libs/auth/src/openid/delegation/verify.rs @@ -1,18 +1,18 @@ +use crate::openid::delegation::types::errors::VerifyOpenidCredentialsError; +use crate::openid::delegation::types::interface::OpenIdCredential; +use crate::openid::delegation::types::provider::OpenIdDelegationProvider; use crate::openid::jwkset::{get_jwks, get_or_refresh_jwks}; use crate::openid::jwt::types::cert::Jwks; use crate::openid::jwt::types::errors::JwtVerifyError; use crate::openid::jwt::types::token::Claims; use crate::openid::jwt::{unsafe_find_jwt_auth_provider, verify_openid_jwt}; use crate::openid::types::provider::OpenIdProvider; -use crate::openid::user::types::errors::VerifyOpenidCredentialsError; -use crate::openid::user::types::interface::OpenIdCredential; -use crate::openid::user::types::provider::OpenIdDelegationProvider; use crate::openid::utils::build_nonce; use crate::state::types::config::{OpenIdAuthProviderClientId, OpenIdAuthProviders}; use crate::state::types::state::Salt; use crate::strategies::AuthHeapStrategy; -type VerifyOpenIdCredentialsResult = +type VerifyOpenIdDelegationCredentialsResult = Result<(OpenIdCredential, OpenIdDelegationProvider), VerifyOpenidCredentialsError>; pub async fn verify_openid_credentials_with_jwks_renewal( @@ -20,7 +20,7 @@ pub async fn verify_openid_credentials_with_jwks_renewal( salt: &Salt, providers: &OpenIdAuthProviders, auth_heap: &impl AuthHeapStrategy, -) -> VerifyOpenIdCredentialsResult { +) -> VerifyOpenIdDelegationCredentialsResult { let (auth_provider, config) = unsafe_find_jwt_auth_provider(providers, jwt) .map_err(VerifyOpenidCredentialsError::JwtFindProvider)?; @@ -38,7 +38,7 @@ pub fn verify_openid_credentials_with_cached_jwks( salt: &Salt, providers: &OpenIdAuthProviders, auth_heap: &impl AuthHeapStrategy, -) -> VerifyOpenIdCredentialsResult { +) -> VerifyOpenIdDelegationCredentialsResult { let (auth_provider, config) = unsafe_find_jwt_auth_provider(providers, jwt) .map_err(VerifyOpenidCredentialsError::JwtFindProvider)?; @@ -55,7 +55,7 @@ fn verify_openid_credentials( provider: &OpenIdDelegationProvider, client_id: &OpenIdAuthProviderClientId, salt: &Salt, -) -> VerifyOpenIdCredentialsResult { +) -> VerifyOpenIdDelegationCredentialsResult { let assert_audience = |claims: &Claims| -> Result<(), JwtVerifyError> { if claims.aud != client_id.as_str() { return Err(JwtVerifyError::BadClaim("aud".to_string())); diff --git a/src/libs/auth/src/openid/jwt/provider.rs b/src/libs/auth/src/openid/jwt/provider.rs index 72a387d569..0890733fa0 100644 --- a/src/libs/auth/src/openid/jwt/provider.rs +++ b/src/libs/auth/src/openid/jwt/provider.rs @@ -1,7 +1,7 @@ +use crate::openid::delegation::types::provider::OpenIdDelegationProvider; use crate::openid::jwt::header::decode_jwt_header; use crate::openid::jwt::types::errors::JwtFindProviderError; use crate::openid::jwt::types::token::UnsafeClaims; -use crate::openid::user::types::provider::OpenIdDelegationProvider; use crate::state::types::config::{OpenIdAuthProviders, OpenIdProviderAuthConfig}; use jsonwebtoken::dangerous; @@ -34,8 +34,8 @@ pub fn unsafe_find_jwt_auth_provider<'a>( #[cfg(test)] mod tests { use super::unsafe_find_jwt_auth_provider; + use crate::openid::delegation::types::provider::OpenIdDelegationProvider; use crate::openid::jwt::types::errors::JwtFindProviderError; - use crate::openid::user::types::provider::OpenIdDelegationProvider; use crate::state::types::config::{OpenIdAuthProviders, OpenIdProviderAuthConfig}; use base64::engine::general_purpose::URL_SAFE_NO_PAD; use base64::Engine; diff --git a/src/libs/auth/src/openid/mod.rs b/src/libs/auth/src/openid/mod.rs index c31a2a0561..e962e11ab5 100644 --- a/src/libs/auth/src/openid/mod.rs +++ b/src/libs/auth/src/openid/mod.rs @@ -1,7 +1,7 @@ +pub mod automation; +pub mod delegation; mod impls; pub mod jwkset; pub mod jwt; pub mod types; -pub mod user; mod utils; -pub mod workload; diff --git a/src/libs/auth/src/state/types.rs b/src/libs/auth/src/state/types.rs index f8db817b2b..f2f1058669 100644 --- a/src/libs/auth/src/state/types.rs +++ b/src/libs/auth/src/state/types.rs @@ -53,7 +53,7 @@ pub(crate) mod runtime_state { pub mod config { use crate::delegation::types::DelegationTargets; - use crate::openid::user::types::provider::OpenIdDelegationProvider; + use crate::openid::delegation::types::provider::OpenIdDelegationProvider; use candid::{CandidType, Deserialize, Principal}; use junobuild_shared::types::core::DomainName; use junobuild_shared::types::state::{Timestamp, Version}; diff --git a/src/libs/satellite/src/auth/delegation.rs b/src/libs/satellite/src/auth/delegation.rs index 5867de2d03..b259366fce 100644 --- a/src/libs/satellite/src/auth/delegation.rs +++ b/src/libs/satellite/src/auth/delegation.rs @@ -4,8 +4,8 @@ use junobuild_auth::delegation::types::{ GetDelegationError, GetDelegationResult, OpenIdGetDelegationArgs, OpenIdPrepareDelegationArgs, PrepareDelegationError, PreparedDelegation, }; -use junobuild_auth::openid::user::types::interface::OpenIdCredential; -use junobuild_auth::openid::user::types::provider::OpenIdDelegationProvider; +use junobuild_auth::openid::delegation::types::interface::OpenIdCredential; +use junobuild_auth::openid::delegation::types::provider::OpenIdDelegationProvider; use junobuild_auth::state::types::config::OpenIdAuthProviders; use junobuild_auth::{delegation, openid}; @@ -22,14 +22,15 @@ pub async fn openid_prepare_delegation( args: &OpenIdPrepareDelegationArgs, providers: &OpenIdAuthProviders, ) -> OpenIdPrepareDelegationResult { - let (credential, provider) = match openid::user::verify_openid_credentials_with_jwks_renewal( - &args.jwt, &args.salt, providers, &AuthHeap, - ) - .await - { - Ok(value) => value, - Err(err) => return Err(PrepareDelegationError::from(err)), - }; + let (credential, provider) = + match openid::delegation::verify_openid_credentials_with_jwks_renewal( + &args.jwt, &args.salt, providers, &AuthHeap, + ) + .await + { + Ok(value) => value, + Err(err) => return Err(PrepareDelegationError::from(err)), + }; let result = delegation::openid_prepare_delegation( &args.session_key, @@ -46,12 +47,13 @@ pub fn openid_get_delegation( args: &OpenIdGetDelegationArgs, providers: &OpenIdAuthProviders, ) -> GetDelegationResult { - let (credential, provider) = match openid::user::verify_openid_credentials_with_cached_jwks( - &args.jwt, &args.salt, providers, &AuthHeap, - ) { - Ok(value) => value, - Err(err) => return Err(GetDelegationError::from(err)), - }; + let (credential, provider) = + match openid::delegation::verify_openid_credentials_with_cached_jwks( + &args.jwt, &args.salt, providers, &AuthHeap, + ) { + Ok(value) => value, + Err(err) => return Err(GetDelegationError::from(err)), + }; delegation::openid_get_delegation( &args.session_key, diff --git a/src/libs/satellite/src/auth/register.rs b/src/libs/satellite/src/auth/register.rs index 308d577780..2e1742ec76 100644 --- a/src/libs/satellite/src/auth/register.rs +++ b/src/libs/satellite/src/auth/register.rs @@ -7,8 +7,8 @@ use crate::user::core::types::state::{OpenIdData, ProviderData, UserData}; use crate::Doc; use candid::Principal; use junobuild_auth::delegation::types::UserKey; -use junobuild_auth::openid::user::types::interface::OpenIdCredential; -use junobuild_auth::openid::user::types::provider::OpenIdDelegationProvider; +use junobuild_auth::openid::delegation::types::interface::OpenIdCredential; +use junobuild_auth::openid::delegation::types::provider::OpenIdDelegationProvider; use junobuild_collections::constants::db::COLLECTION_USER_KEY; use junobuild_collections::msg::msg_db_collection_not_found; use junobuild_shared::ic::api::id; diff --git a/src/libs/satellite/src/controllers/authenticate.rs b/src/libs/satellite/src/controllers/authenticate.rs index 1c70614da9..a182106a73 100644 --- a/src/libs/satellite/src/controllers/authenticate.rs +++ b/src/libs/satellite/src/controllers/authenticate.rs @@ -16,7 +16,7 @@ use std::cmp::min; pub async fn openid_authenticate_controller( args: &OpenIdAuthenticateControllerArgs, ) -> OpenIdAuthenticateControllerResult { - match openid::workload::verify_openid_credentials_with_jwks_renewal( + match openid::automation::verify_openid_credentials_with_jwks_renewal( &args.jwt, &OpenIdProvider::GitHubActions, &AuthHeap, diff --git a/src/libs/satellite/src/controllers/constants.rs b/src/libs/satellite/src/controllers/constants.rs index 5e2a474a40..7db3c782bc 100644 --- a/src/libs/satellite/src/controllers/constants.rs +++ b/src/libs/satellite/src/controllers/constants.rs @@ -3,5 +3,5 @@ const MINUTE_NS: u64 = 60 * 1_000_000_000; // 10 minutes in nanoseconds pub const DEFAULT_CONTROLLER_DURATION_NS: u64 = 10 * MINUTE_NS; -// The maximum duration for a workload controller +// The maximum duration for a automation controller pub const MAX_CONTROLLER_DURATION_NS: u64 = 60 * MINUTE_NS; diff --git a/src/libs/satellite/src/controllers/types.rs b/src/libs/satellite/src/controllers/types.rs index a5877b710f..59b069cb53 100644 --- a/src/libs/satellite/src/controllers/types.rs +++ b/src/libs/satellite/src/controllers/types.rs @@ -1,5 +1,5 @@ use candid::{CandidType, Deserialize}; -use junobuild_auth::openid::workload::types::errors::VerifyOpenidWorkloadCredentialsError; +use junobuild_auth::openid::automation::types::errors::VerifyOpenidAutomationCredentialsError; use junobuild_shared::types::state::{ControllerId, Metadata}; use serde::Serialize; @@ -25,7 +25,7 @@ pub enum GrantableScope { #[derive(CandidType, Serialize, Deserialize)] pub enum AuthenticateControllerError { - VerifyOpenIdCredentials(VerifyOpenidWorkloadCredentialsError), + VerifyOpenIdCredentials(VerifyOpenidAutomationCredentialsError), RegisterController(String), } diff --git a/src/libs/satellite/src/user/core/impls.rs b/src/libs/satellite/src/user/core/impls.rs index 4c9d374c56..e6c3b32c9f 100644 --- a/src/libs/satellite/src/user/core/impls.rs +++ b/src/libs/satellite/src/user/core/impls.rs @@ -9,9 +9,9 @@ use crate::user::core::types::state::{ AuthProvider, OpenIdData, ProviderData, UserData, WebAuthnData, }; use crate::{Doc, SetDoc}; +use junobuild_auth::openid::delegation::types::interface::OpenIdCredential; +use junobuild_auth::openid::delegation::types::provider::OpenIdDelegationProvider; use junobuild_auth::openid::types::provider::OpenIdProvider; -use junobuild_auth::openid::user::types::interface::OpenIdCredential; -use junobuild_auth::openid::user::types::provider::OpenIdDelegationProvider; use junobuild_auth::profile::types::{OpenIdProfile, Validated}; use junobuild_utils::encode_doc_data; From 660c09dd2731c232fa499a36eeeeb35638dfc0e2 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 28 Jan 2026 11:20:10 +0100 Subject: [PATCH 12/32] feat: expose --- src/libs/auth/src/openid/delegation/types.rs | 3 --- src/libs/satellite/src/api/controllers.rs | 4 ++-- .../satellite/src/controllers/authenticate.rs | 9 ++++----- src/libs/satellite/src/controllers/types.rs | 4 ++-- src/libs/satellite/src/impls.rs | 13 ++++++++++++- src/libs/satellite/src/lib.rs | 17 ++++++++++++----- src/libs/satellite/src/types.rs | 7 +++++++ 7 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/libs/auth/src/openid/delegation/types.rs b/src/libs/auth/src/openid/delegation/types.rs index 5af20ee0a9..8646d806e3 100644 --- a/src/libs/auth/src/openid/delegation/types.rs +++ b/src/libs/auth/src/openid/delegation/types.rs @@ -1,6 +1,3 @@ -use candid::{CandidType, Deserialize}; -use serde::Serialize; - pub mod interface { pub struct OpenIdCredentialKey<'a> { pub iss: &'a String, diff --git a/src/libs/satellite/src/api/controllers.rs b/src/libs/satellite/src/api/controllers.rs index 916acb9bbe..76a2e049c2 100644 --- a/src/libs/satellite/src/api/controllers.rs +++ b/src/libs/satellite/src/api/controllers.rs @@ -1,6 +1,6 @@ use crate::controllers::openid_authenticate_controller; use crate::controllers::store::{delete_controllers, set_controllers as set_controllers_store}; -use crate::controllers::types::{AuthenticateControllerArgs, OpenIdAuthenticateControllerResult}; +use crate::controllers::types::{AuthenticateControllerArgs, AuthenticateControllerResult}; use crate::{get_admin_controllers, get_controllers}; use ic_cdk::trap; use junobuild_shared::constants::shared::MAX_NUMBER_OF_SATELLITE_CONTROLLERS; @@ -54,7 +54,7 @@ pub fn list_controllers() -> Controllers { pub async fn authenticate_controller( args: AuthenticateControllerArgs, -) -> OpenIdAuthenticateControllerResult { +) -> AuthenticateControllerResult { match args { AuthenticateControllerArgs::OpenId(args) => openid_authenticate_controller(&args).await, } diff --git a/src/libs/satellite/src/controllers/authenticate.rs b/src/libs/satellite/src/controllers/authenticate.rs index a182106a73..0b4e8e8af1 100644 --- a/src/libs/satellite/src/controllers/authenticate.rs +++ b/src/libs/satellite/src/controllers/authenticate.rs @@ -2,8 +2,7 @@ use crate::auth::strategy_impls::AuthHeap; use crate::controllers::constants::{DEFAULT_CONTROLLER_DURATION_NS, MAX_CONTROLLER_DURATION_NS}; use crate::controllers::store::set_controllers; use crate::controllers::types::{ - AuthenticateControllerError, OpenIdAuthenticateControllerArgs, - OpenIdAuthenticateControllerResult, + AuthenticateControllerResult, AuthenticationControllerError, OpenIdAuthenticateControllerArgs, }; use ic_cdk::api::time; use junobuild_auth::openid; @@ -15,7 +14,7 @@ use std::cmp::min; pub async fn openid_authenticate_controller( args: &OpenIdAuthenticateControllerArgs, -) -> OpenIdAuthenticateControllerResult { +) -> AuthenticateControllerResult { match openid::automation::verify_openid_credentials_with_jwks_renewal( &args.jwt, &OpenIdProvider::GitHubActions, @@ -24,8 +23,8 @@ pub async fn openid_authenticate_controller( .await { Ok(_) => register_controller(args) - .map_err(|err| AuthenticateControllerError::RegisterController(err.to_string())), - Err(err) => Err(AuthenticateControllerError::VerifyOpenIdCredentials(err)), + .map_err(|err| AuthenticationControllerError::RegisterController(err.to_string())), + Err(err) => Err(AuthenticationControllerError::VerifyOpenIdCredentials(err)), } } diff --git a/src/libs/satellite/src/controllers/types.rs b/src/libs/satellite/src/controllers/types.rs index 59b069cb53..65805bf8c8 100644 --- a/src/libs/satellite/src/controllers/types.rs +++ b/src/libs/satellite/src/controllers/types.rs @@ -24,9 +24,9 @@ pub enum GrantableScope { } #[derive(CandidType, Serialize, Deserialize)] -pub enum AuthenticateControllerError { +pub enum AuthenticationControllerError { VerifyOpenIdCredentials(VerifyOpenidAutomationCredentialsError), RegisterController(String), } -pub type OpenIdAuthenticateControllerResult = Result<(), AuthenticateControllerError>; +pub type AuthenticateControllerResult = Result<(), AuthenticationControllerError>; diff --git a/src/libs/satellite/src/impls.rs b/src/libs/satellite/src/impls.rs index 0b31f105fd..c66f4fd33a 100644 --- a/src/libs/satellite/src/impls.rs +++ b/src/libs/satellite/src/impls.rs @@ -1,6 +1,8 @@ +use crate::controllers::types::AuthenticateControllerResult; use crate::memory::internal::init_stable_state; use crate::types::interface::{ - AuthenticateResultResponse, AuthenticationResult, GetDelegationResultResponse, + AuthenticateControllerResultResponse, AuthenticateResultResponse, AuthenticationResult, + GetDelegationResultResponse, }; use crate::types::state::{CollectionType, HeapState, RuntimeState, State}; use junobuild_auth::delegation::types::{GetDelegationError, SignedDelegation}; @@ -46,3 +48,12 @@ impl From for AuthenticateResultResponse { } } } + +impl From for AuthenticateControllerResultResponse { + fn from(r: AuthenticateControllerResult) -> Self { + match r { + Ok(v) => Self::Ok(v), + Err(e) => Self::Err(e), + } + } +} diff --git a/src/libs/satellite/src/lib.rs b/src/libs/satellite/src/lib.rs index d4ecbc8d3a..5afd708209 100644 --- a/src/libs/satellite/src/lib.rs +++ b/src/libs/satellite/src/lib.rs @@ -23,8 +23,8 @@ use crate::guards::{ caller_is_admin_controller, caller_is_controller, caller_is_controller_with_write, }; use crate::types::interface::{ - AuthenticateResultResponse, AuthenticationArgs, Config, DeleteProposalAssets, - GetDelegationArgs, GetDelegationResultResponse, + AuthenticateControllerResultResponse, AuthenticateResultResponse, AuthenticationArgs, Config, + DeleteProposalAssets, GetDelegationArgs, GetDelegationResultResponse, }; use crate::types::state::CollectionType; use ic_cdk_macros::{init, post_upgrade, pre_upgrade, query, update}; @@ -63,11 +63,11 @@ use memory::lifecycle; // ============================================================================================ // These types are made available for use in Serverless Functions. // ============================================================================================ +use crate::controllers::types::AuthenticateControllerArgs; use crate::db::types::interface::SetDbConfig; use junobuild_auth::state::types::interface::SetAuthenticationConfig; pub use sdk::core::*; pub use sdk::internal; - // --------------------------------------------------------- // Init and Upgrade // --------------------------------------------------------- @@ -232,6 +232,13 @@ pub fn list_controllers() -> Controllers { api::controllers::list_controllers() } +#[doc(hidden)] +pub async fn authenticate_controller( + args: AuthenticateControllerArgs, +) -> AuthenticateControllerResultResponse { + api::controllers::authenticate_controller(args).await.into() +} + // --------------------------------------------------------- // Proposal // --------------------------------------------------------- @@ -555,7 +562,7 @@ macro_rules! include_satellite { set_storage_config, submit_proposal, switch_storage_system_memory, upload_asset_chunk, upload_proposal_asset_chunk, }; - - ic_cdk::export_candid!(); }; } + +ic_cdk::export_candid!(); diff --git a/src/libs/satellite/src/types.rs b/src/libs/satellite/src/types.rs index cfcc94b79e..edc724250b 100644 --- a/src/libs/satellite/src/types.rs +++ b/src/libs/satellite/src/types.rs @@ -56,6 +56,7 @@ pub mod state { } pub mod interface { + use crate::controllers::types::AuthenticationControllerError; use crate::db::types::config::DbConfig; use crate::Doc; use candid::CandidType; @@ -118,6 +119,12 @@ pub mod interface { Ok(SignedDelegation), Err(GetDelegationError), } + + #[derive(CandidType, Serialize, Deserialize)] + pub enum AuthenticateControllerResultResponse { + Ok(()), + Err(AuthenticationControllerError), + } } pub mod store { From 8b8a74bf1bbfb9d69c42e724a70d825ba8b1382e Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 28 Jan 2026 11:21:29 +0100 Subject: [PATCH 13/32] chore: redo include --- src/libs/satellite/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/satellite/src/lib.rs b/src/libs/satellite/src/lib.rs index 5afd708209..c57fcabe29 100644 --- a/src/libs/satellite/src/lib.rs +++ b/src/libs/satellite/src/lib.rs @@ -562,7 +562,7 @@ macro_rules! include_satellite { set_storage_config, submit_proposal, switch_storage_system_memory, upload_asset_chunk, upload_proposal_asset_chunk, }; + + ic_cdk::export_candid!(); }; } - -ic_cdk::export_candid!(); From 4706433ba7e98629eb7d5dd5deb4694033da4514 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 28 Jan 2026 11:36:41 +0100 Subject: [PATCH 14/32] feat: generate did --- src/console/console.did | 19 ++++---- src/declarations/console/console.did.d.ts | 19 ++++---- .../console/console.factory.certified.did.js | 17 +++---- .../console/console.factory.did.js | 17 +++---- .../console/console.factory.did.mjs | 17 +++---- src/declarations/satellite/satellite.did.d.ts | 43 +++++++++++++---- .../satellite.factory.certified.did.js | 47 +++++++++++++++---- .../satellite/satellite.factory.did.js | 47 +++++++++++++++---- .../satellite/satellite.factory.did.mjs | 47 +++++++++++++++---- src/declarations/sputnik/sputnik.did.d.ts | 43 +++++++++++++---- .../sputnik/sputnik.factory.certified.did.js | 47 +++++++++++++++---- .../sputnik/sputnik.factory.did.js | 47 +++++++++++++++---- src/libs/satellite/satellite.did | 43 +++++++++++++---- src/libs/satellite/src/controllers/impls.rs | 10 ++-- src/libs/satellite/src/controllers/types.rs | 4 +- src/libs/satellite/src/lib.rs | 1 + src/libs/satellite/src/user/core/impls.rs | 2 +- src/satellite/satellite.did | 43 +++++++++++++---- src/sputnik/sputnik.did | 43 +++++++++++++---- .../test_satellite/test_satellite.did.d.ts | 43 +++++++++++++---- .../test_satellite.factory.certified.did.js | 47 +++++++++++++++---- .../test_satellite.factory.did.js | 47 +++++++++++++++---- .../test_satellite/test_satellite.did | 43 +++++++++++++---- 23 files changed, 563 insertions(+), 173 deletions(-) diff --git a/src/console/console.did b/src/console/console.did index b43c903d61..5ea54ba7b9 100644 --- a/src/console/console.did +++ b/src/console/console.did @@ -52,7 +52,7 @@ type AuthenticationConfigInternetIdentity = record { }; type AuthenticationConfigOpenId = record { observatory_id : opt principal; - providers : vec record { OpenIdProvider; OpenIdProviderConfig }; + providers : vec record { OpenIdDelegationProvider; OpenIdProviderAuthConfig }; }; type AuthenticationError = variant { PrepareDelegation : PrepareDelegationError; @@ -223,8 +223,11 @@ type ListSegmentsArgs = record { segment_kind : opt StorableSegmentKind; }; type Memory = variant { Heap; Stable }; -type OpenId = record { provider : OpenIdAuthProvider; data : OpenIdData }; -type OpenIdAuthProvider = variant { GitHub; Google }; +type OpenId = record { provider : OpenIdDelegationProvider; data : OpenIdData }; +type OpenIdAuthProviderDelegationConfig = record { + targets : opt vec principal; + max_time_to_live : opt nat64; +}; type OpenIdData = record { name : opt text; locale : opt text; @@ -234,6 +237,7 @@ type OpenIdData = record { given_name : opt text; preferred_username : opt text; }; +type OpenIdDelegationProvider = variant { GitHub; Google }; type OpenIdGetDelegationArgs = record { jwt : text; session_key : blob; @@ -245,15 +249,10 @@ type OpenIdPrepareDelegationArgs = record { session_key : blob; salt : blob; }; -type OpenIdProvider = variant { GitHubActions; Google; GitHubProxy }; -type OpenIdProviderConfig = record { - delegation : opt OpenIdProviderDelegationConfig; +type OpenIdProviderAuthConfig = record { + delegation : opt OpenIdAuthProviderDelegationConfig; client_id : text; }; -type OpenIdProviderDelegationConfig = record { - targets : opt vec principal; - max_time_to_live : opt nat64; -}; type PaymentStatus = variant { Refunded; Acknowledged; Completed }; type PrepareDelegationError = variant { JwtFindProvider : JwtFindProviderError; diff --git a/src/declarations/console/console.did.d.ts b/src/declarations/console/console.did.d.ts index 88cdde866c..9a63b818c6 100644 --- a/src/declarations/console/console.did.d.ts +++ b/src/declarations/console/console.did.d.ts @@ -69,7 +69,7 @@ export interface AuthenticationConfigInternetIdentity { } export interface AuthenticationConfigOpenId { observatory_id: [] | [Principal]; - providers: Array<[OpenIdProvider, OpenIdProviderConfig]>; + providers: Array<[OpenIdDelegationProvider, OpenIdProviderAuthConfig]>; } export type AuthenticationError = | { @@ -277,10 +277,13 @@ export interface ListSegmentsArgs { } export type Memory = { Heap: null } | { Stable: null }; export interface OpenId { - provider: OpenIdAuthProvider; + provider: OpenIdDelegationProvider; data: OpenIdData; } -export type OpenIdAuthProvider = { GitHub: null } | { Google: null }; +export interface OpenIdAuthProviderDelegationConfig { + targets: [] | [Array]; + max_time_to_live: [] | [bigint]; +} export interface OpenIdData { name: [] | [string]; locale: [] | [string]; @@ -290,6 +293,7 @@ export interface OpenIdData { given_name: [] | [string]; preferred_username: [] | [string]; } +export type OpenIdDelegationProvider = { GitHub: null } | { Google: null }; export interface OpenIdGetDelegationArgs { jwt: string; session_key: Uint8Array; @@ -301,15 +305,10 @@ export interface OpenIdPrepareDelegationArgs { session_key: Uint8Array; salt: Uint8Array; } -export type OpenIdProvider = { GitHubActions: null } | { Google: null } | { GitHubProxy: null }; -export interface OpenIdProviderConfig { - delegation: [] | [OpenIdProviderDelegationConfig]; +export interface OpenIdProviderAuthConfig { + delegation: [] | [OpenIdAuthProviderDelegationConfig]; client_id: string; } -export interface OpenIdProviderDelegationConfig { - targets: [] | [Array]; - max_time_to_live: [] | [bigint]; -} export type PaymentStatus = { Refunded: null } | { Acknowledged: null } | { Completed: null }; export type PrepareDelegationError = | { diff --git a/src/declarations/console/console.factory.certified.did.js b/src/declarations/console/console.factory.certified.did.js index 3b28c5d410..f2f72f1182 100644 --- a/src/declarations/console/console.factory.certified.did.js +++ b/src/declarations/console/console.factory.certified.did.js @@ -24,7 +24,7 @@ export const idlFactory = ({ IDL }) => { user_key: IDL.Vec(IDL.Nat8), expiration: IDL.Nat64 }); - const OpenIdAuthProvider = IDL.Variant({ + const OpenIdDelegationProvider = IDL.Variant({ GitHub: IDL.Null, Google: IDL.Null }); @@ -38,7 +38,7 @@ export const idlFactory = ({ IDL }) => { preferred_username: IDL.Opt(IDL.Text) }); const OpenId = IDL.Record({ - provider: OpenIdAuthProvider, + provider: OpenIdDelegationProvider, data: OpenIdData }); const Provider = IDL.Variant({ @@ -133,22 +133,17 @@ export const idlFactory = ({ IDL }) => { const DeleteProposalAssets = IDL.Record({ proposal_ids: IDL.Vec(IDL.Nat) }); - const OpenIdProvider = IDL.Variant({ - GitHubActions: IDL.Null, - Google: IDL.Null, - GitHubProxy: IDL.Null - }); - const OpenIdProviderDelegationConfig = IDL.Record({ + const OpenIdAuthProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), max_time_to_live: IDL.Opt(IDL.Nat64) }); - const OpenIdProviderConfig = IDL.Record({ - delegation: IDL.Opt(OpenIdProviderDelegationConfig), + const OpenIdProviderAuthConfig = IDL.Record({ + delegation: IDL.Opt(OpenIdAuthProviderDelegationConfig), client_id: IDL.Text }); const AuthenticationConfigOpenId = IDL.Record({ observatory_id: IDL.Opt(IDL.Principal), - providers: IDL.Vec(IDL.Tuple(OpenIdProvider, OpenIdProviderConfig)) + providers: IDL.Vec(IDL.Tuple(OpenIdDelegationProvider, OpenIdProviderAuthConfig)) }); const AuthenticationConfigInternetIdentity = IDL.Record({ derivation_origin: IDL.Opt(IDL.Text), diff --git a/src/declarations/console/console.factory.did.js b/src/declarations/console/console.factory.did.js index 7c2917ef44..1fe980de44 100644 --- a/src/declarations/console/console.factory.did.js +++ b/src/declarations/console/console.factory.did.js @@ -24,7 +24,7 @@ export const idlFactory = ({ IDL }) => { user_key: IDL.Vec(IDL.Nat8), expiration: IDL.Nat64 }); - const OpenIdAuthProvider = IDL.Variant({ + const OpenIdDelegationProvider = IDL.Variant({ GitHub: IDL.Null, Google: IDL.Null }); @@ -38,7 +38,7 @@ export const idlFactory = ({ IDL }) => { preferred_username: IDL.Opt(IDL.Text) }); const OpenId = IDL.Record({ - provider: OpenIdAuthProvider, + provider: OpenIdDelegationProvider, data: OpenIdData }); const Provider = IDL.Variant({ @@ -133,22 +133,17 @@ export const idlFactory = ({ IDL }) => { const DeleteProposalAssets = IDL.Record({ proposal_ids: IDL.Vec(IDL.Nat) }); - const OpenIdProvider = IDL.Variant({ - GitHubActions: IDL.Null, - Google: IDL.Null, - GitHubProxy: IDL.Null - }); - const OpenIdProviderDelegationConfig = IDL.Record({ + const OpenIdAuthProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), max_time_to_live: IDL.Opt(IDL.Nat64) }); - const OpenIdProviderConfig = IDL.Record({ - delegation: IDL.Opt(OpenIdProviderDelegationConfig), + const OpenIdProviderAuthConfig = IDL.Record({ + delegation: IDL.Opt(OpenIdAuthProviderDelegationConfig), client_id: IDL.Text }); const AuthenticationConfigOpenId = IDL.Record({ observatory_id: IDL.Opt(IDL.Principal), - providers: IDL.Vec(IDL.Tuple(OpenIdProvider, OpenIdProviderConfig)) + providers: IDL.Vec(IDL.Tuple(OpenIdDelegationProvider, OpenIdProviderAuthConfig)) }); const AuthenticationConfigInternetIdentity = IDL.Record({ derivation_origin: IDL.Opt(IDL.Text), diff --git a/src/declarations/console/console.factory.did.mjs b/src/declarations/console/console.factory.did.mjs index 7c2917ef44..1fe980de44 100644 --- a/src/declarations/console/console.factory.did.mjs +++ b/src/declarations/console/console.factory.did.mjs @@ -24,7 +24,7 @@ export const idlFactory = ({ IDL }) => { user_key: IDL.Vec(IDL.Nat8), expiration: IDL.Nat64 }); - const OpenIdAuthProvider = IDL.Variant({ + const OpenIdDelegationProvider = IDL.Variant({ GitHub: IDL.Null, Google: IDL.Null }); @@ -38,7 +38,7 @@ export const idlFactory = ({ IDL }) => { preferred_username: IDL.Opt(IDL.Text) }); const OpenId = IDL.Record({ - provider: OpenIdAuthProvider, + provider: OpenIdDelegationProvider, data: OpenIdData }); const Provider = IDL.Variant({ @@ -133,22 +133,17 @@ export const idlFactory = ({ IDL }) => { const DeleteProposalAssets = IDL.Record({ proposal_ids: IDL.Vec(IDL.Nat) }); - const OpenIdProvider = IDL.Variant({ - GitHubActions: IDL.Null, - Google: IDL.Null, - GitHubProxy: IDL.Null - }); - const OpenIdProviderDelegationConfig = IDL.Record({ + const OpenIdAuthProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), max_time_to_live: IDL.Opt(IDL.Nat64) }); - const OpenIdProviderConfig = IDL.Record({ - delegation: IDL.Opt(OpenIdProviderDelegationConfig), + const OpenIdProviderAuthConfig = IDL.Record({ + delegation: IDL.Opt(OpenIdAuthProviderDelegationConfig), client_id: IDL.Text }); const AuthenticationConfigOpenId = IDL.Record({ observatory_id: IDL.Opt(IDL.Principal), - providers: IDL.Vec(IDL.Tuple(OpenIdProvider, OpenIdProviderConfig)) + providers: IDL.Vec(IDL.Tuple(OpenIdDelegationProvider, OpenIdProviderAuthConfig)) }); const AuthenticationConfigInternetIdentity = IDL.Record({ derivation_origin: IDL.Opt(IDL.Text), diff --git a/src/declarations/satellite/satellite.did.d.ts b/src/declarations/satellite/satellite.did.d.ts index a51b94415b..db58076314 100644 --- a/src/declarations/satellite/satellite.did.d.ts +++ b/src/declarations/satellite/satellite.did.d.ts @@ -34,6 +34,12 @@ export interface AssetNoContent { export interface AssetsUpgradeOptions { clear_existing_assets: [] | [boolean]; } +export type AuthenticateControllerArgs = { + OpenId: OpenIdAuthenticateControllerArgs; +}; +export type AuthenticateControllerResultResponse = + | { Ok: null } + | { Err: AuthenticationControllerError }; export type AuthenticateResultResponse = { Ok: Authentication } | { Err: AuthenticationError }; export interface Authentication { doc: Doc; @@ -54,8 +60,11 @@ export interface AuthenticationConfigInternetIdentity { } export interface AuthenticationConfigOpenId { observatory_id: [] | [Principal]; - providers: Array<[OpenIdProvider, OpenIdProviderConfig]>; + providers: Array<[OpenIdDelegationProvider, OpenIdProviderAuthConfig]>; } +export type AuthenticationControllerError = + | { RegisterController: string } + | { VerifyOpenIdCredentials: VerifyOpenidAutomationCredentialsError }; export type AuthenticationError = | { PrepareDelegation: PrepareDelegationError; @@ -64,6 +73,7 @@ export type AuthenticationError = export interface AuthenticationRules { allowed_callers: Array; } +export type AutomationScope = { Write: null } | { Submit: null }; export type CollectionType = { Db: null } | { Storage: null }; export interface CommitBatch { batch_id: bigint; @@ -259,6 +269,18 @@ export interface MemorySize { stable: bigint; heap: bigint; } +export interface OpenIdAuthProviderDelegationConfig { + targets: [] | [Array]; + max_time_to_live: [] | [bigint]; +} +export interface OpenIdAuthenticateControllerArgs { + jwt: string; + metadata: Array<[string, string]>; + scope: AutomationScope; + max_time_to_live: [] | [bigint]; + controller_id: Principal; +} +export type OpenIdDelegationProvider = { GitHub: null } | { Google: null }; export interface OpenIdGetDelegationArgs { jwt: string; session_key: Uint8Array; @@ -270,15 +292,10 @@ export interface OpenIdPrepareDelegationArgs { session_key: Uint8Array; salt: Uint8Array; } -export type OpenIdProvider = { GitHubActions: null } | { Google: null } | { GitHubProxy: null }; -export interface OpenIdProviderConfig { - delegation: [] | [OpenIdProviderDelegationConfig]; +export interface OpenIdProviderAuthConfig { + delegation: [] | [OpenIdAuthProviderDelegationConfig]; client_id: string; } -export interface OpenIdProviderDelegationConfig { - targets: [] | [Array]; - max_time_to_live: [] | [bigint]; -} export type Permission = | { Controllers: null } | { Private: null } @@ -438,8 +455,18 @@ export interface UploadChunk { export interface UploadChunkResult { chunk_id: bigint; } +export type VerifyOpenidAutomationCredentialsError = + | { + GetCachedJwks: null; + } + | { JwtVerify: JwtVerifyError } + | { GetOrFetchJwks: GetOrRefreshJwksError }; export interface _SERVICE { authenticate: ActorMethod<[AuthenticationArgs], AuthenticateResultResponse>; + authenticate_controller: ActorMethod< + [AuthenticateControllerArgs], + AuthenticateControllerResultResponse + >; commit_asset_upload: ActorMethod<[CommitBatch], undefined>; commit_proposal: ActorMethod<[CommitProposal], null>; commit_proposal_asset_upload: ActorMethod<[CommitBatch], undefined>; diff --git a/src/declarations/satellite/satellite.factory.certified.did.js b/src/declarations/satellite/satellite.factory.certified.did.js index 00743eabe6..c43568c316 100644 --- a/src/declarations/satellite/satellite.factory.certified.did.js +++ b/src/declarations/satellite/satellite.factory.certified.did.js @@ -75,6 +75,33 @@ export const idlFactory = ({ IDL }) => { Ok: Authentication, Err: AuthenticationError }); + const AutomationScope = IDL.Variant({ + Write: IDL.Null, + Submit: IDL.Null + }); + const OpenIdAuthenticateControllerArgs = IDL.Record({ + jwt: IDL.Text, + metadata: IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text)), + scope: AutomationScope, + max_time_to_live: IDL.Opt(IDL.Nat64), + controller_id: IDL.Principal + }); + const AuthenticateControllerArgs = IDL.Variant({ + OpenId: OpenIdAuthenticateControllerArgs + }); + const VerifyOpenidAutomationCredentialsError = IDL.Variant({ + GetCachedJwks: IDL.Null, + JwtVerify: JwtVerifyError, + GetOrFetchJwks: GetOrRefreshJwksError + }); + const AuthenticationControllerError = IDL.Variant({ + RegisterController: IDL.Text, + VerifyOpenIdCredentials: VerifyOpenidAutomationCredentialsError + }); + const AuthenticateControllerResultResponse = IDL.Variant({ + Ok: IDL.Null, + Err: AuthenticationControllerError + }); const CommitBatch = IDL.Record({ batch_id: IDL.Nat, headers: IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text)), @@ -158,22 +185,21 @@ export const idlFactory = ({ IDL }) => { created_at: IDL.Nat64, version: IDL.Opt(IDL.Nat64) }); - const OpenIdProvider = IDL.Variant({ - GitHubActions: IDL.Null, - Google: IDL.Null, - GitHubProxy: IDL.Null + const OpenIdDelegationProvider = IDL.Variant({ + GitHub: IDL.Null, + Google: IDL.Null }); - const OpenIdProviderDelegationConfig = IDL.Record({ + const OpenIdAuthProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), max_time_to_live: IDL.Opt(IDL.Nat64) }); - const OpenIdProviderConfig = IDL.Record({ - delegation: IDL.Opt(OpenIdProviderDelegationConfig), + const OpenIdProviderAuthConfig = IDL.Record({ + delegation: IDL.Opt(OpenIdAuthProviderDelegationConfig), client_id: IDL.Text }); const AuthenticationConfigOpenId = IDL.Record({ observatory_id: IDL.Opt(IDL.Principal), - providers: IDL.Vec(IDL.Tuple(OpenIdProvider, OpenIdProviderConfig)) + providers: IDL.Vec(IDL.Tuple(OpenIdDelegationProvider, OpenIdProviderAuthConfig)) }); const AuthenticationConfigInternetIdentity = IDL.Record({ derivation_origin: IDL.Opt(IDL.Text), @@ -447,6 +473,11 @@ export const idlFactory = ({ IDL }) => { return IDL.Service({ authenticate: IDL.Func([AuthenticationArgs], [AuthenticateResultResponse], []), + authenticate_controller: IDL.Func( + [AuthenticateControllerArgs], + [AuthenticateControllerResultResponse], + [] + ), commit_asset_upload: IDL.Func([CommitBatch], [], []), commit_proposal: IDL.Func([CommitProposal], [IDL.Null], []), commit_proposal_asset_upload: IDL.Func([CommitBatch], [], []), diff --git a/src/declarations/satellite/satellite.factory.did.js b/src/declarations/satellite/satellite.factory.did.js index 0161a96157..62bff55282 100644 --- a/src/declarations/satellite/satellite.factory.did.js +++ b/src/declarations/satellite/satellite.factory.did.js @@ -75,6 +75,33 @@ export const idlFactory = ({ IDL }) => { Ok: Authentication, Err: AuthenticationError }); + const AutomationScope = IDL.Variant({ + Write: IDL.Null, + Submit: IDL.Null + }); + const OpenIdAuthenticateControllerArgs = IDL.Record({ + jwt: IDL.Text, + metadata: IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text)), + scope: AutomationScope, + max_time_to_live: IDL.Opt(IDL.Nat64), + controller_id: IDL.Principal + }); + const AuthenticateControllerArgs = IDL.Variant({ + OpenId: OpenIdAuthenticateControllerArgs + }); + const VerifyOpenidAutomationCredentialsError = IDL.Variant({ + GetCachedJwks: IDL.Null, + JwtVerify: JwtVerifyError, + GetOrFetchJwks: GetOrRefreshJwksError + }); + const AuthenticationControllerError = IDL.Variant({ + RegisterController: IDL.Text, + VerifyOpenIdCredentials: VerifyOpenidAutomationCredentialsError + }); + const AuthenticateControllerResultResponse = IDL.Variant({ + Ok: IDL.Null, + Err: AuthenticationControllerError + }); const CommitBatch = IDL.Record({ batch_id: IDL.Nat, headers: IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text)), @@ -158,22 +185,21 @@ export const idlFactory = ({ IDL }) => { created_at: IDL.Nat64, version: IDL.Opt(IDL.Nat64) }); - const OpenIdProvider = IDL.Variant({ - GitHubActions: IDL.Null, - Google: IDL.Null, - GitHubProxy: IDL.Null + const OpenIdDelegationProvider = IDL.Variant({ + GitHub: IDL.Null, + Google: IDL.Null }); - const OpenIdProviderDelegationConfig = IDL.Record({ + const OpenIdAuthProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), max_time_to_live: IDL.Opt(IDL.Nat64) }); - const OpenIdProviderConfig = IDL.Record({ - delegation: IDL.Opt(OpenIdProviderDelegationConfig), + const OpenIdProviderAuthConfig = IDL.Record({ + delegation: IDL.Opt(OpenIdAuthProviderDelegationConfig), client_id: IDL.Text }); const AuthenticationConfigOpenId = IDL.Record({ observatory_id: IDL.Opt(IDL.Principal), - providers: IDL.Vec(IDL.Tuple(OpenIdProvider, OpenIdProviderConfig)) + providers: IDL.Vec(IDL.Tuple(OpenIdDelegationProvider, OpenIdProviderAuthConfig)) }); const AuthenticationConfigInternetIdentity = IDL.Record({ derivation_origin: IDL.Opt(IDL.Text), @@ -447,6 +473,11 @@ export const idlFactory = ({ IDL }) => { return IDL.Service({ authenticate: IDL.Func([AuthenticationArgs], [AuthenticateResultResponse], []), + authenticate_controller: IDL.Func( + [AuthenticateControllerArgs], + [AuthenticateControllerResultResponse], + [] + ), commit_asset_upload: IDL.Func([CommitBatch], [], []), commit_proposal: IDL.Func([CommitProposal], [IDL.Null], []), commit_proposal_asset_upload: IDL.Func([CommitBatch], [], []), diff --git a/src/declarations/satellite/satellite.factory.did.mjs b/src/declarations/satellite/satellite.factory.did.mjs index 0161a96157..62bff55282 100644 --- a/src/declarations/satellite/satellite.factory.did.mjs +++ b/src/declarations/satellite/satellite.factory.did.mjs @@ -75,6 +75,33 @@ export const idlFactory = ({ IDL }) => { Ok: Authentication, Err: AuthenticationError }); + const AutomationScope = IDL.Variant({ + Write: IDL.Null, + Submit: IDL.Null + }); + const OpenIdAuthenticateControllerArgs = IDL.Record({ + jwt: IDL.Text, + metadata: IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text)), + scope: AutomationScope, + max_time_to_live: IDL.Opt(IDL.Nat64), + controller_id: IDL.Principal + }); + const AuthenticateControllerArgs = IDL.Variant({ + OpenId: OpenIdAuthenticateControllerArgs + }); + const VerifyOpenidAutomationCredentialsError = IDL.Variant({ + GetCachedJwks: IDL.Null, + JwtVerify: JwtVerifyError, + GetOrFetchJwks: GetOrRefreshJwksError + }); + const AuthenticationControllerError = IDL.Variant({ + RegisterController: IDL.Text, + VerifyOpenIdCredentials: VerifyOpenidAutomationCredentialsError + }); + const AuthenticateControllerResultResponse = IDL.Variant({ + Ok: IDL.Null, + Err: AuthenticationControllerError + }); const CommitBatch = IDL.Record({ batch_id: IDL.Nat, headers: IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text)), @@ -158,22 +185,21 @@ export const idlFactory = ({ IDL }) => { created_at: IDL.Nat64, version: IDL.Opt(IDL.Nat64) }); - const OpenIdProvider = IDL.Variant({ - GitHubActions: IDL.Null, - Google: IDL.Null, - GitHubProxy: IDL.Null + const OpenIdDelegationProvider = IDL.Variant({ + GitHub: IDL.Null, + Google: IDL.Null }); - const OpenIdProviderDelegationConfig = IDL.Record({ + const OpenIdAuthProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), max_time_to_live: IDL.Opt(IDL.Nat64) }); - const OpenIdProviderConfig = IDL.Record({ - delegation: IDL.Opt(OpenIdProviderDelegationConfig), + const OpenIdProviderAuthConfig = IDL.Record({ + delegation: IDL.Opt(OpenIdAuthProviderDelegationConfig), client_id: IDL.Text }); const AuthenticationConfigOpenId = IDL.Record({ observatory_id: IDL.Opt(IDL.Principal), - providers: IDL.Vec(IDL.Tuple(OpenIdProvider, OpenIdProviderConfig)) + providers: IDL.Vec(IDL.Tuple(OpenIdDelegationProvider, OpenIdProviderAuthConfig)) }); const AuthenticationConfigInternetIdentity = IDL.Record({ derivation_origin: IDL.Opt(IDL.Text), @@ -447,6 +473,11 @@ export const idlFactory = ({ IDL }) => { return IDL.Service({ authenticate: IDL.Func([AuthenticationArgs], [AuthenticateResultResponse], []), + authenticate_controller: IDL.Func( + [AuthenticateControllerArgs], + [AuthenticateControllerResultResponse], + [] + ), commit_asset_upload: IDL.Func([CommitBatch], [], []), commit_proposal: IDL.Func([CommitProposal], [IDL.Null], []), commit_proposal_asset_upload: IDL.Func([CommitBatch], [], []), diff --git a/src/declarations/sputnik/sputnik.did.d.ts b/src/declarations/sputnik/sputnik.did.d.ts index a51b94415b..db58076314 100644 --- a/src/declarations/sputnik/sputnik.did.d.ts +++ b/src/declarations/sputnik/sputnik.did.d.ts @@ -34,6 +34,12 @@ export interface AssetNoContent { export interface AssetsUpgradeOptions { clear_existing_assets: [] | [boolean]; } +export type AuthenticateControllerArgs = { + OpenId: OpenIdAuthenticateControllerArgs; +}; +export type AuthenticateControllerResultResponse = + | { Ok: null } + | { Err: AuthenticationControllerError }; export type AuthenticateResultResponse = { Ok: Authentication } | { Err: AuthenticationError }; export interface Authentication { doc: Doc; @@ -54,8 +60,11 @@ export interface AuthenticationConfigInternetIdentity { } export interface AuthenticationConfigOpenId { observatory_id: [] | [Principal]; - providers: Array<[OpenIdProvider, OpenIdProviderConfig]>; + providers: Array<[OpenIdDelegationProvider, OpenIdProviderAuthConfig]>; } +export type AuthenticationControllerError = + | { RegisterController: string } + | { VerifyOpenIdCredentials: VerifyOpenidAutomationCredentialsError }; export type AuthenticationError = | { PrepareDelegation: PrepareDelegationError; @@ -64,6 +73,7 @@ export type AuthenticationError = export interface AuthenticationRules { allowed_callers: Array; } +export type AutomationScope = { Write: null } | { Submit: null }; export type CollectionType = { Db: null } | { Storage: null }; export interface CommitBatch { batch_id: bigint; @@ -259,6 +269,18 @@ export interface MemorySize { stable: bigint; heap: bigint; } +export interface OpenIdAuthProviderDelegationConfig { + targets: [] | [Array]; + max_time_to_live: [] | [bigint]; +} +export interface OpenIdAuthenticateControllerArgs { + jwt: string; + metadata: Array<[string, string]>; + scope: AutomationScope; + max_time_to_live: [] | [bigint]; + controller_id: Principal; +} +export type OpenIdDelegationProvider = { GitHub: null } | { Google: null }; export interface OpenIdGetDelegationArgs { jwt: string; session_key: Uint8Array; @@ -270,15 +292,10 @@ export interface OpenIdPrepareDelegationArgs { session_key: Uint8Array; salt: Uint8Array; } -export type OpenIdProvider = { GitHubActions: null } | { Google: null } | { GitHubProxy: null }; -export interface OpenIdProviderConfig { - delegation: [] | [OpenIdProviderDelegationConfig]; +export interface OpenIdProviderAuthConfig { + delegation: [] | [OpenIdAuthProviderDelegationConfig]; client_id: string; } -export interface OpenIdProviderDelegationConfig { - targets: [] | [Array]; - max_time_to_live: [] | [bigint]; -} export type Permission = | { Controllers: null } | { Private: null } @@ -438,8 +455,18 @@ export interface UploadChunk { export interface UploadChunkResult { chunk_id: bigint; } +export type VerifyOpenidAutomationCredentialsError = + | { + GetCachedJwks: null; + } + | { JwtVerify: JwtVerifyError } + | { GetOrFetchJwks: GetOrRefreshJwksError }; export interface _SERVICE { authenticate: ActorMethod<[AuthenticationArgs], AuthenticateResultResponse>; + authenticate_controller: ActorMethod< + [AuthenticateControllerArgs], + AuthenticateControllerResultResponse + >; commit_asset_upload: ActorMethod<[CommitBatch], undefined>; commit_proposal: ActorMethod<[CommitProposal], null>; commit_proposal_asset_upload: ActorMethod<[CommitBatch], undefined>; diff --git a/src/declarations/sputnik/sputnik.factory.certified.did.js b/src/declarations/sputnik/sputnik.factory.certified.did.js index 00743eabe6..c43568c316 100644 --- a/src/declarations/sputnik/sputnik.factory.certified.did.js +++ b/src/declarations/sputnik/sputnik.factory.certified.did.js @@ -75,6 +75,33 @@ export const idlFactory = ({ IDL }) => { Ok: Authentication, Err: AuthenticationError }); + const AutomationScope = IDL.Variant({ + Write: IDL.Null, + Submit: IDL.Null + }); + const OpenIdAuthenticateControllerArgs = IDL.Record({ + jwt: IDL.Text, + metadata: IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text)), + scope: AutomationScope, + max_time_to_live: IDL.Opt(IDL.Nat64), + controller_id: IDL.Principal + }); + const AuthenticateControllerArgs = IDL.Variant({ + OpenId: OpenIdAuthenticateControllerArgs + }); + const VerifyOpenidAutomationCredentialsError = IDL.Variant({ + GetCachedJwks: IDL.Null, + JwtVerify: JwtVerifyError, + GetOrFetchJwks: GetOrRefreshJwksError + }); + const AuthenticationControllerError = IDL.Variant({ + RegisterController: IDL.Text, + VerifyOpenIdCredentials: VerifyOpenidAutomationCredentialsError + }); + const AuthenticateControllerResultResponse = IDL.Variant({ + Ok: IDL.Null, + Err: AuthenticationControllerError + }); const CommitBatch = IDL.Record({ batch_id: IDL.Nat, headers: IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text)), @@ -158,22 +185,21 @@ export const idlFactory = ({ IDL }) => { created_at: IDL.Nat64, version: IDL.Opt(IDL.Nat64) }); - const OpenIdProvider = IDL.Variant({ - GitHubActions: IDL.Null, - Google: IDL.Null, - GitHubProxy: IDL.Null + const OpenIdDelegationProvider = IDL.Variant({ + GitHub: IDL.Null, + Google: IDL.Null }); - const OpenIdProviderDelegationConfig = IDL.Record({ + const OpenIdAuthProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), max_time_to_live: IDL.Opt(IDL.Nat64) }); - const OpenIdProviderConfig = IDL.Record({ - delegation: IDL.Opt(OpenIdProviderDelegationConfig), + const OpenIdProviderAuthConfig = IDL.Record({ + delegation: IDL.Opt(OpenIdAuthProviderDelegationConfig), client_id: IDL.Text }); const AuthenticationConfigOpenId = IDL.Record({ observatory_id: IDL.Opt(IDL.Principal), - providers: IDL.Vec(IDL.Tuple(OpenIdProvider, OpenIdProviderConfig)) + providers: IDL.Vec(IDL.Tuple(OpenIdDelegationProvider, OpenIdProviderAuthConfig)) }); const AuthenticationConfigInternetIdentity = IDL.Record({ derivation_origin: IDL.Opt(IDL.Text), @@ -447,6 +473,11 @@ export const idlFactory = ({ IDL }) => { return IDL.Service({ authenticate: IDL.Func([AuthenticationArgs], [AuthenticateResultResponse], []), + authenticate_controller: IDL.Func( + [AuthenticateControllerArgs], + [AuthenticateControllerResultResponse], + [] + ), commit_asset_upload: IDL.Func([CommitBatch], [], []), commit_proposal: IDL.Func([CommitProposal], [IDL.Null], []), commit_proposal_asset_upload: IDL.Func([CommitBatch], [], []), diff --git a/src/declarations/sputnik/sputnik.factory.did.js b/src/declarations/sputnik/sputnik.factory.did.js index 0161a96157..62bff55282 100644 --- a/src/declarations/sputnik/sputnik.factory.did.js +++ b/src/declarations/sputnik/sputnik.factory.did.js @@ -75,6 +75,33 @@ export const idlFactory = ({ IDL }) => { Ok: Authentication, Err: AuthenticationError }); + const AutomationScope = IDL.Variant({ + Write: IDL.Null, + Submit: IDL.Null + }); + const OpenIdAuthenticateControllerArgs = IDL.Record({ + jwt: IDL.Text, + metadata: IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text)), + scope: AutomationScope, + max_time_to_live: IDL.Opt(IDL.Nat64), + controller_id: IDL.Principal + }); + const AuthenticateControllerArgs = IDL.Variant({ + OpenId: OpenIdAuthenticateControllerArgs + }); + const VerifyOpenidAutomationCredentialsError = IDL.Variant({ + GetCachedJwks: IDL.Null, + JwtVerify: JwtVerifyError, + GetOrFetchJwks: GetOrRefreshJwksError + }); + const AuthenticationControllerError = IDL.Variant({ + RegisterController: IDL.Text, + VerifyOpenIdCredentials: VerifyOpenidAutomationCredentialsError + }); + const AuthenticateControllerResultResponse = IDL.Variant({ + Ok: IDL.Null, + Err: AuthenticationControllerError + }); const CommitBatch = IDL.Record({ batch_id: IDL.Nat, headers: IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text)), @@ -158,22 +185,21 @@ export const idlFactory = ({ IDL }) => { created_at: IDL.Nat64, version: IDL.Opt(IDL.Nat64) }); - const OpenIdProvider = IDL.Variant({ - GitHubActions: IDL.Null, - Google: IDL.Null, - GitHubProxy: IDL.Null + const OpenIdDelegationProvider = IDL.Variant({ + GitHub: IDL.Null, + Google: IDL.Null }); - const OpenIdProviderDelegationConfig = IDL.Record({ + const OpenIdAuthProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), max_time_to_live: IDL.Opt(IDL.Nat64) }); - const OpenIdProviderConfig = IDL.Record({ - delegation: IDL.Opt(OpenIdProviderDelegationConfig), + const OpenIdProviderAuthConfig = IDL.Record({ + delegation: IDL.Opt(OpenIdAuthProviderDelegationConfig), client_id: IDL.Text }); const AuthenticationConfigOpenId = IDL.Record({ observatory_id: IDL.Opt(IDL.Principal), - providers: IDL.Vec(IDL.Tuple(OpenIdProvider, OpenIdProviderConfig)) + providers: IDL.Vec(IDL.Tuple(OpenIdDelegationProvider, OpenIdProviderAuthConfig)) }); const AuthenticationConfigInternetIdentity = IDL.Record({ derivation_origin: IDL.Opt(IDL.Text), @@ -447,6 +473,11 @@ export const idlFactory = ({ IDL }) => { return IDL.Service({ authenticate: IDL.Func([AuthenticationArgs], [AuthenticateResultResponse], []), + authenticate_controller: IDL.Func( + [AuthenticateControllerArgs], + [AuthenticateControllerResultResponse], + [] + ), commit_asset_upload: IDL.Func([CommitBatch], [], []), commit_proposal: IDL.Func([CommitProposal], [IDL.Null], []), commit_proposal_asset_upload: IDL.Func([CommitBatch], [], []), diff --git a/src/libs/satellite/satellite.did b/src/libs/satellite/satellite.did index 363df45340..4044aee380 100644 --- a/src/libs/satellite/satellite.did +++ b/src/libs/satellite/satellite.did @@ -20,6 +20,13 @@ type AssetNoContent = record { version : opt nat64; }; type AssetsUpgradeOptions = record { clear_existing_assets : opt bool }; +type AuthenticateControllerArgs = variant { + OpenId : OpenIdAuthenticateControllerArgs; +}; +type AuthenticateControllerResultResponse = variant { + Ok; + Err : AuthenticationControllerError; +}; type AuthenticateResultResponse = variant { Ok : Authentication; Err : AuthenticationError; @@ -40,13 +47,18 @@ type AuthenticationConfigInternetIdentity = record { }; type AuthenticationConfigOpenId = record { observatory_id : opt principal; - providers : vec record { OpenIdProvider; OpenIdProviderConfig }; + providers : vec record { OpenIdDelegationProvider; OpenIdProviderAuthConfig }; +}; +type AuthenticationControllerError = variant { + RegisterController : text; + VerifyOpenIdCredentials : VerifyOpenidAutomationCredentialsError; }; type AuthenticationError = variant { PrepareDelegation : PrepareDelegationError; RegisterUser : text; }; type AuthenticationRules = record { allowed_callers : vec principal }; +type AutomationScope = variant { Write; Submit }; type CollectionType = variant { Db; Storage }; type CommitBatch = record { batch_id : nat; @@ -210,6 +222,18 @@ type ListRulesResults = record { }; type Memory = variant { Heap; Stable }; type MemorySize = record { stable : nat64; heap : nat64 }; +type OpenIdAuthProviderDelegationConfig = record { + targets : opt vec principal; + max_time_to_live : opt nat64; +}; +type OpenIdAuthenticateControllerArgs = record { + jwt : text; + metadata : vec record { text; text }; + scope : AutomationScope; + max_time_to_live : opt nat64; + controller_id : principal; +}; +type OpenIdDelegationProvider = variant { GitHub; Google }; type OpenIdGetDelegationArgs = record { jwt : text; session_key : blob; @@ -221,15 +245,10 @@ type OpenIdPrepareDelegationArgs = record { session_key : blob; salt : blob; }; -type OpenIdProvider = variant { GitHubActions; Google; GitHubProxy }; -type OpenIdProviderConfig = record { - delegation : opt OpenIdProviderDelegationConfig; +type OpenIdProviderAuthConfig = record { + delegation : opt OpenIdAuthProviderDelegationConfig; client_id : text; }; -type OpenIdProviderDelegationConfig = record { - targets : opt vec principal; - max_time_to_live : opt nat64; -}; type Permission = variant { Controllers; Private; Public; Managed }; type PrepareDelegationError = variant { JwtFindProvider : JwtFindProviderError; @@ -371,8 +390,16 @@ type UploadChunk = record { order_id : opt nat; }; type UploadChunkResult = record { chunk_id : nat }; +type VerifyOpenidAutomationCredentialsError = variant { + GetCachedJwks; + JwtVerify : JwtVerifyError; + GetOrFetchJwks : GetOrRefreshJwksError; +}; service : (InitSatelliteArgs) -> { authenticate : (AuthenticationArgs) -> (AuthenticateResultResponse); + authenticate_controller : (AuthenticateControllerArgs) -> ( + AuthenticateControllerResultResponse, + ); commit_asset_upload : (CommitBatch) -> (); commit_proposal : (CommitProposal) -> (null); commit_proposal_asset_upload : (CommitBatch) -> (); diff --git a/src/libs/satellite/src/controllers/impls.rs b/src/libs/satellite/src/controllers/impls.rs index cc82bf1169..d588dca7bc 100644 --- a/src/libs/satellite/src/controllers/impls.rs +++ b/src/libs/satellite/src/controllers/impls.rs @@ -1,11 +1,11 @@ -use crate::controllers::types::GrantableScope; +use crate::controllers::types::AutomationScope; use junobuild_shared::types::state::ControllerScope; -impl From for ControllerScope { - fn from(scope: GrantableScope) -> Self { +impl From for ControllerScope { + fn from(scope: AutomationScope) -> Self { match scope { - GrantableScope::Write => ControllerScope::Write, - GrantableScope::Submit => ControllerScope::Submit, + AutomationScope::Write => ControllerScope::Write, + AutomationScope::Submit => ControllerScope::Submit, } } } diff --git a/src/libs/satellite/src/controllers/types.rs b/src/libs/satellite/src/controllers/types.rs index 65805bf8c8..5f8c4def76 100644 --- a/src/libs/satellite/src/controllers/types.rs +++ b/src/libs/satellite/src/controllers/types.rs @@ -12,13 +12,13 @@ pub enum AuthenticateControllerArgs { pub struct OpenIdAuthenticateControllerArgs { pub jwt: String, pub controller_id: ControllerId, - pub scope: GrantableScope, + pub scope: AutomationScope, pub metadata: Metadata, pub max_time_to_live: Option, } #[derive(CandidType, Serialize, Deserialize, Clone)] -pub enum GrantableScope { +pub enum AutomationScope { Write, Submit, } diff --git a/src/libs/satellite/src/lib.rs b/src/libs/satellite/src/lib.rs index c57fcabe29..9679bc3894 100644 --- a/src/libs/satellite/src/lib.rs +++ b/src/libs/satellite/src/lib.rs @@ -233,6 +233,7 @@ pub fn list_controllers() -> Controllers { } #[doc(hidden)] +#[update] pub async fn authenticate_controller( args: AuthenticateControllerArgs, ) -> AuthenticateControllerResultResponse { diff --git a/src/libs/satellite/src/user/core/impls.rs b/src/libs/satellite/src/user/core/impls.rs index e6c3b32c9f..c427d9ea69 100644 --- a/src/libs/satellite/src/user/core/impls.rs +++ b/src/libs/satellite/src/user/core/impls.rs @@ -11,7 +11,6 @@ use crate::user::core::types::state::{ use crate::{Doc, SetDoc}; use junobuild_auth::openid::delegation::types::interface::OpenIdCredential; use junobuild_auth::openid::delegation::types::provider::OpenIdDelegationProvider; -use junobuild_auth::openid::types::provider::OpenIdProvider; use junobuild_auth::profile::types::{OpenIdProfile, Validated}; use junobuild_utils::encode_doc_data; @@ -176,6 +175,7 @@ mod tests { use crate::user::core::types::state::{ AuthProvider, OpenIdData, ProviderData, UserData, WebAuthnData, }; + use junobuild_auth::openid::types::provider::OpenIdProvider; // ------------------------ // WebAuthnData diff --git a/src/satellite/satellite.did b/src/satellite/satellite.did index 9e4fec0266..582ab2b94c 100644 --- a/src/satellite/satellite.did +++ b/src/satellite/satellite.did @@ -22,6 +22,13 @@ type AssetNoContent = record { version : opt nat64; }; type AssetsUpgradeOptions = record { clear_existing_assets : opt bool }; +type AuthenticateControllerArgs = variant { + OpenId : OpenIdAuthenticateControllerArgs; +}; +type AuthenticateControllerResultResponse = variant { + Ok; + Err : AuthenticationControllerError; +}; type AuthenticateResultResponse = variant { Ok : Authentication; Err : AuthenticationError; @@ -42,13 +49,18 @@ type AuthenticationConfigInternetIdentity = record { }; type AuthenticationConfigOpenId = record { observatory_id : opt principal; - providers : vec record { OpenIdProvider; OpenIdProviderConfig }; + providers : vec record { OpenIdDelegationProvider; OpenIdProviderAuthConfig }; +}; +type AuthenticationControllerError = variant { + RegisterController : text; + VerifyOpenIdCredentials : VerifyOpenidAutomationCredentialsError; }; type AuthenticationError = variant { PrepareDelegation : PrepareDelegationError; RegisterUser : text; }; type AuthenticationRules = record { allowed_callers : vec principal }; +type AutomationScope = variant { Write; Submit }; type CollectionType = variant { Db; Storage }; type CommitBatch = record { batch_id : nat; @@ -212,6 +224,18 @@ type ListRulesResults = record { }; type Memory = variant { Heap; Stable }; type MemorySize = record { stable : nat64; heap : nat64 }; +type OpenIdAuthProviderDelegationConfig = record { + targets : opt vec principal; + max_time_to_live : opt nat64; +}; +type OpenIdAuthenticateControllerArgs = record { + jwt : text; + metadata : vec record { text; text }; + scope : AutomationScope; + max_time_to_live : opt nat64; + controller_id : principal; +}; +type OpenIdDelegationProvider = variant { GitHub; Google }; type OpenIdGetDelegationArgs = record { jwt : text; session_key : blob; @@ -223,15 +247,10 @@ type OpenIdPrepareDelegationArgs = record { session_key : blob; salt : blob; }; -type OpenIdProvider = variant { GitHubActions; Google; GitHubProxy }; -type OpenIdProviderConfig = record { - delegation : opt OpenIdProviderDelegationConfig; +type OpenIdProviderAuthConfig = record { + delegation : opt OpenIdAuthProviderDelegationConfig; client_id : text; }; -type OpenIdProviderDelegationConfig = record { - targets : opt vec principal; - max_time_to_live : opt nat64; -}; type Permission = variant { Controllers; Private; Public; Managed }; type PrepareDelegationError = variant { JwtFindProvider : JwtFindProviderError; @@ -373,8 +392,16 @@ type UploadChunk = record { order_id : opt nat; }; type UploadChunkResult = record { chunk_id : nat }; +type VerifyOpenidAutomationCredentialsError = variant { + GetCachedJwks; + JwtVerify : JwtVerifyError; + GetOrFetchJwks : GetOrRefreshJwksError; +}; service : (InitSatelliteArgs) -> { authenticate : (AuthenticationArgs) -> (AuthenticateResultResponse); + authenticate_controller : (AuthenticateControllerArgs) -> ( + AuthenticateControllerResultResponse, + ); commit_asset_upload : (CommitBatch) -> (); commit_proposal : (CommitProposal) -> (null); commit_proposal_asset_upload : (CommitBatch) -> (); diff --git a/src/sputnik/sputnik.did b/src/sputnik/sputnik.did index 4eb0e68586..030dc10729 100644 --- a/src/sputnik/sputnik.did +++ b/src/sputnik/sputnik.did @@ -22,6 +22,13 @@ type AssetNoContent = record { version : opt nat64; }; type AssetsUpgradeOptions = record { clear_existing_assets : opt bool }; +type AuthenticateControllerArgs = variant { + OpenId : OpenIdAuthenticateControllerArgs; +}; +type AuthenticateControllerResultResponse = variant { + Ok; + Err : AuthenticationControllerError; +}; type AuthenticateResultResponse = variant { Ok : Authentication; Err : AuthenticationError; @@ -42,13 +49,18 @@ type AuthenticationConfigInternetIdentity = record { }; type AuthenticationConfigOpenId = record { observatory_id : opt principal; - providers : vec record { OpenIdProvider; OpenIdProviderConfig }; + providers : vec record { OpenIdDelegationProvider; OpenIdProviderAuthConfig }; +}; +type AuthenticationControllerError = variant { + RegisterController : text; + VerifyOpenIdCredentials : VerifyOpenidAutomationCredentialsError; }; type AuthenticationError = variant { PrepareDelegation : PrepareDelegationError; RegisterUser : text; }; type AuthenticationRules = record { allowed_callers : vec principal }; +type AutomationScope = variant { Write; Submit }; type CollectionType = variant { Db; Storage }; type CommitBatch = record { batch_id : nat; @@ -212,6 +224,18 @@ type ListRulesResults = record { }; type Memory = variant { Heap; Stable }; type MemorySize = record { stable : nat64; heap : nat64 }; +type OpenIdAuthProviderDelegationConfig = record { + targets : opt vec principal; + max_time_to_live : opt nat64; +}; +type OpenIdAuthenticateControllerArgs = record { + jwt : text; + metadata : vec record { text; text }; + scope : AutomationScope; + max_time_to_live : opt nat64; + controller_id : principal; +}; +type OpenIdDelegationProvider = variant { GitHub; Google }; type OpenIdGetDelegationArgs = record { jwt : text; session_key : blob; @@ -223,15 +247,10 @@ type OpenIdPrepareDelegationArgs = record { session_key : blob; salt : blob; }; -type OpenIdProvider = variant { GitHubActions; Google; GitHubProxy }; -type OpenIdProviderConfig = record { - delegation : opt OpenIdProviderDelegationConfig; +type OpenIdProviderAuthConfig = record { + delegation : opt OpenIdAuthProviderDelegationConfig; client_id : text; }; -type OpenIdProviderDelegationConfig = record { - targets : opt vec principal; - max_time_to_live : opt nat64; -}; type Permission = variant { Controllers; Private; Public; Managed }; type PrepareDelegationError = variant { JwtFindProvider : JwtFindProviderError; @@ -373,8 +392,16 @@ type UploadChunk = record { order_id : opt nat; }; type UploadChunkResult = record { chunk_id : nat }; +type VerifyOpenidAutomationCredentialsError = variant { + GetCachedJwks; + JwtVerify : JwtVerifyError; + GetOrFetchJwks : GetOrRefreshJwksError; +}; service : (InitSatelliteArgs) -> { authenticate : (AuthenticationArgs) -> (AuthenticateResultResponse); + authenticate_controller : (AuthenticateControllerArgs) -> ( + AuthenticateControllerResultResponse, + ); commit_asset_upload : (CommitBatch) -> (); commit_proposal : (CommitProposal) -> (null); commit_proposal_asset_upload : (CommitBatch) -> (); diff --git a/src/tests/declarations/test_satellite/test_satellite.did.d.ts b/src/tests/declarations/test_satellite/test_satellite.did.d.ts index cc7fbe2464..9506498cb5 100644 --- a/src/tests/declarations/test_satellite/test_satellite.did.d.ts +++ b/src/tests/declarations/test_satellite/test_satellite.did.d.ts @@ -34,6 +34,12 @@ export interface AssetNoContent { export interface AssetsUpgradeOptions { clear_existing_assets: [] | [boolean]; } +export type AuthenticateControllerArgs = { + OpenId: OpenIdAuthenticateControllerArgs; +}; +export type AuthenticateControllerResultResponse = + | { Ok: null } + | { Err: AuthenticationControllerError }; export type AuthenticateResultResponse = { Ok: Authentication } | { Err: AuthenticationError }; export interface Authentication { doc: Doc; @@ -54,8 +60,11 @@ export interface AuthenticationConfigInternetIdentity { } export interface AuthenticationConfigOpenId { observatory_id: [] | [Principal]; - providers: Array<[OpenIdProvider, OpenIdProviderConfig]>; + providers: Array<[OpenIdDelegationProvider, OpenIdProviderAuthConfig]>; } +export type AuthenticationControllerError = + | { RegisterController: string } + | { VerifyOpenIdCredentials: VerifyOpenidAutomationCredentialsError }; export type AuthenticationError = | { PrepareDelegation: PrepareDelegationError; @@ -64,6 +73,7 @@ export type AuthenticationError = export interface AuthenticationRules { allowed_callers: Array; } +export type AutomationScope = { Write: null } | { Submit: null }; export type CollectionType = { Db: null } | { Storage: null }; export interface CommitBatch { batch_id: bigint; @@ -259,6 +269,18 @@ export interface MemorySize { stable: bigint; heap: bigint; } +export interface OpenIdAuthProviderDelegationConfig { + targets: [] | [Array]; + max_time_to_live: [] | [bigint]; +} +export interface OpenIdAuthenticateControllerArgs { + jwt: string; + metadata: Array<[string, string]>; + scope: AutomationScope; + max_time_to_live: [] | [bigint]; + controller_id: Principal; +} +export type OpenIdDelegationProvider = { GitHub: null } | { Google: null }; export interface OpenIdGetDelegationArgs { jwt: string; session_key: Uint8Array; @@ -270,15 +292,10 @@ export interface OpenIdPrepareDelegationArgs { session_key: Uint8Array; salt: Uint8Array; } -export type OpenIdProvider = { GitHubActions: null } | { Google: null } | { GitHubProxy: null }; -export interface OpenIdProviderConfig { - delegation: [] | [OpenIdProviderDelegationConfig]; +export interface OpenIdProviderAuthConfig { + delegation: [] | [OpenIdAuthProviderDelegationConfig]; client_id: string; } -export interface OpenIdProviderDelegationConfig { - targets: [] | [Array]; - max_time_to_live: [] | [bigint]; -} export type Permission = | { Controllers: null } | { Private: null } @@ -439,8 +456,18 @@ export interface UploadChunk { export interface UploadChunkResult { chunk_id: bigint; } +export type VerifyOpenidAutomationCredentialsError = + | { + GetCachedJwks: null; + } + | { JwtVerify: JwtVerifyError } + | { GetOrFetchJwks: GetOrRefreshJwksError }; export interface _SERVICE { authenticate: ActorMethod<[AuthenticationArgs], AuthenticateResultResponse>; + authenticate_controller: ActorMethod< + [AuthenticateControllerArgs], + AuthenticateControllerResultResponse + >; commit_asset_upload: ActorMethod<[CommitBatch], undefined>; commit_proposal: ActorMethod<[CommitProposal], null>; commit_proposal_asset_upload: ActorMethod<[CommitBatch], undefined>; diff --git a/src/tests/declarations/test_satellite/test_satellite.factory.certified.did.js b/src/tests/declarations/test_satellite/test_satellite.factory.certified.did.js index 8fa34b8bfa..09f883de72 100644 --- a/src/tests/declarations/test_satellite/test_satellite.factory.certified.did.js +++ b/src/tests/declarations/test_satellite/test_satellite.factory.certified.did.js @@ -75,6 +75,33 @@ export const idlFactory = ({ IDL }) => { Ok: Authentication, Err: AuthenticationError }); + const AutomationScope = IDL.Variant({ + Write: IDL.Null, + Submit: IDL.Null + }); + const OpenIdAuthenticateControllerArgs = IDL.Record({ + jwt: IDL.Text, + metadata: IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text)), + scope: AutomationScope, + max_time_to_live: IDL.Opt(IDL.Nat64), + controller_id: IDL.Principal + }); + const AuthenticateControllerArgs = IDL.Variant({ + OpenId: OpenIdAuthenticateControllerArgs + }); + const VerifyOpenidAutomationCredentialsError = IDL.Variant({ + GetCachedJwks: IDL.Null, + JwtVerify: JwtVerifyError, + GetOrFetchJwks: GetOrRefreshJwksError + }); + const AuthenticationControllerError = IDL.Variant({ + RegisterController: IDL.Text, + VerifyOpenIdCredentials: VerifyOpenidAutomationCredentialsError + }); + const AuthenticateControllerResultResponse = IDL.Variant({ + Ok: IDL.Null, + Err: AuthenticationControllerError + }); const CommitBatch = IDL.Record({ batch_id: IDL.Nat, headers: IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text)), @@ -158,22 +185,21 @@ export const idlFactory = ({ IDL }) => { created_at: IDL.Nat64, version: IDL.Opt(IDL.Nat64) }); - const OpenIdProvider = IDL.Variant({ - GitHubActions: IDL.Null, - Google: IDL.Null, - GitHubProxy: IDL.Null + const OpenIdDelegationProvider = IDL.Variant({ + GitHub: IDL.Null, + Google: IDL.Null }); - const OpenIdProviderDelegationConfig = IDL.Record({ + const OpenIdAuthProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), max_time_to_live: IDL.Opt(IDL.Nat64) }); - const OpenIdProviderConfig = IDL.Record({ - delegation: IDL.Opt(OpenIdProviderDelegationConfig), + const OpenIdProviderAuthConfig = IDL.Record({ + delegation: IDL.Opt(OpenIdAuthProviderDelegationConfig), client_id: IDL.Text }); const AuthenticationConfigOpenId = IDL.Record({ observatory_id: IDL.Opt(IDL.Principal), - providers: IDL.Vec(IDL.Tuple(OpenIdProvider, OpenIdProviderConfig)) + providers: IDL.Vec(IDL.Tuple(OpenIdDelegationProvider, OpenIdProviderAuthConfig)) }); const AuthenticationConfigInternetIdentity = IDL.Record({ derivation_origin: IDL.Opt(IDL.Text), @@ -448,6 +474,11 @@ export const idlFactory = ({ IDL }) => { return IDL.Service({ authenticate: IDL.Func([AuthenticationArgs], [AuthenticateResultResponse], []), + authenticate_controller: IDL.Func( + [AuthenticateControllerArgs], + [AuthenticateControllerResultResponse], + [] + ), commit_asset_upload: IDL.Func([CommitBatch], [], []), commit_proposal: IDL.Func([CommitProposal], [IDL.Null], []), commit_proposal_asset_upload: IDL.Func([CommitBatch], [], []), diff --git a/src/tests/declarations/test_satellite/test_satellite.factory.did.js b/src/tests/declarations/test_satellite/test_satellite.factory.did.js index c91ec13f54..8f4be55325 100644 --- a/src/tests/declarations/test_satellite/test_satellite.factory.did.js +++ b/src/tests/declarations/test_satellite/test_satellite.factory.did.js @@ -75,6 +75,33 @@ export const idlFactory = ({ IDL }) => { Ok: Authentication, Err: AuthenticationError }); + const AutomationScope = IDL.Variant({ + Write: IDL.Null, + Submit: IDL.Null + }); + const OpenIdAuthenticateControllerArgs = IDL.Record({ + jwt: IDL.Text, + metadata: IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text)), + scope: AutomationScope, + max_time_to_live: IDL.Opt(IDL.Nat64), + controller_id: IDL.Principal + }); + const AuthenticateControllerArgs = IDL.Variant({ + OpenId: OpenIdAuthenticateControllerArgs + }); + const VerifyOpenidAutomationCredentialsError = IDL.Variant({ + GetCachedJwks: IDL.Null, + JwtVerify: JwtVerifyError, + GetOrFetchJwks: GetOrRefreshJwksError + }); + const AuthenticationControllerError = IDL.Variant({ + RegisterController: IDL.Text, + VerifyOpenIdCredentials: VerifyOpenidAutomationCredentialsError + }); + const AuthenticateControllerResultResponse = IDL.Variant({ + Ok: IDL.Null, + Err: AuthenticationControllerError + }); const CommitBatch = IDL.Record({ batch_id: IDL.Nat, headers: IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text)), @@ -158,22 +185,21 @@ export const idlFactory = ({ IDL }) => { created_at: IDL.Nat64, version: IDL.Opt(IDL.Nat64) }); - const OpenIdProvider = IDL.Variant({ - GitHubActions: IDL.Null, - Google: IDL.Null, - GitHubProxy: IDL.Null + const OpenIdDelegationProvider = IDL.Variant({ + GitHub: IDL.Null, + Google: IDL.Null }); - const OpenIdProviderDelegationConfig = IDL.Record({ + const OpenIdAuthProviderDelegationConfig = IDL.Record({ targets: IDL.Opt(IDL.Vec(IDL.Principal)), max_time_to_live: IDL.Opt(IDL.Nat64) }); - const OpenIdProviderConfig = IDL.Record({ - delegation: IDL.Opt(OpenIdProviderDelegationConfig), + const OpenIdProviderAuthConfig = IDL.Record({ + delegation: IDL.Opt(OpenIdAuthProviderDelegationConfig), client_id: IDL.Text }); const AuthenticationConfigOpenId = IDL.Record({ observatory_id: IDL.Opt(IDL.Principal), - providers: IDL.Vec(IDL.Tuple(OpenIdProvider, OpenIdProviderConfig)) + providers: IDL.Vec(IDL.Tuple(OpenIdDelegationProvider, OpenIdProviderAuthConfig)) }); const AuthenticationConfigInternetIdentity = IDL.Record({ derivation_origin: IDL.Opt(IDL.Text), @@ -448,6 +474,11 @@ export const idlFactory = ({ IDL }) => { return IDL.Service({ authenticate: IDL.Func([AuthenticationArgs], [AuthenticateResultResponse], []), + authenticate_controller: IDL.Func( + [AuthenticateControllerArgs], + [AuthenticateControllerResultResponse], + [] + ), commit_asset_upload: IDL.Func([CommitBatch], [], []), commit_proposal: IDL.Func([CommitProposal], [IDL.Null], []), commit_proposal_asset_upload: IDL.Func([CommitBatch], [], []), diff --git a/src/tests/fixtures/test_satellite/test_satellite.did b/src/tests/fixtures/test_satellite/test_satellite.did index 61bee19770..ffe75739e6 100644 --- a/src/tests/fixtures/test_satellite/test_satellite.did +++ b/src/tests/fixtures/test_satellite/test_satellite.did @@ -22,6 +22,13 @@ type AssetNoContent = record { version : opt nat64; }; type AssetsUpgradeOptions = record { clear_existing_assets : opt bool }; +type AuthenticateControllerArgs = variant { + OpenId : OpenIdAuthenticateControllerArgs; +}; +type AuthenticateControllerResultResponse = variant { + Ok; + Err : AuthenticationControllerError; +}; type AuthenticateResultResponse = variant { Ok : Authentication; Err : AuthenticationError; @@ -42,13 +49,18 @@ type AuthenticationConfigInternetIdentity = record { }; type AuthenticationConfigOpenId = record { observatory_id : opt principal; - providers : vec record { OpenIdProvider; OpenIdProviderConfig }; + providers : vec record { OpenIdDelegationProvider; OpenIdProviderAuthConfig }; +}; +type AuthenticationControllerError = variant { + RegisterController : text; + VerifyOpenIdCredentials : VerifyOpenidAutomationCredentialsError; }; type AuthenticationError = variant { PrepareDelegation : PrepareDelegationError; RegisterUser : text; }; type AuthenticationRules = record { allowed_callers : vec principal }; +type AutomationScope = variant { Write; Submit }; type CollectionType = variant { Db; Storage }; type CommitBatch = record { batch_id : nat; @@ -212,6 +224,18 @@ type ListRulesResults = record { }; type Memory = variant { Heap; Stable }; type MemorySize = record { stable : nat64; heap : nat64 }; +type OpenIdAuthProviderDelegationConfig = record { + targets : opt vec principal; + max_time_to_live : opt nat64; +}; +type OpenIdAuthenticateControllerArgs = record { + jwt : text; + metadata : vec record { text; text }; + scope : AutomationScope; + max_time_to_live : opt nat64; + controller_id : principal; +}; +type OpenIdDelegationProvider = variant { GitHub; Google }; type OpenIdGetDelegationArgs = record { jwt : text; session_key : blob; @@ -223,15 +247,10 @@ type OpenIdPrepareDelegationArgs = record { session_key : blob; salt : blob; }; -type OpenIdProvider = variant { GitHubActions; Google; GitHubProxy }; -type OpenIdProviderConfig = record { - delegation : opt OpenIdProviderDelegationConfig; +type OpenIdProviderAuthConfig = record { + delegation : opt OpenIdAuthProviderDelegationConfig; client_id : text; }; -type OpenIdProviderDelegationConfig = record { - targets : opt vec principal; - max_time_to_live : opt nat64; -}; type Permission = variant { Controllers; Private; Public; Managed }; type PrepareDelegationError = variant { JwtFindProvider : JwtFindProviderError; @@ -373,8 +392,16 @@ type UploadChunk = record { order_id : opt nat; }; type UploadChunkResult = record { chunk_id : nat }; +type VerifyOpenidAutomationCredentialsError = variant { + GetCachedJwks; + JwtVerify : JwtVerifyError; + GetOrFetchJwks : GetOrRefreshJwksError; +}; service : (InitSatelliteArgs) -> { authenticate : (AuthenticationArgs) -> (AuthenticateResultResponse); + authenticate_controller : (AuthenticateControllerArgs) -> ( + AuthenticateControllerResultResponse, + ); commit_asset_upload : (CommitBatch) -> (); commit_proposal : (CommitProposal) -> (null); commit_proposal_asset_upload : (CommitBatch) -> (); From f87b61a605e000cc0efaea60633d09ece8add8b7 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 28 Jan 2026 11:39:24 +0100 Subject: [PATCH 15/32] feat: actions in observatory --- src/observatory/src/openid/scheduler.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/observatory/src/openid/scheduler.rs b/src/observatory/src/openid/scheduler.rs index 3ffc530e82..cbdc468770 100644 --- a/src/observatory/src/openid/scheduler.rs +++ b/src/observatory/src/openid/scheduler.rs @@ -9,10 +9,14 @@ use std::time::Duration; pub fn defer_restart_monitoring() { // Early spare one timer if no scheduler is enabled. - let enabled_count = [OpenIdProvider::Google, OpenIdProvider::GitHubProxy] - .into_iter() - .filter(|provider| is_scheduler_enabled(provider)) - .count(); + let enabled_count = [ + OpenIdProvider::Google, + OpenIdProvider::GitHubProxy, + OpenIdProvider::GitHubActions, + ] + .into_iter() + .filter(|provider| is_scheduler_enabled(provider)) + .count(); if enabled_count == 0 { return; @@ -24,7 +28,11 @@ pub fn defer_restart_monitoring() { } async fn restart_monitoring() { - for provider in [OpenIdProvider::Google, OpenIdProvider::GitHubProxy] { + for provider in [ + OpenIdProvider::Google, + OpenIdProvider::GitHubProxy, + OpenIdProvider::GitHubActions, + ] { schedule_certificate_update(provider, None); } } From ed29f566b5b920e654e8692ef0066033e8cbcb24 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 28 Jan 2026 12:03:07 +0100 Subject: [PATCH 16/32] feat: one time upgrade --- src/console/src/lib.rs | 1 + src/console/src/memory/lifecycle.rs | 6 +++- src/console/src/upgrade/impls.rs | 55 +++++++++++++++++++++++++++++ src/console/src/upgrade/mod.rs | 2 ++ src/console/src/upgrade/types.rs | 55 +++++++++++++++++++++++++++++ 5 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 src/console/src/upgrade/impls.rs create mode 100644 src/console/src/upgrade/mod.rs create mode 100644 src/console/src/upgrade/types.rs diff --git a/src/console/src/lib.rs b/src/console/src/lib.rs index 02650cc546..d4432531bb 100644 --- a/src/console/src/lib.rs +++ b/src/console/src/lib.rs @@ -17,6 +17,7 @@ mod rates; mod segments; mod store; mod types; +mod upgrade; use crate::types::interface::AuthenticationArgs; use crate::types::interface::AuthenticationResult; diff --git a/src/console/src/memory/lifecycle.rs b/src/console/src/memory/lifecycle.rs index c4d911d416..1bf23cf919 100644 --- a/src/console/src/memory/lifecycle.rs +++ b/src/console/src/memory/lifecycle.rs @@ -10,6 +10,7 @@ use junobuild_shared::ic::api::caller; use junobuild_shared::memory::upgrade::{read_post_upgrade, write_pre_upgrade}; use junobuild_shared::segments::controllers::init_admin_controllers; use std::collections::HashMap; +use crate::upgrade::types::upgrade::UpgradeState; #[init] fn init() { @@ -50,9 +51,12 @@ fn post_upgrade() { let memory = get_memory_upgrades(); let state_bytes = read_post_upgrade(&memory); - let state: State = from_reader(&*state_bytes) + // TODO: remove once stable memory introduced on mainnet + let upgrade_state: UpgradeState = from_reader(&*state_bytes) .expect("Failed to decode the state of the console in post_upgrade hook."); + let state: State = upgrade_state.into(); + STATE.with(|s| *s.borrow_mut() = state); defer_init_certified_assets(); diff --git a/src/console/src/upgrade/impls.rs b/src/console/src/upgrade/impls.rs new file mode 100644 index 0000000000..974b9ad7b0 --- /dev/null +++ b/src/console/src/upgrade/impls.rs @@ -0,0 +1,55 @@ +use junobuild_auth::openid::types::provider::OpenIdProvider; +use junobuild_auth::state::types::state::{AuthenticationHeapState, OpenIdState}; +use crate::types::state::{HeapState, State}; +use crate::upgrade::types::upgrade::{UpgradeAuthenticationHeapState, UpgradeHeapState, UpgradeOpenIdProvider, UpgradeState}; + +impl From for State { + fn from(upgrade: UpgradeState) -> Self { + State { + stable: upgrade.stable, + heap: upgrade.heap.into(), + } + } +} + +impl From for HeapState { + fn from(upgrade: UpgradeHeapState) -> Self { + HeapState { + authentication: upgrade.authentication.map(|auth| auth.into()), + controllers: upgrade.controllers, + mission_controls: upgrade.mission_controls, + payments: upgrade.payments, + invitation_codes: upgrade.invitation_codes, + factory_fees: upgrade.factory_fees, + factory_rates: upgrade.factory_rates, + storage: upgrade.storage, + releases_metadata: upgrade.releases_metadata, + } + } +} + +impl From for AuthenticationHeapState { + fn from(upgrade: UpgradeAuthenticationHeapState) -> Self { + AuthenticationHeapState { + config: upgrade.config, + salt: upgrade.salt, + openid: upgrade.openid.map(|openid_state| { + OpenIdState { + certificates: openid_state.certificates + .into_iter() + .map(|(provider, cert)| (provider.into(), cert)) + .collect() + } + }), + } + } +} + +impl From for OpenIdProvider { + fn from(old: UpgradeOpenIdProvider) -> Self { + match old { + UpgradeOpenIdProvider::Google => OpenIdProvider::Google, + UpgradeOpenIdProvider::GitHub => OpenIdProvider::GitHubProxy, + } + } +} \ No newline at end of file diff --git a/src/console/src/upgrade/mod.rs b/src/console/src/upgrade/mod.rs new file mode 100644 index 0000000000..39fcb9cfe1 --- /dev/null +++ b/src/console/src/upgrade/mod.rs @@ -0,0 +1,2 @@ +mod impls; +pub mod types; diff --git a/src/console/src/upgrade/types.rs b/src/console/src/upgrade/types.rs new file mode 100644 index 0000000000..8075586c5e --- /dev/null +++ b/src/console/src/upgrade/types.rs @@ -0,0 +1,55 @@ +pub mod upgrade { + use std::collections::HashMap; + use candid::{CandidType, Deserialize}; + use serde::Serialize; + use junobuild_auth::state::types::config::AuthenticationConfig; + use junobuild_auth::state::types::state::{OpenIdCachedCertificate, Salt}; + use junobuild_shared::types::state::Controllers; + use junobuild_storage::types::state::StorageHeapState; + use crate::types::state::{Accounts, FactoryFees, FactoryRates, IcpPayments, InvitationCodes, ReleasesMetadata, StableState}; + use crate::memory::manager::init_stable_state; + + #[derive(Serialize, Deserialize)] + pub struct UpgradeState { + // Direct stable state: State that is uses stable memory directly as its store. No need for pre/post upgrade hooks. + #[serde(skip, default = "init_stable_state")] + pub stable: StableState, + + pub heap: UpgradeHeapState, + } + + #[derive(Default, CandidType, Serialize, Deserialize, Clone)] + pub struct UpgradeHeapState { + #[deprecated(note = "Deprecated. Use stable memory instead.")] + pub mission_controls: Accounts, + #[deprecated(note = "Deprecated. Use stable memory instead.")] + pub payments: IcpPayments, + pub invitation_codes: InvitationCodes, + pub controllers: Controllers, + pub factory_fees: Option, + pub factory_rates: Option, + pub storage: StorageHeapState, + pub authentication: Option, + pub releases_metadata: ReleasesMetadata, + } + + #[derive(Default, CandidType, Serialize, Deserialize, Clone)] + pub struct UpgradeAuthenticationHeapState { + pub config: AuthenticationConfig, + pub salt: Option, + pub openid: Option, + } + + #[derive(Default, CandidType, Serialize, Deserialize, Clone)] + pub struct UpgradeOpenIdState { + pub certificates: HashMap, + } + + #[derive( + CandidType, Serialize, Deserialize, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug, + )] + pub enum UpgradeOpenIdProvider { + Google, + GitHub, + } +} From 20c1f5fbf6a6478f7af487e23b30095958357664 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 28 Jan 2026 12:09:55 +0100 Subject: [PATCH 17/32] feat: one time upgrade --- src/console/src/memory/lifecycle.rs | 2 +- src/console/src/upgrade/impls.rs | 21 +++++----- src/console/src/upgrade/types.rs | 11 ++++-- src/observatory/src/lib.rs | 1 + src/observatory/src/memory/lifecycle.rs | 6 ++- src/observatory/src/upgrade/impls.rs | 51 +++++++++++++++++++++++++ src/observatory/src/upgrade/mod.rs | 2 + src/observatory/src/upgrade/types.rs | 40 +++++++++++++++++++ 8 files changed, 118 insertions(+), 16 deletions(-) create mode 100644 src/observatory/src/upgrade/impls.rs create mode 100644 src/observatory/src/upgrade/mod.rs create mode 100644 src/observatory/src/upgrade/types.rs diff --git a/src/console/src/memory/lifecycle.rs b/src/console/src/memory/lifecycle.rs index 1bf23cf919..5a782b9791 100644 --- a/src/console/src/memory/lifecycle.rs +++ b/src/console/src/memory/lifecycle.rs @@ -4,13 +4,13 @@ use crate::fees::init_factory_fees; use crate::memory::manager::{get_memory_upgrades, init_stable_state, STATE}; use crate::rates::init::init_factory_rates; use crate::types::state::{HeapState, ReleasesMetadata, State}; +use crate::upgrade::types::upgrade::UpgradeState; use ciborium::{from_reader, into_writer}; use ic_cdk_macros::{init, post_upgrade, pre_upgrade}; use junobuild_shared::ic::api::caller; use junobuild_shared::memory::upgrade::{read_post_upgrade, write_pre_upgrade}; use junobuild_shared::segments::controllers::init_admin_controllers; use std::collections::HashMap; -use crate::upgrade::types::upgrade::UpgradeState; #[init] fn init() { diff --git a/src/console/src/upgrade/impls.rs b/src/console/src/upgrade/impls.rs index 974b9ad7b0..f4a6f20b88 100644 --- a/src/console/src/upgrade/impls.rs +++ b/src/console/src/upgrade/impls.rs @@ -1,7 +1,9 @@ +use crate::types::state::{HeapState, State}; +use crate::upgrade::types::upgrade::{ + UpgradeAuthenticationHeapState, UpgradeHeapState, UpgradeOpenIdProvider, UpgradeState, +}; use junobuild_auth::openid::types::provider::OpenIdProvider; use junobuild_auth::state::types::state::{AuthenticationHeapState, OpenIdState}; -use crate::types::state::{HeapState, State}; -use crate::upgrade::types::upgrade::{UpgradeAuthenticationHeapState, UpgradeHeapState, UpgradeOpenIdProvider, UpgradeState}; impl From for State { fn from(upgrade: UpgradeState) -> Self { @@ -33,13 +35,12 @@ impl From for AuthenticationHeapState { AuthenticationHeapState { config: upgrade.config, salt: upgrade.salt, - openid: upgrade.openid.map(|openid_state| { - OpenIdState { - certificates: openid_state.certificates - .into_iter() - .map(|(provider, cert)| (provider.into(), cert)) - .collect() - } + openid: upgrade.openid.map(|openid_state| OpenIdState { + certificates: openid_state + .certificates + .into_iter() + .map(|(provider, cert)| (provider.into(), cert)) + .collect(), }), } } @@ -52,4 +53,4 @@ impl From for OpenIdProvider { UpgradeOpenIdProvider::GitHub => OpenIdProvider::GitHubProxy, } } -} \ No newline at end of file +} diff --git a/src/console/src/upgrade/types.rs b/src/console/src/upgrade/types.rs index 8075586c5e..cde8370010 100644 --- a/src/console/src/upgrade/types.rs +++ b/src/console/src/upgrade/types.rs @@ -1,13 +1,16 @@ pub mod upgrade { - use std::collections::HashMap; + use crate::memory::manager::init_stable_state; + use crate::types::state::{ + Accounts, FactoryFees, FactoryRates, IcpPayments, InvitationCodes, ReleasesMetadata, + StableState, + }; use candid::{CandidType, Deserialize}; - use serde::Serialize; use junobuild_auth::state::types::config::AuthenticationConfig; use junobuild_auth::state::types::state::{OpenIdCachedCertificate, Salt}; use junobuild_shared::types::state::Controllers; use junobuild_storage::types::state::StorageHeapState; - use crate::types::state::{Accounts, FactoryFees, FactoryRates, IcpPayments, InvitationCodes, ReleasesMetadata, StableState}; - use crate::memory::manager::init_stable_state; + use serde::Serialize; + use std::collections::HashMap; #[derive(Serialize, Deserialize)] pub struct UpgradeState { diff --git a/src/observatory/src/lib.rs b/src/observatory/src/lib.rs index e183fe0da0..34d7e821c4 100644 --- a/src/observatory/src/lib.rs +++ b/src/observatory/src/lib.rs @@ -11,6 +11,7 @@ mod random; mod store; mod templates; mod types; +mod upgrade; use crate::types::interface::GetNotifications; use crate::types::interface::NotifyStatus; diff --git a/src/observatory/src/memory/lifecycle.rs b/src/observatory/src/memory/lifecycle.rs index f841d7183f..3527855fd2 100644 --- a/src/observatory/src/memory/lifecycle.rs +++ b/src/observatory/src/memory/lifecycle.rs @@ -3,6 +3,7 @@ use crate::memory::state::STATE; use crate::openid::scheduler::defer_restart_monitoring; use crate::random::defer_init_random_seed; use crate::types::state::{HeapState, State}; +use crate::upgrade::types::upgrade::UpgradeState; use ciborium::{from_reader, into_writer}; use ic_cdk_macros::{init, post_upgrade, pre_upgrade}; use junobuild_shared::ic::api::caller; @@ -45,9 +46,12 @@ fn post_upgrade() { let memory = get_memory_upgrades(); let state_bytes = read_post_upgrade(&memory); - let state: State = from_reader(&*state_bytes) + // TODO: remove once stable memory introduced on mainnet + let upgrade_state: UpgradeState = from_reader(&*state_bytes) .expect("Failed to decode the state of the observatory in post_upgrade hook."); + let state: State = upgrade_state.into(); + STATE.with(|s| *s.borrow_mut() = state); init_runtime_state(); diff --git a/src/observatory/src/upgrade/impls.rs b/src/observatory/src/upgrade/impls.rs new file mode 100644 index 0000000000..2c6ad30b25 --- /dev/null +++ b/src/observatory/src/upgrade/impls.rs @@ -0,0 +1,51 @@ +use crate::types::state::{HeapState, OpenId, State}; +use crate::upgrade::types::upgrade::{ + UpgradeHeapState, UpgradeOpenId, UpgradeOpenIdProvider, UpgradeState, +}; +use junobuild_auth::openid::types::provider::OpenIdProvider; + +impl From for State { + fn from(upgrade: UpgradeState) -> Self { + State { + stable: upgrade.stable, + heap: upgrade.heap.into(), + } + } +} + +impl From for HeapState { + fn from(upgrade: UpgradeHeapState) -> Self { + HeapState { + controllers: upgrade.controllers, + env: upgrade.env, + openid: upgrade.openid.map(|openid| openid.into()), + rates: upgrade.rates, + } + } +} + +impl From for OpenId { + fn from(upgrade: UpgradeOpenId) -> Self { + OpenId { + certificates: upgrade + .certificates + .into_iter() + .map(|(provider, cert)| (provider.into(), cert)) + .collect(), + schedulers: upgrade + .schedulers + .into_iter() + .map(|(provider, scheduler)| (provider.into(), scheduler)) + .collect(), + } + } +} + +impl From for OpenIdProvider { + fn from(old: UpgradeOpenIdProvider) -> Self { + match old { + UpgradeOpenIdProvider::Google => OpenIdProvider::Google, + UpgradeOpenIdProvider::GitHub => OpenIdProvider::GitHubProxy, + } + } +} diff --git a/src/observatory/src/upgrade/mod.rs b/src/observatory/src/upgrade/mod.rs new file mode 100644 index 0000000000..39fcb9cfe1 --- /dev/null +++ b/src/observatory/src/upgrade/mod.rs @@ -0,0 +1,2 @@ +mod impls; +pub mod types; diff --git a/src/observatory/src/upgrade/types.rs b/src/observatory/src/upgrade/types.rs new file mode 100644 index 0000000000..d5cd9d858d --- /dev/null +++ b/src/observatory/src/upgrade/types.rs @@ -0,0 +1,40 @@ +pub mod upgrade { + use crate::memory::init_stable_state; + use crate::types::state::{Env, OpenIdScheduler, Rates, StableState}; + use candid::{CandidType, Deserialize}; + use junobuild_auth::openid::types::provider::OpenIdCertificate; + use junobuild_shared::types::state::Controllers; + use serde::Serialize; + use std::collections::HashMap; + + #[derive(Serialize, Deserialize)] + pub struct UpgradeState { + // Direct stable state: State that is uses stable memory directly as its store. No need for pre/post upgrade hooks. + #[serde(skip, default = "init_stable_state")] + pub stable: StableState, + + pub heap: UpgradeHeapState, + } + + #[derive(Default, CandidType, Serialize, Deserialize)] + pub struct UpgradeHeapState { + pub controllers: Controllers, + pub env: Option, + pub openid: Option, + pub rates: Option, + } + + #[derive(Default, CandidType, Serialize, Deserialize)] + pub struct UpgradeOpenId { + pub certificates: HashMap, + pub schedulers: HashMap, + } + + #[derive( + CandidType, Serialize, Deserialize, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug, + )] + pub enum UpgradeOpenIdProvider { + Google, + GitHub, + } +} From 0ad8e611edbb67a930b69cea690ce6be5f30b481 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 28 Jan 2026 12:40:48 +0100 Subject: [PATCH 18/32] feat: export auth --- src/libs/satellite/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/satellite/src/lib.rs b/src/libs/satellite/src/lib.rs index 9679bc3894..c5199eda74 100644 --- a/src/libs/satellite/src/lib.rs +++ b/src/libs/satellite/src/lib.rs @@ -561,7 +561,7 @@ macro_rules! include_satellite { post_upgrade, pre_upgrade, reject_proposal, set_asset_token, set_auth_config, set_controllers, set_custom_domain, set_db_config, set_doc, set_many_docs, set_rule, set_storage_config, submit_proposal, switch_storage_system_memory, upload_asset_chunk, - upload_proposal_asset_chunk, + upload_proposal_asset_chunk, authenticate_controller, }; ic_cdk::export_candid!(); From 235abb50573bb407113317897e00e7b8adc5f63c Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 28 Jan 2026 15:09:04 +0100 Subject: [PATCH 19/32] chore: fmt --- src/libs/satellite/src/lib.rs | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/libs/satellite/src/lib.rs b/src/libs/satellite/src/lib.rs index c5199eda74..db1474a5f0 100644 --- a/src/libs/satellite/src/lib.rs +++ b/src/libs/satellite/src/lib.rs @@ -18,7 +18,9 @@ mod sdk; mod types; mod user; +use crate::controllers::types::AuthenticateControllerArgs; use crate::db::types::config::DbConfig; +use crate::db::types::interface::SetDbConfig; use crate::guards::{ caller_is_admin_controller, caller_is_controller, caller_is_controller_with_write, }; @@ -29,6 +31,7 @@ use crate::types::interface::{ use crate::types::state::CollectionType; use ic_cdk_macros::{init, post_upgrade, pre_upgrade, query, update}; use junobuild_auth::state::types::config::AuthenticationConfig; +use junobuild_auth::state::types::interface::SetAuthenticationConfig; use junobuild_cdn::proposals::{ CommitProposal, ListProposalResults, ListProposalsParams, Proposal, ProposalId, ProposalType, RejectProposal, @@ -63,11 +66,9 @@ use memory::lifecycle; // ============================================================================================ // These types are made available for use in Serverless Functions. // ============================================================================================ -use crate::controllers::types::AuthenticateControllerArgs; -use crate::db::types::interface::SetDbConfig; -use junobuild_auth::state::types::interface::SetAuthenticationConfig; pub use sdk::core::*; pub use sdk::internal; + // --------------------------------------------------------- // Init and Upgrade // --------------------------------------------------------- @@ -548,20 +549,20 @@ pub fn memory_size() -> MemorySize { macro_rules! include_satellite { () => { use junobuild_satellite::{ - authenticate, commit_asset_upload, commit_proposal, commit_proposal_asset_upload, - commit_proposal_many_assets_upload, count_assets, count_collection_assets, - count_collection_docs, count_docs, count_proposals, del_asset, del_assets, - del_controllers, del_custom_domain, del_doc, del_docs, del_filtered_assets, - del_filtered_docs, del_many_assets, del_many_docs, del_rule, delete_proposal_assets, - deposit_cycles, get_asset, get_auth_config, get_config, get_db_config, get_delegation, - get_doc, get_many_assets, get_many_docs, get_proposal, get_storage_config, - http_request, http_request_streaming_callback, init, init_asset_upload, init_proposal, - init_proposal_asset_upload, init_proposal_many_assets_upload, list_assets, - list_controllers, list_custom_domains, list_docs, list_proposals, list_rules, - post_upgrade, pre_upgrade, reject_proposal, set_asset_token, set_auth_config, - set_controllers, set_custom_domain, set_db_config, set_doc, set_many_docs, set_rule, - set_storage_config, submit_proposal, switch_storage_system_memory, upload_asset_chunk, - upload_proposal_asset_chunk, authenticate_controller, + authenticate, authenticate_controller, authenticate_controller, commit_asset_upload, + commit_proposal, commit_proposal_asset_upload, commit_proposal_many_assets_upload, + count_assets, count_collection_assets, count_collection_docs, count_docs, + count_proposals, del_asset, del_assets, del_controllers, del_custom_domain, del_doc, + del_docs, del_filtered_assets, del_filtered_docs, del_many_assets, del_many_docs, + del_rule, delete_proposal_assets, deposit_cycles, get_asset, get_auth_config, + get_config, get_db_config, get_delegation, get_doc, get_many_assets, get_many_docs, + get_proposal, get_storage_config, http_request, http_request_streaming_callback, init, + init_asset_upload, init_proposal, init_proposal_asset_upload, + init_proposal_many_assets_upload, list_assets, list_controllers, list_custom_domains, + list_docs, list_proposals, list_rules, post_upgrade, pre_upgrade, reject_proposal, + set_asset_token, set_auth_config, set_controllers, set_custom_domain, set_db_config, + set_doc, set_many_docs, set_rule, set_storage_config, submit_proposal, + switch_storage_system_memory, upload_asset_chunk, upload_proposal_asset_chunk, }; ic_cdk::export_candid!(); From 46fb0aa638e980f38dfe40f4e1f8a98438712d0c Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 29 Jan 2026 06:28:52 +0100 Subject: [PATCH 20/32] chore: remove unused impl --- src/libs/auth/src/openid/delegation/impls.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/libs/auth/src/openid/delegation/impls.rs b/src/libs/auth/src/openid/delegation/impls.rs index 26a63fb684..b7678db777 100644 --- a/src/libs/auth/src/openid/delegation/impls.rs +++ b/src/libs/auth/src/openid/delegation/impls.rs @@ -29,21 +29,6 @@ impl<'a> From<&'a OpenIdCredential> for OpenIdCredentialKey<'a> { } } -impl TryFrom<&OpenIdProvider> for OpenIdDelegationProvider { - type Error = String; - - fn try_from(provider: &OpenIdProvider) -> Result { - match provider { - OpenIdProvider::Google => Ok(OpenIdDelegationProvider::Google), - OpenIdProvider::GitHubAuth => Ok(OpenIdDelegationProvider::GitHub), - _ => Err(format!( - "{:?} is not supported for user authentication", - provider - )), - } - } -} - impl From<&OpenIdDelegationProvider> for OpenIdProvider { fn from(delegation_provider: &OpenIdDelegationProvider) -> Self { match delegation_provider { From 5f193fc10b7d23145b2bd46cf6efbfb1e28b8c89 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 29 Jan 2026 06:30:56 +0100 Subject: [PATCH 21/32] feat: rename --- src/libs/auth/src/openid/delegation/verify.rs | 14 +++++------ src/libs/auth/src/openid/jwt/provider.rs | 24 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/libs/auth/src/openid/delegation/verify.rs b/src/libs/auth/src/openid/delegation/verify.rs index 1078079e4d..7d60a487a1 100644 --- a/src/libs/auth/src/openid/delegation/verify.rs +++ b/src/libs/auth/src/openid/delegation/verify.rs @@ -5,7 +5,7 @@ use crate::openid::jwkset::{get_jwks, get_or_refresh_jwks}; use crate::openid::jwt::types::cert::Jwks; use crate::openid::jwt::types::errors::JwtVerifyError; use crate::openid::jwt::types::token::Claims; -use crate::openid::jwt::{unsafe_find_jwt_auth_provider, verify_openid_jwt}; +use crate::openid::jwt::{unsafe_find_jwt_delegation_provider, verify_openid_jwt}; use crate::openid::types::provider::OpenIdProvider; use crate::openid::utils::build_nonce; use crate::state::types::config::{OpenIdAuthProviderClientId, OpenIdAuthProviders}; @@ -21,16 +21,16 @@ pub async fn verify_openid_credentials_with_jwks_renewal( providers: &OpenIdAuthProviders, auth_heap: &impl AuthHeapStrategy, ) -> VerifyOpenIdDelegationCredentialsResult { - let (auth_provider, config) = unsafe_find_jwt_auth_provider(providers, jwt) + let (delegation_provider, config) = unsafe_find_jwt_delegation_provider(providers, jwt) .map_err(VerifyOpenidCredentialsError::JwtFindProvider)?; - let provider: OpenIdProvider = (&auth_provider).into(); + let provider: OpenIdProvider = (&delegation_provider).into(); let jwks = get_or_refresh_jwks(&provider, jwt, auth_heap) .await .map_err(VerifyOpenidCredentialsError::GetOrFetchJwks)?; - verify_openid_credentials(jwt, &jwks, &auth_provider, &config.client_id, salt) + verify_openid_credentials(jwt, &jwks, &delegation_provider, &config.client_id, salt) } pub fn verify_openid_credentials_with_cached_jwks( @@ -39,14 +39,14 @@ pub fn verify_openid_credentials_with_cached_jwks( providers: &OpenIdAuthProviders, auth_heap: &impl AuthHeapStrategy, ) -> VerifyOpenIdDelegationCredentialsResult { - let (auth_provider, config) = unsafe_find_jwt_auth_provider(providers, jwt) + let (delegation_provider, config) = unsafe_find_jwt_delegation_provider(providers, jwt) .map_err(VerifyOpenidCredentialsError::JwtFindProvider)?; - let provider: OpenIdProvider = (&auth_provider).into(); + let provider: OpenIdProvider = (&delegation_provider).into(); let jwks = get_jwks(&provider, auth_heap).ok_or(VerifyOpenidCredentialsError::GetCachedJwks)?; - verify_openid_credentials(jwt, &jwks, &auth_provider, &config.client_id, salt) + verify_openid_credentials(jwt, &jwks, &delegation_provider, &config.client_id, salt) } fn verify_openid_credentials( diff --git a/src/libs/auth/src/openid/jwt/provider.rs b/src/libs/auth/src/openid/jwt/provider.rs index eef8c1bb12..8f5e2e7332 100644 --- a/src/libs/auth/src/openid/jwt/provider.rs +++ b/src/libs/auth/src/openid/jwt/provider.rs @@ -7,7 +7,7 @@ use jsonwebtoken::dangerous; /// ⚠️ **Warning:** This function decodes the JWT payload *without verifying its signature*. /// Use only to inspect claims (e.g., `iss`) before performing a verified decode. -pub fn unsafe_find_jwt_auth_provider<'a>( +pub fn unsafe_find_jwt_delegation_provider<'a>( providers: &'a OpenIdAuthProviders, jwt: &str, ) -> Result<(OpenIdDelegationProvider, &'a OpenIdAuthProviderConfig), JwtFindProviderError> { @@ -33,7 +33,7 @@ pub fn unsafe_find_jwt_auth_provider<'a>( #[cfg(test)] mod tests { - use super::unsafe_find_jwt_auth_provider; + use super::unsafe_find_jwt_delegation_provider; use crate::openid::delegation::types::provider::OpenIdDelegationProvider; use crate::openid::jwt::types::errors::JwtFindProviderError; use crate::state::types::config::{OpenIdAuthProviderConfig, OpenIdAuthProviders}; @@ -71,7 +71,7 @@ mod tests { let provs = providers_with_google(); let (provider, cfg) = - unsafe_find_jwt_auth_provider(&provs, &jwt).expect("should match provider"); + unsafe_find_jwt_delegation_provider(&provs, &jwt).expect("should match provider"); assert_eq!(provider, OpenIdDelegationProvider::Google); assert_eq!(cfg.client_id, "client-123"); @@ -83,8 +83,8 @@ mod tests { let jwt = jwt_with(json!({"alg":"RS256"}), json!({"iss": iss})); let provs = providers_with_google(); - let (provider, _) = - unsafe_find_jwt_auth_provider(&provs, &jwt).expect("should match even without typ"); + let (provider, _) = unsafe_find_jwt_delegation_provider(&provs, &jwt) + .expect("should match even without typ"); assert_eq!(provider, OpenIdDelegationProvider::Google); } @@ -94,7 +94,7 @@ mod tests { let jwt = jwt_with(json!({"alg":"HS256","typ":"JWT"}), json!({"iss": iss})); let provs = providers_with_google(); - let err = unsafe_find_jwt_auth_provider(&provs, &jwt).unwrap_err(); + let err = unsafe_find_jwt_delegation_provider(&provs, &jwt).unwrap_err(); match err { JwtFindProviderError::BadClaim(f) => assert_eq!(f, "alg"), @@ -111,7 +111,7 @@ mod tests { ); let provs = providers_with_google(); - let err = unsafe_find_jwt_auth_provider(&provs, &jwt).unwrap_err(); + let err = unsafe_find_jwt_delegation_provider(&provs, &jwt).unwrap_err(); match err { JwtFindProviderError::BadClaim(f) => assert_eq!(f, "typ"), @@ -123,7 +123,7 @@ mod tests { fn returns_no_matching_provider_when_issuer_missing() { let jwt = jwt_with(json!({"alg":"RS256","typ":"JWT"}), json!({})); let provs = providers_with_google(); - let err = unsafe_find_jwt_auth_provider(&provs, &jwt).unwrap_err(); + let err = unsafe_find_jwt_delegation_provider(&provs, &jwt).unwrap_err(); assert!(matches!(err, JwtFindProviderError::NoMatchingProvider)); } @@ -134,7 +134,7 @@ mod tests { json!({"iss":"https://unknown.example.com"}), ); let provs = providers_with_google(); - let err = unsafe_find_jwt_auth_provider(&provs, &jwt).unwrap_err(); + let err = unsafe_find_jwt_delegation_provider(&provs, &jwt).unwrap_err(); assert!(matches!(err, JwtFindProviderError::NoMatchingProvider)); } @@ -142,7 +142,7 @@ mod tests { fn malformed_token_is_badsig() { let jwt = "definitely-not-a-jwt"; let provs = providers_with_google(); - let err = unsafe_find_jwt_auth_provider(&provs, jwt).unwrap_err(); + let err = unsafe_find_jwt_delegation_provider(&provs, jwt).unwrap_err(); assert!(matches!(err, JwtFindProviderError::BadSig(_))); } @@ -155,7 +155,7 @@ mod tests { let jwt = format!("{h}.{p}.{s}"); let provs = providers_with_google(); - let err = unsafe_find_jwt_auth_provider(&provs, &jwt).unwrap_err(); + let err = unsafe_find_jwt_delegation_provider(&provs, &jwt).unwrap_err(); assert!(matches!(err, JwtFindProviderError::BadSig(_))); } @@ -163,7 +163,7 @@ mod tests { fn empty_iss_is_no_match() { let jwt = jwt_with(json!({"alg":"RS256","typ":"JWT"}), json!({"iss": ""})); let provs = providers_with_google(); - let err = unsafe_find_jwt_auth_provider(&provs, &jwt).unwrap_err(); + let err = unsafe_find_jwt_delegation_provider(&provs, &jwt).unwrap_err(); assert!(matches!(err, JwtFindProviderError::NoMatchingProvider)); } } From 1a2f7b71dbfcaa03136e1fb6dc752c91edf9d02d Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 29 Jan 2026 07:26:13 +0100 Subject: [PATCH 22/32] refactor: move --- src/console/src/accounts/impls.rs | 2 +- src/console/src/auth/delegation.rs | 11 ++++++----- src/console/src/auth/register.rs | 4 ++-- src/console/src/types.rs | 2 +- src/libs/auth/src/delegation/get.rs | 6 ++++-- src/libs/auth/src/delegation/impls.rs | 2 +- src/libs/auth/src/delegation/prepare.rs | 6 ++++-- src/libs/auth/src/delegation/utils/duration.rs | 2 +- src/libs/auth/src/delegation/utils/seed.rs | 4 ++-- src/libs/auth/src/delegation/utils/targets.rs | 2 +- .../src/openid/{ => credentials}/delegation/impls.rs | 6 ++++-- .../src/openid/{ => credentials}/delegation/mod.rs | 0 .../src/openid/{ => credentials}/delegation/types.rs | 0 .../src/openid/{ => credentials}/delegation/verify.rs | 6 +++--- src/libs/auth/src/openid/credentials/mod.rs | 1 + src/libs/auth/src/openid/jwt/provider.rs | 4 ++-- src/libs/auth/src/openid/mod.rs | 2 +- src/libs/auth/src/state/types.rs | 2 +- src/libs/satellite/src/auth/delegation.rs | 11 ++++++----- src/libs/satellite/src/auth/register.rs | 4 ++-- src/libs/satellite/src/user/core/impls.rs | 4 ++-- 21 files changed, 45 insertions(+), 36 deletions(-) rename src/libs/auth/src/openid/{ => credentials}/delegation/impls.rs (89%) rename src/libs/auth/src/openid/{ => credentials}/delegation/mod.rs (100%) rename src/libs/auth/src/openid/{ => credentials}/delegation/types.rs (100%) rename src/libs/auth/src/openid/{ => credentials}/delegation/verify.rs (92%) create mode 100644 src/libs/auth/src/openid/credentials/mod.rs diff --git a/src/console/src/accounts/impls.rs b/src/console/src/accounts/impls.rs index 516dac591f..d258d59daa 100644 --- a/src/console/src/accounts/impls.rs +++ b/src/console/src/accounts/impls.rs @@ -1,7 +1,7 @@ use crate::constants::E8S_PER_ICP; use crate::types::state::{Account, OpenIdData, Provider}; use ic_cdk::api::time; -use junobuild_auth::openid::delegation::types::interface::OpenIdCredential; +use junobuild_auth::openid::credentials::delegation::types::interface::OpenIdCredential; use junobuild_auth::profile::types::OpenIdProfile; use junobuild_shared::types::state::{MissionControlId, UserId}; diff --git a/src/console/src/auth/delegation.rs b/src/console/src/auth/delegation.rs index b259366fce..c183f6254f 100644 --- a/src/console/src/auth/delegation.rs +++ b/src/console/src/auth/delegation.rs @@ -1,13 +1,14 @@ use crate::auth::strategy_impls::AuthHeap; use crate::certification::strategy_impls::AuthCertificate; +use junobuild_auth::delegation; use junobuild_auth::delegation::types::{ GetDelegationError, GetDelegationResult, OpenIdGetDelegationArgs, OpenIdPrepareDelegationArgs, PrepareDelegationError, PreparedDelegation, }; -use junobuild_auth::openid::delegation::types::interface::OpenIdCredential; -use junobuild_auth::openid::delegation::types::provider::OpenIdDelegationProvider; +use junobuild_auth::openid::credentials; +use junobuild_auth::openid::credentials::delegation::types::interface::OpenIdCredential; +use junobuild_auth::openid::credentials::delegation::types::provider::OpenIdDelegationProvider; use junobuild_auth::state::types::config::OpenIdAuthProviders; -use junobuild_auth::{delegation, openid}; pub type OpenIdPrepareDelegationResult = Result< ( @@ -23,7 +24,7 @@ pub async fn openid_prepare_delegation( providers: &OpenIdAuthProviders, ) -> OpenIdPrepareDelegationResult { let (credential, provider) = - match openid::delegation::verify_openid_credentials_with_jwks_renewal( + match credentials::delegation::verify_openid_credentials_with_jwks_renewal( &args.jwt, &args.salt, providers, &AuthHeap, ) .await @@ -48,7 +49,7 @@ pub fn openid_get_delegation( providers: &OpenIdAuthProviders, ) -> GetDelegationResult { let (credential, provider) = - match openid::delegation::verify_openid_credentials_with_cached_jwks( + match credentials::delegation::verify_openid_credentials_with_cached_jwks( &args.jwt, &args.salt, providers, &AuthHeap, ) { Ok(value) => value, diff --git a/src/console/src/auth/register.rs b/src/console/src/auth/register.rs index 4da81b00c6..613ff553fc 100644 --- a/src/console/src/auth/register.rs +++ b/src/console/src/auth/register.rs @@ -3,8 +3,8 @@ use crate::types::state::OpenId; use crate::types::state::{Account, OpenIdData, Provider}; use candid::Principal; use junobuild_auth::delegation::types::UserKey; -use junobuild_auth::openid::delegation::types::interface::OpenIdCredential; -use junobuild_auth::openid::delegation::types::provider::OpenIdDelegationProvider; +use junobuild_auth::openid::credentials::delegation::types::interface::OpenIdCredential; +use junobuild_auth::openid::credentials::delegation::types::provider::OpenIdDelegationProvider; pub async fn register_account( public_key: &UserKey, diff --git a/src/console/src/types.rs b/src/console/src/types.rs index 70eb514de2..7be910ed89 100644 --- a/src/console/src/types.rs +++ b/src/console/src/types.rs @@ -4,7 +4,7 @@ pub mod state { use candid::CandidType; use ic_ledger_types::{BlockIndex, Tokens}; use ic_stable_structures::StableBTreeMap; - use junobuild_auth::openid::delegation::types::provider::OpenIdDelegationProvider; + use junobuild_auth::openid::credentials::delegation::types::provider::OpenIdDelegationProvider; use junobuild_auth::state::types::state::AuthenticationHeapState; use junobuild_cdn::proposals::{ProposalsStable, SegmentDeploymentVersion}; use junobuild_cdn::storage::{ProposalAssetsStable, ProposalContentChunksStable}; diff --git a/src/libs/auth/src/delegation/get.rs b/src/libs/auth/src/delegation/get.rs index 2117577734..561a116180 100644 --- a/src/libs/auth/src/delegation/get.rs +++ b/src/libs/auth/src/delegation/get.rs @@ -4,8 +4,10 @@ use crate::delegation::types::{ use crate::delegation::utils::seed::calculate_seed; use crate::delegation::utils::signature::{build_signature_inputs, build_signature_msg}; use crate::delegation::utils::targets::build_targets; -use crate::openid::delegation::types::interface::{OpenIdCredential, OpenIdCredentialKey}; -use crate::openid::delegation::types::provider::OpenIdDelegationProvider; +use crate::openid::credentials::delegation::types::interface::{ + OpenIdCredential, OpenIdCredentialKey, +}; +use crate::openid::credentials::delegation::types::provider::OpenIdDelegationProvider; use crate::state::get_salt; use crate::state::services::read_state; use crate::strategies::{AuthCertificateStrategy, AuthHeapStrategy}; diff --git a/src/libs/auth/src/delegation/impls.rs b/src/libs/auth/src/delegation/impls.rs index dbdfc2a9ec..fbb44d7a98 100644 --- a/src/libs/auth/src/delegation/impls.rs +++ b/src/libs/auth/src/delegation/impls.rs @@ -1,5 +1,5 @@ use crate::delegation::types::{GetDelegationError, PrepareDelegationError}; -use crate::openid::delegation::types::errors::VerifyOpenidCredentialsError; +use crate::openid::credentials::delegation::types::errors::VerifyOpenidCredentialsError; impl From for GetDelegationError { fn from(e: VerifyOpenidCredentialsError) -> Self { diff --git a/src/libs/auth/src/delegation/prepare.rs b/src/libs/auth/src/delegation/prepare.rs index 6889ba555f..295e66c018 100644 --- a/src/libs/auth/src/delegation/prepare.rs +++ b/src/libs/auth/src/delegation/prepare.rs @@ -6,8 +6,10 @@ use crate::delegation::utils::duration::build_expiration; use crate::delegation::utils::seed::calculate_seed; use crate::delegation::utils::signature::{build_signature_inputs, build_signature_msg}; use crate::delegation::utils::targets::build_targets; -use crate::openid::delegation::types::interface::{OpenIdCredential, OpenIdCredentialKey}; -use crate::openid::delegation::types::provider::OpenIdDelegationProvider; +use crate::openid::credentials::delegation::types::interface::{ + OpenIdCredential, OpenIdCredentialKey, +}; +use crate::openid::credentials::delegation::types::provider::OpenIdDelegationProvider; use crate::state::get_salt; use crate::state::services::mutate_state; use crate::strategies::{AuthCertificateStrategy, AuthHeapStrategy}; diff --git a/src/libs/auth/src/delegation/utils/duration.rs b/src/libs/auth/src/delegation/utils/duration.rs index 7a7ca03d3a..a922d9e1e8 100644 --- a/src/libs/auth/src/delegation/utils/duration.rs +++ b/src/libs/auth/src/delegation/utils/duration.rs @@ -1,5 +1,5 @@ use crate::delegation::constants::{DEFAULT_EXPIRATION_PERIOD_NS, MAX_EXPIRATION_PERIOD_NS}; -use crate::openid::delegation::types::provider::OpenIdDelegationProvider; +use crate::openid::credentials::delegation::types::provider::OpenIdDelegationProvider; use crate::state::get_config; use crate::strategies::AuthHeapStrategy; use ic_cdk::api::time; diff --git a/src/libs/auth/src/delegation/utils/seed.rs b/src/libs/auth/src/delegation/utils/seed.rs index 3911f4dbfc..967206c38d 100644 --- a/src/libs/auth/src/delegation/utils/seed.rs +++ b/src/libs/auth/src/delegation/utils/seed.rs @@ -1,4 +1,4 @@ -use crate::openid::delegation::types::interface::OpenIdCredentialKey; +use crate::openid::credentials::delegation::types::interface::OpenIdCredentialKey; use crate::state::types::state::Salt; use ic_certification::Hash; use sha2::{Digest, Sha256}; @@ -30,7 +30,7 @@ fn hash_bytes(value: impl AsRef<[u8]>) -> Hash { #[cfg(test)] mod tests { use super::calculate_seed; - use crate::openid::delegation::types::interface::OpenIdCredentialKey; + use crate::openid::credentials::delegation::types::interface::OpenIdCredentialKey; use crate::state::types::state::Salt; use ic_certification::Hash; use sha2::{Digest, Sha256}; diff --git a/src/libs/auth/src/delegation/utils/targets.rs b/src/libs/auth/src/delegation/utils/targets.rs index 7704204e0f..cb9f2c4097 100644 --- a/src/libs/auth/src/delegation/utils/targets.rs +++ b/src/libs/auth/src/delegation/utils/targets.rs @@ -1,5 +1,5 @@ use crate::delegation::types::DelegationTargets; -use crate::openid::delegation::types::provider::OpenIdDelegationProvider; +use crate::openid::credentials::delegation::types::provider::OpenIdDelegationProvider; use crate::state::get_config; use crate::strategies::AuthHeapStrategy; use junobuild_shared::ic::api::id; diff --git a/src/libs/auth/src/openid/delegation/impls.rs b/src/libs/auth/src/openid/credentials/delegation/impls.rs similarity index 89% rename from src/libs/auth/src/openid/delegation/impls.rs rename to src/libs/auth/src/openid/credentials/delegation/impls.rs index b7678db777..db004037db 100644 --- a/src/libs/auth/src/openid/delegation/impls.rs +++ b/src/libs/auth/src/openid/credentials/delegation/impls.rs @@ -1,5 +1,7 @@ -use crate::openid::delegation::types::interface::{OpenIdCredential, OpenIdCredentialKey}; -use crate::openid::delegation::types::provider::OpenIdDelegationProvider; +use crate::openid::credentials::delegation::types::interface::{ + OpenIdCredential, OpenIdCredentialKey, +}; +use crate::openid::credentials::delegation::types::provider::OpenIdDelegationProvider; use crate::openid::jwt::types::token::Claims; use crate::openid::types::provider::OpenIdProvider; use jsonwebtoken::TokenData; diff --git a/src/libs/auth/src/openid/delegation/mod.rs b/src/libs/auth/src/openid/credentials/delegation/mod.rs similarity index 100% rename from src/libs/auth/src/openid/delegation/mod.rs rename to src/libs/auth/src/openid/credentials/delegation/mod.rs diff --git a/src/libs/auth/src/openid/delegation/types.rs b/src/libs/auth/src/openid/credentials/delegation/types.rs similarity index 100% rename from src/libs/auth/src/openid/delegation/types.rs rename to src/libs/auth/src/openid/credentials/delegation/types.rs diff --git a/src/libs/auth/src/openid/delegation/verify.rs b/src/libs/auth/src/openid/credentials/delegation/verify.rs similarity index 92% rename from src/libs/auth/src/openid/delegation/verify.rs rename to src/libs/auth/src/openid/credentials/delegation/verify.rs index 7d60a487a1..82d546b194 100644 --- a/src/libs/auth/src/openid/delegation/verify.rs +++ b/src/libs/auth/src/openid/credentials/delegation/verify.rs @@ -1,6 +1,6 @@ -use crate::openid::delegation::types::errors::VerifyOpenidCredentialsError; -use crate::openid::delegation::types::interface::OpenIdCredential; -use crate::openid::delegation::types::provider::OpenIdDelegationProvider; +use crate::openid::credentials::delegation::types::errors::VerifyOpenidCredentialsError; +use crate::openid::credentials::delegation::types::interface::OpenIdCredential; +use crate::openid::credentials::delegation::types::provider::OpenIdDelegationProvider; use crate::openid::jwkset::{get_jwks, get_or_refresh_jwks}; use crate::openid::jwt::types::cert::Jwks; use crate::openid::jwt::types::errors::JwtVerifyError; diff --git a/src/libs/auth/src/openid/credentials/mod.rs b/src/libs/auth/src/openid/credentials/mod.rs new file mode 100644 index 0000000000..94f5b8fd92 --- /dev/null +++ b/src/libs/auth/src/openid/credentials/mod.rs @@ -0,0 +1 @@ +pub mod delegation; diff --git a/src/libs/auth/src/openid/jwt/provider.rs b/src/libs/auth/src/openid/jwt/provider.rs index 8f5e2e7332..b57e6a5ae9 100644 --- a/src/libs/auth/src/openid/jwt/provider.rs +++ b/src/libs/auth/src/openid/jwt/provider.rs @@ -1,4 +1,4 @@ -use crate::openid::delegation::types::provider::OpenIdDelegationProvider; +use crate::openid::credentials::delegation::types::provider::OpenIdDelegationProvider; use crate::openid::jwt::header::decode_jwt_header; use crate::openid::jwt::types::errors::JwtFindProviderError; use crate::openid::jwt::types::token::UnsafeClaims; @@ -34,7 +34,7 @@ pub fn unsafe_find_jwt_delegation_provider<'a>( #[cfg(test)] mod tests { use super::unsafe_find_jwt_delegation_provider; - use crate::openid::delegation::types::provider::OpenIdDelegationProvider; + use crate::openid::credentials::delegation::types::provider::OpenIdDelegationProvider; use crate::openid::jwt::types::errors::JwtFindProviderError; use crate::state::types::config::{OpenIdAuthProviderConfig, OpenIdAuthProviders}; use base64::engine::general_purpose::URL_SAFE_NO_PAD; diff --git a/src/libs/auth/src/openid/mod.rs b/src/libs/auth/src/openid/mod.rs index e962e11ab5..86389ac488 100644 --- a/src/libs/auth/src/openid/mod.rs +++ b/src/libs/auth/src/openid/mod.rs @@ -1,5 +1,5 @@ pub mod automation; -pub mod delegation; +pub mod credentials; mod impls; pub mod jwkset; pub mod jwt; diff --git a/src/libs/auth/src/state/types.rs b/src/libs/auth/src/state/types.rs index 303be1689a..05664fd8eb 100644 --- a/src/libs/auth/src/state/types.rs +++ b/src/libs/auth/src/state/types.rs @@ -53,7 +53,7 @@ pub(crate) mod runtime_state { pub mod config { use crate::delegation::types::DelegationTargets; - use crate::openid::delegation::types::provider::OpenIdDelegationProvider; + use crate::openid::credentials::delegation::types::provider::OpenIdDelegationProvider; use candid::{CandidType, Deserialize, Principal}; use junobuild_shared::types::core::DomainName; use junobuild_shared::types::state::{Timestamp, Version}; diff --git a/src/libs/satellite/src/auth/delegation.rs b/src/libs/satellite/src/auth/delegation.rs index b259366fce..c183f6254f 100644 --- a/src/libs/satellite/src/auth/delegation.rs +++ b/src/libs/satellite/src/auth/delegation.rs @@ -1,13 +1,14 @@ use crate::auth::strategy_impls::AuthHeap; use crate::certification::strategy_impls::AuthCertificate; +use junobuild_auth::delegation; use junobuild_auth::delegation::types::{ GetDelegationError, GetDelegationResult, OpenIdGetDelegationArgs, OpenIdPrepareDelegationArgs, PrepareDelegationError, PreparedDelegation, }; -use junobuild_auth::openid::delegation::types::interface::OpenIdCredential; -use junobuild_auth::openid::delegation::types::provider::OpenIdDelegationProvider; +use junobuild_auth::openid::credentials; +use junobuild_auth::openid::credentials::delegation::types::interface::OpenIdCredential; +use junobuild_auth::openid::credentials::delegation::types::provider::OpenIdDelegationProvider; use junobuild_auth::state::types::config::OpenIdAuthProviders; -use junobuild_auth::{delegation, openid}; pub type OpenIdPrepareDelegationResult = Result< ( @@ -23,7 +24,7 @@ pub async fn openid_prepare_delegation( providers: &OpenIdAuthProviders, ) -> OpenIdPrepareDelegationResult { let (credential, provider) = - match openid::delegation::verify_openid_credentials_with_jwks_renewal( + match credentials::delegation::verify_openid_credentials_with_jwks_renewal( &args.jwt, &args.salt, providers, &AuthHeap, ) .await @@ -48,7 +49,7 @@ pub fn openid_get_delegation( providers: &OpenIdAuthProviders, ) -> GetDelegationResult { let (credential, provider) = - match openid::delegation::verify_openid_credentials_with_cached_jwks( + match credentials::delegation::verify_openid_credentials_with_cached_jwks( &args.jwt, &args.salt, providers, &AuthHeap, ) { Ok(value) => value, diff --git a/src/libs/satellite/src/auth/register.rs b/src/libs/satellite/src/auth/register.rs index 2e1742ec76..5ba589dda8 100644 --- a/src/libs/satellite/src/auth/register.rs +++ b/src/libs/satellite/src/auth/register.rs @@ -7,8 +7,8 @@ use crate::user::core::types::state::{OpenIdData, ProviderData, UserData}; use crate::Doc; use candid::Principal; use junobuild_auth::delegation::types::UserKey; -use junobuild_auth::openid::delegation::types::interface::OpenIdCredential; -use junobuild_auth::openid::delegation::types::provider::OpenIdDelegationProvider; +use junobuild_auth::openid::credentials::delegation::types::interface::OpenIdCredential; +use junobuild_auth::openid::credentials::delegation::types::provider::OpenIdDelegationProvider; use junobuild_collections::constants::db::COLLECTION_USER_KEY; use junobuild_collections::msg::msg_db_collection_not_found; use junobuild_shared::ic::api::id; diff --git a/src/libs/satellite/src/user/core/impls.rs b/src/libs/satellite/src/user/core/impls.rs index af023a0237..db7e322b3c 100644 --- a/src/libs/satellite/src/user/core/impls.rs +++ b/src/libs/satellite/src/user/core/impls.rs @@ -9,8 +9,8 @@ use crate::user::core::types::state::{ AuthProvider, OpenIdData, ProviderData, UserData, WebAuthnData, }; use crate::{Doc, SetDoc}; -use junobuild_auth::openid::delegation::types::interface::OpenIdCredential; -use junobuild_auth::openid::delegation::types::provider::OpenIdDelegationProvider; +use junobuild_auth::openid::credentials::delegation::types::interface::OpenIdCredential; +use junobuild_auth::openid::credentials::delegation::types::provider::OpenIdDelegationProvider; use junobuild_auth::profile::types::{OpenIdProfile, Validated}; use junobuild_utils::encode_doc_data; From 648538b8b7d2bc92a61af1dfec98419eb30ddf53 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 29 Jan 2026 07:27:40 +0100 Subject: [PATCH 23/32] refactor: move automation --- src/libs/auth/src/openid/{ => credentials}/automation/mod.rs | 0 .../auth/src/openid/{ => credentials}/automation/types.rs | 0 .../auth/src/openid/{ => credentials}/automation/verify.rs | 2 +- src/libs/auth/src/openid/credentials/delegation/verify.rs | 1 - src/libs/auth/src/openid/credentials/mod.rs | 1 + src/libs/auth/src/openid/mod.rs | 1 - src/libs/satellite/src/controllers/authenticate.rs | 4 ++-- src/libs/satellite/src/controllers/types.rs | 2 +- 8 files changed, 5 insertions(+), 6 deletions(-) rename src/libs/auth/src/openid/{ => credentials}/automation/mod.rs (100%) rename src/libs/auth/src/openid/{ => credentials}/automation/types.rs (100%) rename src/libs/auth/src/openid/{ => credentials}/automation/verify.rs (94%) diff --git a/src/libs/auth/src/openid/automation/mod.rs b/src/libs/auth/src/openid/credentials/automation/mod.rs similarity index 100% rename from src/libs/auth/src/openid/automation/mod.rs rename to src/libs/auth/src/openid/credentials/automation/mod.rs diff --git a/src/libs/auth/src/openid/automation/types.rs b/src/libs/auth/src/openid/credentials/automation/types.rs similarity index 100% rename from src/libs/auth/src/openid/automation/types.rs rename to src/libs/auth/src/openid/credentials/automation/types.rs diff --git a/src/libs/auth/src/openid/automation/verify.rs b/src/libs/auth/src/openid/credentials/automation/verify.rs similarity index 94% rename from src/libs/auth/src/openid/automation/verify.rs rename to src/libs/auth/src/openid/credentials/automation/verify.rs index 20236f718c..146c2250cd 100644 --- a/src/libs/auth/src/openid/automation/verify.rs +++ b/src/libs/auth/src/openid/credentials/automation/verify.rs @@ -1,4 +1,4 @@ -use crate::openid::automation::types::errors::VerifyOpenidAutomationCredentialsError; +use crate::openid::credentials::automation::types::errors::VerifyOpenidAutomationCredentialsError; use crate::openid::jwkset::get_or_refresh_jwks; use crate::openid::jwt::types::cert::Jwks; use crate::openid::jwt::types::errors::JwtVerifyError; diff --git a/src/libs/auth/src/openid/credentials/delegation/verify.rs b/src/libs/auth/src/openid/credentials/delegation/verify.rs index 5e2a1f6323..82d546b194 100644 --- a/src/libs/auth/src/openid/credentials/delegation/verify.rs +++ b/src/libs/auth/src/openid/credentials/delegation/verify.rs @@ -12,7 +12,6 @@ use crate::state::types::config::{OpenIdAuthProviderClientId, OpenIdAuthProvider use crate::state::types::state::Salt; use crate::strategies::AuthHeapStrategy; - type VerifyOpenIdDelegationCredentialsResult = Result<(OpenIdCredential, OpenIdDelegationProvider), VerifyOpenidCredentialsError>; diff --git a/src/libs/auth/src/openid/credentials/mod.rs b/src/libs/auth/src/openid/credentials/mod.rs index 94f5b8fd92..d766412ebc 100644 --- a/src/libs/auth/src/openid/credentials/mod.rs +++ b/src/libs/auth/src/openid/credentials/mod.rs @@ -1 +1,2 @@ pub mod delegation; +pub mod automation; diff --git a/src/libs/auth/src/openid/mod.rs b/src/libs/auth/src/openid/mod.rs index 86389ac488..74d28a01c7 100644 --- a/src/libs/auth/src/openid/mod.rs +++ b/src/libs/auth/src/openid/mod.rs @@ -1,4 +1,3 @@ -pub mod automation; pub mod credentials; mod impls; pub mod jwkset; diff --git a/src/libs/satellite/src/controllers/authenticate.rs b/src/libs/satellite/src/controllers/authenticate.rs index 0b4e8e8af1..cf2f49db1f 100644 --- a/src/libs/satellite/src/controllers/authenticate.rs +++ b/src/libs/satellite/src/controllers/authenticate.rs @@ -5,17 +5,17 @@ use crate::controllers::types::{ AuthenticateControllerResult, AuthenticationControllerError, OpenIdAuthenticateControllerArgs, }; use ic_cdk::api::time; -use junobuild_auth::openid; use junobuild_auth::openid::types::provider::OpenIdProvider; use junobuild_shared::segments::controllers::assert_controllers; use junobuild_shared::types::interface::SetController; use junobuild_shared::types::state::ControllerId; use std::cmp::min; +use junobuild_auth::openid::credentials; pub async fn openid_authenticate_controller( args: &OpenIdAuthenticateControllerArgs, ) -> AuthenticateControllerResult { - match openid::automation::verify_openid_credentials_with_jwks_renewal( + match credentials::automation::verify_openid_credentials_with_jwks_renewal( &args.jwt, &OpenIdProvider::GitHubActions, &AuthHeap, diff --git a/src/libs/satellite/src/controllers/types.rs b/src/libs/satellite/src/controllers/types.rs index 5f8c4def76..b19fb3667c 100644 --- a/src/libs/satellite/src/controllers/types.rs +++ b/src/libs/satellite/src/controllers/types.rs @@ -1,5 +1,5 @@ use candid::{CandidType, Deserialize}; -use junobuild_auth::openid::automation::types::errors::VerifyOpenidAutomationCredentialsError; +use junobuild_auth::openid::credentials::automation::types::errors::VerifyOpenidAutomationCredentialsError; use junobuild_shared::types::state::{ControllerId, Metadata}; use serde::Serialize; From 755e0b8c35d7c747ebbf5ae67709d526c6d6ae85 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 29 Jan 2026 09:25:02 +0100 Subject: [PATCH 24/32] feat: automation config --- src/libs/auth/src/openid/impls.rs | 150 +++++++++++++++++++++++++++++- src/libs/auth/src/openid/types.rs | 7 ++ src/libs/auth/src/state/types.rs | 53 +++++++++++ 3 files changed, 209 insertions(+), 1 deletion(-) diff --git a/src/libs/auth/src/openid/impls.rs b/src/libs/auth/src/openid/impls.rs index 215f4d5b60..9b17246913 100644 --- a/src/libs/auth/src/openid/impls.rs +++ b/src/libs/auth/src/openid/impls.rs @@ -1,5 +1,5 @@ use crate::openid::jwt::types::cert::Jwks; -use crate::openid::types::provider::{OpenIdCertificate, OpenIdDelegationProvider, OpenIdProvider}; +use crate::openid::types::provider::{OpenIdAutomationProvider, OpenIdCertificate, OpenIdDelegationProvider, OpenIdProvider}; use ic_cdk::api::time; use junobuild_shared::data::version::next_version; use junobuild_shared::types::state::{Version, Versioned}; @@ -50,6 +50,28 @@ impl OpenIdDelegationProvider { } } +impl From<&OpenIdAutomationProvider> for OpenIdProvider { + fn from(automation_provider: &OpenIdAutomationProvider) -> Self { + match automation_provider { + OpenIdAutomationProvider::GitHub => OpenIdProvider::GitHubActions, + } + } +} + +impl OpenIdAutomationProvider { + pub fn jwks_url(&self) -> &'static str { + match self { + Self::GitHub => OpenIdProvider::GitHubActions.jwks_url(), + } + } + + pub fn issuers(&self) -> &[&'static str] { + match self { + Self::GitHub => OpenIdProvider::GitHubActions.issuers(), + } + } +} + impl Versioned for OpenIdCertificate { fn version(&self) -> Option { self.version @@ -97,3 +119,129 @@ impl Display for OpenIdProvider { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_openid_provider_jwks_urls() { + assert_eq!( + OpenIdProvider::Google.jwks_url(), + "https://www.googleapis.com/oauth2/v3/certs" + ); + assert_eq!( + OpenIdProvider::GitHubAuth.jwks_url(), + "https://api.juno.build/v1/auth/certs" + ); + assert_eq!( + OpenIdProvider::GitHubActions.jwks_url(), + "https://token.actions.githubusercontent.com/.well-known/jwks" + ); + } + + #[test] + fn test_openid_provider_issuers() { + assert_eq!( + OpenIdProvider::Google.issuers(), + &["https://accounts.google.com", "accounts.google.com"] + ); + assert_eq!( + OpenIdProvider::GitHubAuth.issuers(), + &["https://api.juno.build/auth/github"] + ); + assert_eq!( + OpenIdProvider::GitHubActions.issuers(), + &["https://token.actions.githubusercontent.com"] + ); + } + + #[test] + fn test_delegation_provider_to_openid_provider() { + assert_eq!( + OpenIdProvider::from(&OpenIdDelegationProvider::Google), + OpenIdProvider::Google + ); + assert_eq!( + OpenIdProvider::from(&OpenIdDelegationProvider::GitHub), + OpenIdProvider::GitHubAuth + ); + } + + #[test] + fn test_delegation_provider_jwks_urls() { + assert_eq!( + OpenIdDelegationProvider::Google.jwks_url(), + "https://www.googleapis.com/oauth2/v3/certs" + ); + assert_eq!( + OpenIdDelegationProvider::GitHub.jwks_url(), + "https://api.juno.build/v1/auth/certs" + ); + } + + #[test] + fn test_delegation_provider_issuers() { + assert_eq!( + OpenIdDelegationProvider::Google.issuers(), + &["https://accounts.google.com", "accounts.google.com"] + ); + assert_eq!( + OpenIdDelegationProvider::GitHub.issuers(), + &["https://api.juno.build/auth/github"] + ); + } + + #[test] + fn test_automation_provider_to_openid_provider() { + assert_eq!( + OpenIdProvider::from(&OpenIdAutomationProvider::GitHub), + OpenIdProvider::GitHubActions + ); + } + + #[test] + fn test_automation_provider_jwks_urls() { + assert_eq!( + OpenIdAutomationProvider::GitHub.jwks_url(), + "https://token.actions.githubusercontent.com/.well-known/jwks" + ); + } + + #[test] + fn test_automation_provider_issuers() { + assert_eq!( + OpenIdAutomationProvider::GitHub.issuers(), + &["https://token.actions.githubusercontent.com"] + ); + } + + #[test] + fn test_openid_certificate_init() { + let jwks = Jwks { keys: vec![] }; + let cert = OpenIdCertificate::init(&jwks); + + assert_eq!(cert.version, Some(1)); + assert_eq!(cert.created_at, cert.updated_at); + } + + #[test] + fn test_openid_certificate_update() { + let jwks = Jwks { keys: vec![] }; + let initial = OpenIdCertificate::init(&jwks); + + let new_jwks = Jwks { keys: vec![] }; + let updated = OpenIdCertificate::update(&initial, &new_jwks); + + assert_eq!(updated.version, Some(2)); + assert_eq!(updated.created_at, initial.created_at); + assert!(updated.updated_at >= initial.updated_at); + } + + #[test] + fn test_openid_provider_display() { + assert_eq!(format!("{}", OpenIdProvider::Google), "Google"); + assert_eq!(format!("{}", OpenIdProvider::GitHubAuth), "GitHub Proxy"); + assert_eq!(format!("{}", OpenIdProvider::GitHubActions), "GitHub Actions"); + } +} \ No newline at end of file diff --git a/src/libs/auth/src/openid/types.rs b/src/libs/auth/src/openid/types.rs index 35b6b6f238..86606e1a7a 100644 --- a/src/libs/auth/src/openid/types.rs +++ b/src/libs/auth/src/openid/types.rs @@ -21,6 +21,13 @@ pub mod provider { GitHub, } + #[derive( + CandidType, Serialize, Deserialize, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug, + )] + pub enum OpenIdAutomationProvider { + GitHub, + } + #[derive(CandidType, Serialize, Deserialize, Clone)] pub struct OpenIdCertificate { pub jwks: Jwks, diff --git a/src/libs/auth/src/state/types.rs b/src/libs/auth/src/state/types.rs index ccae2a31c0..2e49eb58a3 100644 --- a/src/libs/auth/src/state/types.rs +++ b/src/libs/auth/src/state/types.rs @@ -5,12 +5,17 @@ pub mod state { use candid::CandidType; use serde::{Deserialize, Serialize}; use std::collections::HashMap; + use crate::state::types::automation::AutomationConfig; pub type Salt = [u8; 32]; #[derive(Default, CandidType, Serialize, Deserialize, Clone)] pub struct AuthenticationHeapState { + /// Configuration for user authentication via delegation (Internet Identity, Google, GitHub). + /// Note: Field name kept as "config" for backward compatibility during upgrades. pub config: AuthenticationConfig, + /// Configuration for CI/CD authentication. + pub automation: Option, pub salt: Option, pub openid: Option, } @@ -104,6 +109,54 @@ pub mod config { } } +pub mod automation { + use std::collections::{BTreeMap, HashMap}; + use candid::{CandidType, Deserialize, Principal}; + use serde::Serialize; + use junobuild_shared::types::state::{Timestamp, Version}; + use crate::openid::types::provider::{OpenIdAutomationProvider}; + + #[derive(Default, CandidType, Serialize, Deserialize, Clone)] + pub struct AutomationConfig { + pub openid: Option, + pub version: Option, + pub created_at: Option, + pub updated_at: Option, + } + + #[derive(Default, CandidType, Serialize, Deserialize, Clone)] + pub struct AutomationConfigOpenId { + pub providers: OpenIdAutomationProviders, + pub observatory_id: Option, + } + + pub type OpenIdAutomationProviders = BTreeMap; + + // Repository identifier for GitHub automation. + // Corresponds to the `repository` claim in GitHub OIDC tokens (e.g., "octo-org/octo-repo"). + // See: https://docs.github.com/en/actions/concepts/security/openid-connect#understanding-the-oidc-token + #[derive(CandidType, Serialize, Deserialize, Clone, Debug, Hash, Eq, PartialEq)] + pub struct RepositoryKey { + // Repository owner (e.g. "octo-org") + pub owner: String, + // Repository name (e.g. "octo-repo") + pub name: String, + } + + pub type OpenIdAutomationRepositories = HashMap; + + #[derive(Default, CandidType, Serialize, Deserialize, Clone, Debug)] + pub struct OpenIdAutomationProviderConfig { + pub repositories: OpenIdAutomationRepositories, + } + + #[derive(CandidType, Serialize, Deserialize, Clone, Debug)] + pub struct OpenIdAutomationRepositoryConfig { + // Optionally restrict to specific branches (e.g. ["main", "develop"]) + pub branches: Option>, + } +} + pub mod interface { use crate::state::types::config::{ AuthenticationConfigInternetIdentity, AuthenticationConfigOpenId, AuthenticationRules, From 4319cb0ab8e4e5471ad22c4b445149e2fb1935b9 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 29 Jan 2026 10:50:54 +0100 Subject: [PATCH 25/32] chore: fmt --- src/libs/auth/src/openid/impls.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/libs/auth/src/openid/impls.rs b/src/libs/auth/src/openid/impls.rs index 9b17246913..56d73fa8ad 100644 --- a/src/libs/auth/src/openid/impls.rs +++ b/src/libs/auth/src/openid/impls.rs @@ -1,5 +1,7 @@ use crate::openid::jwt::types::cert::Jwks; -use crate::openid::types::provider::{OpenIdAutomationProvider, OpenIdCertificate, OpenIdDelegationProvider, OpenIdProvider}; +use crate::openid::types::provider::{ + OpenIdAutomationProvider, OpenIdCertificate, OpenIdDelegationProvider, OpenIdProvider, +}; use ic_cdk::api::time; use junobuild_shared::data::version::next_version; use junobuild_shared::types::state::{Version, Versioned}; @@ -242,6 +244,9 @@ mod tests { fn test_openid_provider_display() { assert_eq!(format!("{}", OpenIdProvider::Google), "Google"); assert_eq!(format!("{}", OpenIdProvider::GitHubAuth), "GitHub Proxy"); - assert_eq!(format!("{}", OpenIdProvider::GitHubActions), "GitHub Actions"); + assert_eq!( + format!("{}", OpenIdProvider::GitHubActions), + "GitHub Actions" + ); } -} \ No newline at end of file +} From 3201b64a71abafb6da3f370951549a3936d9a64e Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 29 Jan 2026 11:06:34 +0100 Subject: [PATCH 26/32] feat: init automation to none --- src/libs/auth/src/state/heap.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/auth/src/state/heap.rs b/src/libs/auth/src/state/heap.rs index 18cba3e7ca..8dd29829ab 100644 --- a/src/libs/auth/src/state/heap.rs +++ b/src/libs/auth/src/state/heap.rs @@ -23,6 +23,7 @@ fn insert_config_impl(config: &AuthenticationConfig, state: &mut Option { *state = Some(AuthenticationHeapState { config: config.clone(), + automation: None, salt: None, openid: None, }) @@ -48,6 +49,7 @@ fn insert_salt_impl(salt: &Salt, state: &mut Option) { None => { *state = Some(AuthenticationHeapState { config: AuthenticationConfig::default(), + automation: None, salt: Some(*salt), openid: None, }) From 8487ecf249597661ff739c608a7192abc9bc25b4 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 29 Jan 2026 11:07:26 +0100 Subject: [PATCH 27/32] feat: make unsafe_find_jwt_provider generic again --- .../openid/credentials/delegation/verify.rs | 6 ++-- src/libs/auth/src/openid/impls.rs | 17 ++++++++-- src/libs/auth/src/openid/jwt/provider.rs | 33 ++++++++++--------- src/libs/auth/src/openid/jwt/types.rs | 6 ++++ 4 files changed, 41 insertions(+), 21 deletions(-) diff --git a/src/libs/auth/src/openid/credentials/delegation/verify.rs b/src/libs/auth/src/openid/credentials/delegation/verify.rs index 979232804d..2a585f3594 100644 --- a/src/libs/auth/src/openid/credentials/delegation/verify.rs +++ b/src/libs/auth/src/openid/credentials/delegation/verify.rs @@ -4,7 +4,7 @@ use crate::openid::jwkset::{get_jwks, get_or_refresh_jwks}; use crate::openid::jwt::types::cert::Jwks; use crate::openid::jwt::types::errors::JwtVerifyError; use crate::openid::jwt::types::token::Claims; -use crate::openid::jwt::{unsafe_find_jwt_delegation_provider, verify_openid_jwt}; +use crate::openid::jwt::{unsafe_find_jwt_provider, verify_openid_jwt}; use crate::openid::types::provider::OpenIdDelegationProvider; use crate::openid::types::provider::OpenIdProvider; use crate::openid::utils::build_nonce; @@ -21,7 +21,7 @@ pub async fn verify_openid_credentials_with_jwks_renewal( providers: &OpenIdAuthProviders, auth_heap: &impl AuthHeapStrategy, ) -> VerifyOpenIdDelegationCredentialsResult { - let (delegation_provider, config) = unsafe_find_jwt_delegation_provider(providers, jwt) + let (delegation_provider, config) = unsafe_find_jwt_provider(providers, jwt) .map_err(VerifyOpenidCredentialsError::JwtFindProvider)?; let provider: OpenIdProvider = (&delegation_provider).into(); @@ -39,7 +39,7 @@ pub fn verify_openid_credentials_with_cached_jwks( providers: &OpenIdAuthProviders, auth_heap: &impl AuthHeapStrategy, ) -> VerifyOpenIdDelegationCredentialsResult { - let (delegation_provider, config) = unsafe_find_jwt_delegation_provider(providers, jwt) + let (delegation_provider, config) = unsafe_find_jwt_provider(providers, jwt) .map_err(VerifyOpenidCredentialsError::JwtFindProvider)?; let provider: OpenIdProvider = (&delegation_provider).into(); diff --git a/src/libs/auth/src/openid/impls.rs b/src/libs/auth/src/openid/impls.rs index 56d73fa8ad..b76fcbdfca 100644 --- a/src/libs/auth/src/openid/impls.rs +++ b/src/libs/auth/src/openid/impls.rs @@ -1,11 +1,10 @@ use crate::openid::jwt::types::cert::Jwks; -use crate::openid::types::provider::{ - OpenIdAutomationProvider, OpenIdCertificate, OpenIdDelegationProvider, OpenIdProvider, -}; +use crate::openid::types::provider::{OpenIdAutomationProvider, OpenIdCertificate, OpenIdDelegationProvider, OpenIdProvider}; use ic_cdk::api::time; use junobuild_shared::data::version::next_version; use junobuild_shared::types::state::{Version, Versioned}; use std::fmt::{Display, Formatter, Result as FmtResult}; +use crate::openid::jwt::types::provider::JwtIssuers; impl OpenIdProvider { pub fn jwks_url(&self) -> &'static str { @@ -74,6 +73,18 @@ impl OpenIdAutomationProvider { } } +impl JwtIssuers for OpenIdDelegationProvider { + fn issuers(&self) -> &[&'static str] { + self.issuers() + } +} + +impl JwtIssuers for OpenIdAutomationProvider { + fn issuers(&self) -> &[&'static str] { + self.issuers() + } +} + impl Versioned for OpenIdCertificate { fn version(&self) -> Option { self.version diff --git a/src/libs/auth/src/openid/jwt/provider.rs b/src/libs/auth/src/openid/jwt/provider.rs index 360babd0c5..060157aaef 100644 --- a/src/libs/auth/src/openid/jwt/provider.rs +++ b/src/libs/auth/src/openid/jwt/provider.rs @@ -1,16 +1,19 @@ +use std::collections::BTreeMap; use crate::openid::jwt::header::decode_jwt_header; use crate::openid::jwt::types::errors::JwtFindProviderError; use crate::openid::jwt::types::token::UnsafeClaims; -use crate::openid::types::provider::OpenIdDelegationProvider; -use crate::state::types::config::{OpenIdAuthProviderConfig, OpenIdAuthProviders}; use jsonwebtoken::dangerous; +use crate::openid::jwt::types::provider::JwtIssuers; /// ⚠️ **Warning:** This function decodes the JWT payload *without verifying its signature*. /// Use only to inspect claims (e.g., `iss`) before performing a verified decode. -pub fn unsafe_find_jwt_delegation_provider<'a>( - providers: &'a OpenIdAuthProviders, +pub fn unsafe_find_jwt_provider<'a, Provider, Config>( + providers: &'a BTreeMap, jwt: &str, -) -> Result<(OpenIdDelegationProvider, &'a OpenIdAuthProviderConfig), JwtFindProviderError> { +) -> Result<(Provider, &'a Config), JwtFindProviderError> +where + Provider: Clone + JwtIssuers, +{ // 1) Header sanity check decode_jwt_header(jwt).map_err(JwtFindProviderError::from)?; @@ -33,7 +36,7 @@ pub fn unsafe_find_jwt_delegation_provider<'a>( #[cfg(test)] mod tests { - use super::unsafe_find_jwt_delegation_provider; + use super::unsafe_find_jwt_provider; use crate::openid::jwt::types::errors::JwtFindProviderError; use crate::openid::types::provider::OpenIdDelegationProvider; use crate::state::types::config::{OpenIdAuthProviderConfig, OpenIdAuthProviders}; @@ -71,7 +74,7 @@ mod tests { let provs = providers_with_google(); let (provider, cfg) = - unsafe_find_jwt_delegation_provider(&provs, &jwt).expect("should match provider"); + unsafe_find_jwt_provider(&provs, &jwt).expect("should match provider"); assert_eq!(provider, OpenIdDelegationProvider::Google); assert_eq!(cfg.client_id, "client-123"); @@ -83,7 +86,7 @@ mod tests { let jwt = jwt_with(json!({"alg":"RS256"}), json!({"iss": iss})); let provs = providers_with_google(); - let (provider, _) = unsafe_find_jwt_delegation_provider(&provs, &jwt) + let (provider, _) = unsafe_find_jwt_provider(&provs, &jwt) .expect("should match even without typ"); assert_eq!(provider, OpenIdDelegationProvider::Google); } @@ -94,7 +97,7 @@ mod tests { let jwt = jwt_with(json!({"alg":"HS256","typ":"JWT"}), json!({"iss": iss})); let provs = providers_with_google(); - let err = unsafe_find_jwt_delegation_provider(&provs, &jwt).unwrap_err(); + let err = unsafe_find_jwt_provider(&provs, &jwt).unwrap_err(); match err { JwtFindProviderError::BadClaim(f) => assert_eq!(f, "alg"), @@ -111,7 +114,7 @@ mod tests { ); let provs = providers_with_google(); - let err = unsafe_find_jwt_delegation_provider(&provs, &jwt).unwrap_err(); + let err = unsafe_find_jwt_provider(&provs, &jwt).unwrap_err(); match err { JwtFindProviderError::BadClaim(f) => assert_eq!(f, "typ"), @@ -123,7 +126,7 @@ mod tests { fn returns_no_matching_provider_when_issuer_missing() { let jwt = jwt_with(json!({"alg":"RS256","typ":"JWT"}), json!({})); let provs = providers_with_google(); - let err = unsafe_find_jwt_delegation_provider(&provs, &jwt).unwrap_err(); + let err = unsafe_find_jwt_provider(&provs, &jwt).unwrap_err(); assert!(matches!(err, JwtFindProviderError::NoMatchingProvider)); } @@ -134,7 +137,7 @@ mod tests { json!({"iss":"https://unknown.example.com"}), ); let provs = providers_with_google(); - let err = unsafe_find_jwt_delegation_provider(&provs, &jwt).unwrap_err(); + let err = unsafe_find_jwt_provider(&provs, &jwt).unwrap_err(); assert!(matches!(err, JwtFindProviderError::NoMatchingProvider)); } @@ -142,7 +145,7 @@ mod tests { fn malformed_token_is_badsig() { let jwt = "definitely-not-a-jwt"; let provs = providers_with_google(); - let err = unsafe_find_jwt_delegation_provider(&provs, jwt).unwrap_err(); + let err = unsafe_find_jwt_provider(&provs, jwt).unwrap_err(); assert!(matches!(err, JwtFindProviderError::BadSig(_))); } @@ -155,7 +158,7 @@ mod tests { let jwt = format!("{h}.{p}.{s}"); let provs = providers_with_google(); - let err = unsafe_find_jwt_delegation_provider(&provs, &jwt).unwrap_err(); + let err = unsafe_find_jwt_provider(&provs, &jwt).unwrap_err(); assert!(matches!(err, JwtFindProviderError::BadSig(_))); } @@ -163,7 +166,7 @@ mod tests { fn empty_iss_is_no_match() { let jwt = jwt_with(json!({"alg":"RS256","typ":"JWT"}), json!({"iss": ""})); let provs = providers_with_google(); - let err = unsafe_find_jwt_delegation_provider(&provs, &jwt).unwrap_err(); + let err = unsafe_find_jwt_provider(&provs, &jwt).unwrap_err(); assert!(matches!(err, JwtFindProviderError::NoMatchingProvider)); } } diff --git a/src/libs/auth/src/openid/jwt/types.rs b/src/libs/auth/src/openid/jwt/types.rs index 30baa34a83..dedcc4eb23 100644 --- a/src/libs/auth/src/openid/jwt/types.rs +++ b/src/libs/auth/src/openid/jwt/types.rs @@ -193,3 +193,9 @@ pub(crate) mod errors { BadClaim(String), } } + +pub mod provider { + pub trait JwtIssuers { + fn issuers(&self) -> &[&'static str]; + } +} \ No newline at end of file From 5f5dd948b36c978bba356d21bc3473cf8b0ac9b8 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 29 Jan 2026 11:09:17 +0100 Subject: [PATCH 28/32] chore: merge main --- src/libs/auth/src/openid/jwt/provider.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/auth/src/openid/jwt/provider.rs b/src/libs/auth/src/openid/jwt/provider.rs index 2a291ae389..060157aaef 100644 --- a/src/libs/auth/src/openid/jwt/provider.rs +++ b/src/libs/auth/src/openid/jwt/provider.rs @@ -34,7 +34,6 @@ where Err(JwtFindProviderError::NoMatchingProvider) } - #[cfg(test)] mod tests { use super::unsafe_find_jwt_provider; From 17524bd7a6b967cdd12ac62283c9ef10c8bfb02d Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 29 Jan 2026 11:13:15 +0100 Subject: [PATCH 29/32] feat: verify with providers --- src/libs/auth/src/delegation/impls.rs | 2 +- .../src/openid/credentials/automation/verify.rs | 16 +++++++++++----- .../src/openid/credentials/delegation/types.rs | 15 --------------- .../src/openid/credentials/delegation/verify.rs | 2 +- src/libs/auth/src/openid/credentials/mod.rs | 1 + .../openid/credentials/{automation => }/types.rs | 9 +++++---- .../satellite/src/controllers/authenticate.rs | 8 ++++++-- 7 files changed, 25 insertions(+), 28 deletions(-) rename src/libs/auth/src/openid/credentials/{automation => }/types.rs (60%) diff --git a/src/libs/auth/src/delegation/impls.rs b/src/libs/auth/src/delegation/impls.rs index fbb44d7a98..c2aee21bf2 100644 --- a/src/libs/auth/src/delegation/impls.rs +++ b/src/libs/auth/src/delegation/impls.rs @@ -1,5 +1,5 @@ use crate::delegation::types::{GetDelegationError, PrepareDelegationError}; -use crate::openid::credentials::delegation::types::errors::VerifyOpenidCredentialsError; +use crate::openid::credentials::types::errors::VerifyOpenidCredentialsError; impl From for GetDelegationError { fn from(e: VerifyOpenidCredentialsError) -> Self { diff --git a/src/libs/auth/src/openid/credentials/automation/verify.rs b/src/libs/auth/src/openid/credentials/automation/verify.rs index 146c2250cd..114611ace9 100644 --- a/src/libs/auth/src/openid/credentials/automation/verify.rs +++ b/src/libs/auth/src/openid/credentials/automation/verify.rs @@ -1,22 +1,28 @@ -use crate::openid::credentials::automation::types::errors::VerifyOpenidAutomationCredentialsError; +use crate::openid::credentials::types::errors::VerifyOpenidCredentialsError; use crate::openid::jwkset::get_or_refresh_jwks; use crate::openid::jwt::types::cert::Jwks; use crate::openid::jwt::types::errors::JwtVerifyError; use crate::openid::jwt::types::token::Claims; -use crate::openid::jwt::verify_openid_jwt; +use crate::openid::jwt::{unsafe_find_jwt_provider, verify_openid_jwt}; use crate::openid::types::provider::OpenIdProvider; +use crate::state::types::automation::OpenIdAutomationProviders; use crate::strategies::AuthHeapStrategy; -type VerifyOpenIdAutomationCredentialsResult = Result<(), VerifyOpenidAutomationCredentialsError>; +type VerifyOpenIdAutomationCredentialsResult = Result<(), VerifyOpenidCredentialsError>; pub async fn verify_openid_credentials_with_jwks_renewal( jwt: &str, - provider: &OpenIdProvider, + providers: &OpenIdAutomationProviders, auth_heap: &impl AuthHeapStrategy, ) -> VerifyOpenIdAutomationCredentialsResult { + let (automation_provider, config) = unsafe_find_jwt_provider(providers, jwt) + .map_err(VerifyOpenidCredentialsError::JwtFindProvider)?; + + let provider: OpenIdProvider = (&automation_provider).into(); + let jwks = get_or_refresh_jwks(&provider, jwt, auth_heap) .await - .map_err(VerifyOpenidAutomationCredentialsError::GetOrFetchJwks)?; + .map_err(VerifyOpenidCredentialsError::GetOrFetchJwks)?; verify_openid_credentials(jwt, &jwks, &provider) } diff --git a/src/libs/auth/src/openid/credentials/delegation/types.rs b/src/libs/auth/src/openid/credentials/delegation/types.rs index af0991b32c..375d7599ab 100644 --- a/src/libs/auth/src/openid/credentials/delegation/types.rs +++ b/src/libs/auth/src/openid/credentials/delegation/types.rs @@ -17,18 +17,3 @@ pub mod interface { pub locale: Option, } } - -pub(crate) mod errors { - use crate::openid::jwkset::types::errors::GetOrRefreshJwksError; - use crate::openid::jwt::types::errors::{JwtFindProviderError, JwtVerifyError}; - use candid::{CandidType, Deserialize}; - use serde::Serialize; - - #[derive(CandidType, Serialize, Deserialize, Debug)] - pub enum VerifyOpenidCredentialsError { - GetOrFetchJwks(GetOrRefreshJwksError), - GetCachedJwks, - JwtFindProvider(JwtFindProviderError), - JwtVerify(JwtVerifyError), - } -} diff --git a/src/libs/auth/src/openid/credentials/delegation/verify.rs b/src/libs/auth/src/openid/credentials/delegation/verify.rs index 2a585f3594..e4b72a0711 100644 --- a/src/libs/auth/src/openid/credentials/delegation/verify.rs +++ b/src/libs/auth/src/openid/credentials/delegation/verify.rs @@ -1,4 +1,4 @@ -use crate::openid::credentials::delegation::types::errors::VerifyOpenidCredentialsError; +use crate::openid::credentials::types::errors::VerifyOpenidCredentialsError; use crate::openid::credentials::delegation::types::interface::OpenIdCredential; use crate::openid::jwkset::{get_jwks, get_or_refresh_jwks}; use crate::openid::jwt::types::cert::Jwks; diff --git a/src/libs/auth/src/openid/credentials/mod.rs b/src/libs/auth/src/openid/credentials/mod.rs index d766412ebc..c4602919fd 100644 --- a/src/libs/auth/src/openid/credentials/mod.rs +++ b/src/libs/auth/src/openid/credentials/mod.rs @@ -1,2 +1,3 @@ pub mod delegation; pub mod automation; +pub mod types; diff --git a/src/libs/auth/src/openid/credentials/automation/types.rs b/src/libs/auth/src/openid/credentials/types.rs similarity index 60% rename from src/libs/auth/src/openid/credentials/automation/types.rs rename to src/libs/auth/src/openid/credentials/types.rs index 9fd4d03ff1..40f964e922 100644 --- a/src/libs/auth/src/openid/credentials/automation/types.rs +++ b/src/libs/auth/src/openid/credentials/types.rs @@ -1,13 +1,14 @@ -pub mod errors { +pub(crate) mod errors { use crate::openid::jwkset::types::errors::GetOrRefreshJwksError; - use crate::openid::jwt::types::errors::JwtVerifyError; + use crate::openid::jwt::types::errors::{JwtFindProviderError, JwtVerifyError}; use candid::{CandidType, Deserialize}; use serde::Serialize; #[derive(CandidType, Serialize, Deserialize, Debug)] - pub enum VerifyOpenidAutomationCredentialsError { + pub enum VerifyOpenidCredentialsError { GetOrFetchJwks(GetOrRefreshJwksError), GetCachedJwks, + JwtFindProvider(JwtFindProviderError), JwtVerify(JwtVerifyError), } -} +} \ No newline at end of file diff --git a/src/libs/satellite/src/controllers/authenticate.rs b/src/libs/satellite/src/controllers/authenticate.rs index cf2f49db1f..832486ebba 100644 --- a/src/libs/satellite/src/controllers/authenticate.rs +++ b/src/libs/satellite/src/controllers/authenticate.rs @@ -5,19 +5,23 @@ use crate::controllers::types::{ AuthenticateControllerResult, AuthenticationControllerError, OpenIdAuthenticateControllerArgs, }; use ic_cdk::api::time; -use junobuild_auth::openid::types::provider::OpenIdProvider; use junobuild_shared::segments::controllers::assert_controllers; use junobuild_shared::types::interface::SetController; use junobuild_shared::types::state::ControllerId; use std::cmp::min; use junobuild_auth::openid::credentials; +use junobuild_auth::state::get_providers; pub async fn openid_authenticate_controller( args: &OpenIdAuthenticateControllerArgs, + // TODO: Result> ) -> AuthenticateControllerResult { + // TODO: get_automation_providers + let providers = get_providers(&AuthHeap)?; + match credentials::automation::verify_openid_credentials_with_jwks_renewal( &args.jwt, - &OpenIdProvider::GitHubActions, + &providers, &AuthHeap, ) .await From cc8713e31920e3442c0aabc909d740e15f214249 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 29 Jan 2026 13:45:16 +0100 Subject: [PATCH 30/32] feat: rework for consistency and similar pattern plus integration in state --- .../src/automation}/constants.rs | 4 +- src/libs/auth/src/automation/impls.rs | 17 +++++ src/libs/auth/src/automation/mod.rs | 7 +++ src/libs/auth/src/automation/prepare.rs | 31 ++++++++++ src/libs/auth/src/automation/types.rs | 43 +++++++++++++ .../auth/src/automation/utils/duration.rs | 25 ++++++++ src/libs/auth/src/automation/utils/mod.rs | 2 + src/libs/auth/src/automation/utils/scope.rs | 19 ++++++ src/libs/auth/src/lib.rs | 1 + .../openid/credentials/automation/verify.rs | 12 ++-- src/libs/auth/src/state/errors.rs | 2 + src/libs/auth/src/state/heap.rs | 31 +++++++++- src/libs/auth/src/state/mod.rs | 2 +- src/libs/auth/src/state/store.rs | 16 ++++- src/libs/auth/src/state/types.rs | 8 +++ src/libs/satellite/src/api/controllers.rs | 4 +- .../satellite/src/controllers/authenticate.rs | 62 ++++++++----------- .../satellite/src/controllers/automation.rs | 40 ++++++++++++ src/libs/satellite/src/controllers/mod.rs | 2 +- src/libs/satellite/src/controllers/types.rs | 20 ++---- src/libs/satellite/src/impls.rs | 6 +- src/libs/satellite/src/types.rs | 4 +- 22 files changed, 286 insertions(+), 72 deletions(-) rename src/libs/{satellite/src/controllers => auth/src/automation}/constants.rs (50%) create mode 100644 src/libs/auth/src/automation/impls.rs create mode 100644 src/libs/auth/src/automation/mod.rs create mode 100644 src/libs/auth/src/automation/prepare.rs create mode 100644 src/libs/auth/src/automation/types.rs create mode 100644 src/libs/auth/src/automation/utils/duration.rs create mode 100644 src/libs/auth/src/automation/utils/mod.rs create mode 100644 src/libs/auth/src/automation/utils/scope.rs create mode 100644 src/libs/satellite/src/controllers/automation.rs diff --git a/src/libs/satellite/src/controllers/constants.rs b/src/libs/auth/src/automation/constants.rs similarity index 50% rename from src/libs/satellite/src/controllers/constants.rs rename to src/libs/auth/src/automation/constants.rs index 7db3c782bc..d027c00359 100644 --- a/src/libs/satellite/src/controllers/constants.rs +++ b/src/libs/auth/src/automation/constants.rs @@ -1,7 +1,7 @@ const MINUTE_NS: u64 = 60 * 1_000_000_000; // 10 minutes in nanoseconds -pub const DEFAULT_CONTROLLER_DURATION_NS: u64 = 10 * MINUTE_NS; +pub const DEFAULT_EXPIRATION_PERIOD_NS: u64 = 10 * MINUTE_NS; // The maximum duration for a automation controller -pub const MAX_CONTROLLER_DURATION_NS: u64 = 60 * MINUTE_NS; +pub const MAX_EXPIRATION_PERIOD_NS: u64 = 60 * MINUTE_NS; diff --git a/src/libs/auth/src/automation/impls.rs b/src/libs/auth/src/automation/impls.rs new file mode 100644 index 0000000000..da847697ba --- /dev/null +++ b/src/libs/auth/src/automation/impls.rs @@ -0,0 +1,17 @@ +use crate::automation::types::PrepareAutomationError; +use crate::openid::credentials::types::errors::VerifyOpenidCredentialsError; + +impl From for PrepareAutomationError { + fn from(e: VerifyOpenidCredentialsError) -> Self { + match e { + VerifyOpenidCredentialsError::GetOrFetchJwks(err) => { + PrepareAutomationError::GetOrFetchJwks(err) + } + VerifyOpenidCredentialsError::GetCachedJwks => PrepareAutomationError::GetCachedJwks, + VerifyOpenidCredentialsError::JwtFindProvider(err) => { + PrepareAutomationError::JwtFindProvider(err) + } + VerifyOpenidCredentialsError::JwtVerify(err) => PrepareAutomationError::JwtVerify(err), + } + } +} \ No newline at end of file diff --git a/src/libs/auth/src/automation/mod.rs b/src/libs/auth/src/automation/mod.rs new file mode 100644 index 0000000000..f546f4b03c --- /dev/null +++ b/src/libs/auth/src/automation/mod.rs @@ -0,0 +1,7 @@ +mod prepare; +pub mod types; +mod utils; +mod constants; +mod impls; + +pub use prepare::*; \ No newline at end of file diff --git a/src/libs/auth/src/automation/prepare.rs b/src/libs/auth/src/automation/prepare.rs new file mode 100644 index 0000000000..0390f77753 --- /dev/null +++ b/src/libs/auth/src/automation/prepare.rs @@ -0,0 +1,31 @@ +use junobuild_shared::segments::controllers::assert_controllers; +use junobuild_shared::types::state::ControllerId; +use crate::automation::types::{PrepareAutomationError, PrepareAutomationResult, PreparedAutomation, PreparedControllerAutomation}; +use crate::automation::utils::duration::build_expiration; +use crate::automation::utils::scope::build_scope; +use crate::openid::types::provider::{OpenIdAutomationProvider}; +use crate::strategies::{AuthHeapStrategy}; + +pub fn openid_prepare_automation( + controller_id: &ControllerId, + provider: &OpenIdAutomationProvider, + auth_heap: &impl AuthHeapStrategy, +) -> PrepareAutomationResult { + let controllers: [ControllerId; 1] = [controller_id.clone()]; + + assert_controllers(&controllers).map_err(PrepareAutomationError::InvalidController)?; + + // TODO: Assert do not exist + + let expires_at = build_expiration(provider, auth_heap); + + let scope = build_scope(provider, auth_heap); + + let controller: PreparedControllerAutomation = PreparedControllerAutomation { + id: controller_id.clone(), + expires_at, + scope + }; + + Ok(PreparedAutomation { controller }) +} \ No newline at end of file diff --git a/src/libs/auth/src/automation/types.rs b/src/libs/auth/src/automation/types.rs new file mode 100644 index 0000000000..f1e45026e6 --- /dev/null +++ b/src/libs/auth/src/automation/types.rs @@ -0,0 +1,43 @@ +use candid::{CandidType, Deserialize}; +use serde::Serialize; +use junobuild_shared::types::interface::SetController; +use junobuild_shared::types::state::ControllerId; +use crate::delegation::types::SessionKey; +use crate::openid::jwkset::types::errors::GetOrRefreshJwksError; +use crate::openid::jwt::types::errors::{JwtFindProviderError, JwtVerifyError}; +use crate::state::types::state::Salt; + +#[derive(CandidType, Serialize, Deserialize)] +pub struct OpenIdPrepareAutomationArgs { + pub jwt: String, + pub controller_id: ControllerId, +} + +pub type PrepareAutomationResult = Result; + +#[derive(CandidType, Serialize, Deserialize)] +pub struct PreparedAutomation { + pub controller: PreparedControllerAutomation, +} + +#[derive(CandidType, Serialize, Deserialize)] +pub struct PreparedControllerAutomation { + pub id: ControllerId, + pub scope: AutomationScope, + pub expires_at: u64, +} + +#[derive(CandidType, Serialize, Deserialize, Clone, Debug)] +pub enum AutomationScope { + Write, + Submit, +} + +#[derive(CandidType, Serialize, Deserialize, Debug)] +pub enum PrepareAutomationError { + InvalidController(String), + GetOrFetchJwks(GetOrRefreshJwksError), + GetCachedJwks, + JwtFindProvider(JwtFindProviderError), + JwtVerify(JwtVerifyError), +} \ No newline at end of file diff --git a/src/libs/auth/src/automation/utils/duration.rs b/src/libs/auth/src/automation/utils/duration.rs new file mode 100644 index 0000000000..d6b3c78e4b --- /dev/null +++ b/src/libs/auth/src/automation/utils/duration.rs @@ -0,0 +1,25 @@ +use crate::automation::constants::{DEFAULT_EXPIRATION_PERIOD_NS, MAX_EXPIRATION_PERIOD_NS}; +use crate::openid::types::provider::OpenIdAutomationProvider; +use crate::state::get_automation; +use crate::strategies::AuthHeapStrategy; +use ic_cdk::api::time; +use std::cmp::min; + +pub fn build_expiration( + provider: &OpenIdAutomationProvider, + auth_heap: &impl AuthHeapStrategy, +) -> u64 { + let max_time_to_live = get_automation(auth_heap) + .as_ref() + .and_then(|automation| automation.openid.as_ref()) + .and_then(|openid| openid.providers.get(provider)) + .and_then(|openid| openid.controller.as_ref()) + .and_then(|controller| controller.max_time_to_live); + + let controller_duration = min( + max_time_to_live.unwrap_or(DEFAULT_EXPIRATION_PERIOD_NS), + MAX_EXPIRATION_PERIOD_NS, + ); + + time().saturating_add(controller_duration) +} diff --git a/src/libs/auth/src/automation/utils/mod.rs b/src/libs/auth/src/automation/utils/mod.rs new file mode 100644 index 0000000000..25a801248a --- /dev/null +++ b/src/libs/auth/src/automation/utils/mod.rs @@ -0,0 +1,2 @@ +pub mod duration; +pub mod scope; \ No newline at end of file diff --git a/src/libs/auth/src/automation/utils/scope.rs b/src/libs/auth/src/automation/utils/scope.rs new file mode 100644 index 0000000000..aa579d9040 --- /dev/null +++ b/src/libs/auth/src/automation/utils/scope.rs @@ -0,0 +1,19 @@ +use crate::automation::types::AutomationScope; +use crate::openid::types::provider::{OpenIdAutomationProvider}; +use crate::state::get_automation; +use crate::strategies::AuthHeapStrategy; + +// We default to AutomationScope::Write because practically that's what most developers use. +// i.e. most developers expect their GitHub Actions build to take effect +pub fn build_scope( + provider: &OpenIdAutomationProvider, + auth_heap: &impl AuthHeapStrategy, +) -> AutomationScope { + get_automation(auth_heap) + .as_ref() + .and_then(|automation| automation.openid.as_ref()) + .and_then(|openid| openid.providers.get(provider)) + .and_then(|openid| openid.controller.as_ref()) + .and_then(|controller| controller.scope.clone()) + .unwrap_or(AutomationScope::Write) +} \ No newline at end of file diff --git a/src/libs/auth/src/lib.rs b/src/libs/auth/src/lib.rs index 968d88c334..88bb97093e 100644 --- a/src/libs/auth/src/lib.rs +++ b/src/libs/auth/src/lib.rs @@ -4,5 +4,6 @@ pub mod profile; mod random; pub mod state; pub mod strategies; +pub mod automation; pub use state::errors; diff --git a/src/libs/auth/src/openid/credentials/automation/verify.rs b/src/libs/auth/src/openid/credentials/automation/verify.rs index 114611ace9..1ff1f9b8ee 100644 --- a/src/libs/auth/src/openid/credentials/automation/verify.rs +++ b/src/libs/auth/src/openid/credentials/automation/verify.rs @@ -4,11 +4,11 @@ use crate::openid::jwt::types::cert::Jwks; use crate::openid::jwt::types::errors::JwtVerifyError; use crate::openid::jwt::types::token::Claims; use crate::openid::jwt::{unsafe_find_jwt_provider, verify_openid_jwt}; -use crate::openid::types::provider::OpenIdProvider; +use crate::openid::types::provider::{OpenIdAutomationProvider, OpenIdProvider}; use crate::state::types::automation::OpenIdAutomationProviders; use crate::strategies::AuthHeapStrategy; -type VerifyOpenIdAutomationCredentialsResult = Result<(), VerifyOpenidCredentialsError>; +type VerifyOpenIdAutomationCredentialsResult = Result; pub async fn verify_openid_credentials_with_jwks_renewal( jwt: &str, @@ -24,13 +24,13 @@ pub async fn verify_openid_credentials_with_jwks_renewal( .await .map_err(VerifyOpenidCredentialsError::GetOrFetchJwks)?; - verify_openid_credentials(jwt, &jwks, &provider) + verify_openid_credentials(jwt, &jwks, &automation_provider) } fn verify_openid_credentials( jwt: &str, jwks: &Jwks, - provider: &OpenIdProvider, + provider: &OpenIdAutomationProvider, ) -> VerifyOpenIdAutomationCredentialsResult { let assert_audience = |claims: &Claims| -> Result<(), JwtVerifyError> { // if claims.aud != client_id.as_str() { @@ -61,7 +61,7 @@ fn verify_openid_credentials( &assert_audience, &assert_no_replay, ) - .map_err(VerifyOpenidAutomationCredentialsError::JwtVerify)?; + .map_err(VerifyOpenidCredentialsError::JwtVerify)?; - Ok(()) + Ok(provider.clone()) } diff --git a/src/libs/auth/src/state/errors.rs b/src/libs/auth/src/state/errors.rs index 77c596c6ae..e630223e2b 100644 --- a/src/libs/auth/src/state/errors.rs +++ b/src/libs/auth/src/state/errors.rs @@ -2,5 +2,7 @@ pub const JUNO_AUTH_ERROR_INVALID_ORIGIN: &str = "juno.auth.error.invalid_origin"; // No authentication configuration found. pub const JUNO_AUTH_ERROR_NOT_CONFIGURED: &str = "juno.auth.error.not_configured"; +// No automation configuration found. +pub const JUNO_AUTH_ERROR_AUTOMATION_NOT_CONFIGURED: &str = "juno.auth.error.automation_not_configured"; // Authentication with OpenId disabled. pub const JUNO_AUTH_ERROR_OPENID_DISABLED: &str = "juno.auth.error.openid_disabled"; diff --git a/src/libs/auth/src/state/heap.rs b/src/libs/auth/src/state/heap.rs index 8dd29829ab..4f0e6b8443 100644 --- a/src/libs/auth/src/state/heap.rs +++ b/src/libs/auth/src/state/heap.rs @@ -4,7 +4,7 @@ use crate::state::types::state::Salt; use crate::state::types::state::{AuthenticationHeapState, OpenIdCachedCertificate, OpenIdState}; use crate::strategies::AuthHeapStrategy; use std::collections::hash_map::Entry; - +use crate::state::types::automation::AutomationConfig; // --------------------------------------------------------- // Config // --------------------------------------------------------- @@ -32,6 +32,35 @@ fn insert_config_impl(config: &AuthenticationConfig, state: &mut Option Option { + auth_heap + .with_auth_state(|authentication| { + authentication.as_ref().and_then(|auth| auth.automation.clone()) + }) +} + +pub fn insert_automation(auth_heap: &impl AuthHeapStrategy, automation: &Option) { + auth_heap.with_auth_state_mut(|authentication| insert_automation_impl(automation, authentication)) +} + +fn insert_automation_impl(automation: &Option, state: &mut Option) { + match state { + None => { + *state = Some(AuthenticationHeapState { + config: AuthenticationConfig::default(), + automation: automation.clone(), + salt: None, + openid: None, + }) + } + Some(state) => state.automation = automation.clone(), + } +} + // --------------------------------------------------------- // Salt // --------------------------------------------------------- diff --git a/src/libs/auth/src/state/mod.rs b/src/libs/auth/src/state/mod.rs index 7d5fdf1328..2989008e1c 100644 --- a/src/libs/auth/src/state/mod.rs +++ b/src/libs/auth/src/state/mod.rs @@ -10,7 +10,7 @@ pub mod types; pub use heap::{ cache_certificate, get_cached_certificate, get_config, get_openid_state, get_salt, insert_salt, - record_fetch_attempt, + record_fetch_attempt, get_automation }; pub use runtime::*; pub use store::*; diff --git a/src/libs/auth/src/state/store.rs b/src/libs/auth/src/state/store.rs index 7e727e12f9..fb990453ef 100644 --- a/src/libs/auth/src/state/store.rs +++ b/src/libs/auth/src/state/store.rs @@ -1,6 +1,6 @@ -use crate::errors::{JUNO_AUTH_ERROR_NOT_CONFIGURED, JUNO_AUTH_ERROR_OPENID_DISABLED}; +use crate::errors::{JUNO_AUTH_ERROR_AUTOMATION_NOT_CONFIGURED, JUNO_AUTH_ERROR_NOT_CONFIGURED, JUNO_AUTH_ERROR_OPENID_DISABLED}; use crate::state::assert::assert_set_config; -use crate::state::heap::get_config; +use crate::state::heap::{get_automation, get_config}; use crate::state::heap::insert_config; use crate::state::types::config::{AuthenticationConfig, OpenIdAuthProviders}; use crate::state::types::interface::SetAuthenticationConfig; @@ -8,6 +8,7 @@ use crate::state::{get_salt, insert_salt}; use crate::strategies::AuthHeapStrategy; use junobuild_shared::ic::api::print; use junobuild_shared::random::raw_rand; +use crate::state::types::automation::OpenIdAutomationProviders; pub fn set_config( auth_heap: &impl AuthHeapStrategy, @@ -56,3 +57,14 @@ pub fn get_auth_providers( Ok(openid.providers.clone()) } + +pub fn get_automation_providers( + auth_heap: &impl AuthHeapStrategy, +) -> Result { + let config = get_automation(auth_heap).ok_or(JUNO_AUTH_ERROR_AUTOMATION_NOT_CONFIGURED.to_string())?; + let openid = config + .openid + .ok_or(JUNO_AUTH_ERROR_OPENID_DISABLED.to_string())?; + + Ok(openid.providers.clone()) +} diff --git a/src/libs/auth/src/state/types.rs b/src/libs/auth/src/state/types.rs index 2e49eb58a3..fa782049ae 100644 --- a/src/libs/auth/src/state/types.rs +++ b/src/libs/auth/src/state/types.rs @@ -114,6 +114,7 @@ pub mod automation { use candid::{CandidType, Deserialize, Principal}; use serde::Serialize; use junobuild_shared::types::state::{Timestamp, Version}; + use crate::automation::types::AutomationScope; use crate::openid::types::provider::{OpenIdAutomationProvider}; #[derive(Default, CandidType, Serialize, Deserialize, Clone)] @@ -148,6 +149,7 @@ pub mod automation { #[derive(Default, CandidType, Serialize, Deserialize, Clone, Debug)] pub struct OpenIdAutomationProviderConfig { pub repositories: OpenIdAutomationRepositories, + pub controller: Option, } #[derive(CandidType, Serialize, Deserialize, Clone, Debug)] @@ -155,6 +157,12 @@ pub mod automation { // Optionally restrict to specific branches (e.g. ["main", "develop"]) pub branches: Option>, } + + #[derive(CandidType, Serialize, Deserialize, Clone, Debug)] + pub struct OpenIdAutomationProviderControllerConfig { + pub scope: Option, + pub max_time_to_live: Option, + } } pub mod interface { diff --git a/src/libs/satellite/src/api/controllers.rs b/src/libs/satellite/src/api/controllers.rs index 76a2e049c2..f2ddb7b89f 100644 --- a/src/libs/satellite/src/api/controllers.rs +++ b/src/libs/satellite/src/api/controllers.rs @@ -1,6 +1,6 @@ use crate::controllers::openid_authenticate_controller; use crate::controllers::store::{delete_controllers, set_controllers as set_controllers_store}; -use crate::controllers::types::{AuthenticateControllerArgs, AuthenticateControllerResult}; +use crate::controllers::types::{AuthenticateControllerArgs, AuthenticateAutomationResult}; use crate::{get_admin_controllers, get_controllers}; use ic_cdk::trap; use junobuild_shared::constants::shared::MAX_NUMBER_OF_SATELLITE_CONTROLLERS; @@ -54,7 +54,7 @@ pub fn list_controllers() -> Controllers { pub async fn authenticate_controller( args: AuthenticateControllerArgs, -) -> AuthenticateControllerResult { +) -> AuthenticateAutomationResult { match args { AuthenticateControllerArgs::OpenId(args) => openid_authenticate_controller(&args).await, } diff --git a/src/libs/satellite/src/controllers/authenticate.rs b/src/libs/satellite/src/controllers/authenticate.rs index 832486ebba..071c3acf57 100644 --- a/src/libs/satellite/src/controllers/authenticate.rs +++ b/src/libs/satellite/src/controllers/authenticate.rs @@ -1,54 +1,42 @@ use crate::auth::strategy_impls::AuthHeap; -use crate::controllers::constants::{DEFAULT_CONTROLLER_DURATION_NS, MAX_CONTROLLER_DURATION_NS}; use crate::controllers::store::set_controllers; use crate::controllers::types::{ - AuthenticateControllerResult, AuthenticationControllerError, OpenIdAuthenticateControllerArgs, + AuthenticateAutomationResult, AuthenticationAutomationError, }; -use ic_cdk::api::time; -use junobuild_shared::segments::controllers::assert_controllers; +use junobuild_auth::state::{get_automation_providers}; use junobuild_shared::types::interface::SetController; use junobuild_shared::types::state::ControllerId; -use std::cmp::min; -use junobuild_auth::openid::credentials; -use junobuild_auth::state::get_providers; +use junobuild_auth::automation::types::{OpenIdPrepareAutomationArgs, PreparedAutomation}; +use crate::controllers::automation; pub async fn openid_authenticate_controller( - args: &OpenIdAuthenticateControllerArgs, + args: &OpenIdPrepareAutomationArgs, // TODO: Result> -) -> AuthenticateControllerResult { - // TODO: get_automation_providers - let providers = get_providers(&AuthHeap)?; - - match credentials::automation::verify_openid_credentials_with_jwks_renewal( - &args.jwt, - &providers, - &AuthHeap, - ) - .await - { - Ok(_) => register_controller(args) - .map_err(|err| AuthenticationControllerError::RegisterController(err.to_string())), - Err(err) => Err(AuthenticationControllerError::VerifyOpenIdCredentials(err)), - } -} - -fn register_controller(args: &OpenIdAuthenticateControllerArgs) -> Result<(), String> { - let controllers: [ControllerId; 1] = [args.controller_id.clone()]; - - assert_controllers(&controllers)?; +) -> Result { + let providers = get_automation_providers(&AuthHeap)?; + + // TODO: rate? + + let prepared_automation = automation::openid_prepare_delegation(args, &providers).await; + + let result = match prepared_automation { + Ok((automation, _ , __)) => { + register_controller(&automation); + Ok(()) + } + Err(err) => Err(AuthenticationAutomationError::PrepareAutomation(err)), + }; - // TODO: Assert do not exist + Ok(result) +} - let expires_at = min( - args.max_time_to_live - .unwrap_or(DEFAULT_CONTROLLER_DURATION_NS), - MAX_CONTROLLER_DURATION_NS, - ); +fn register_controller(prepared_automation: &PreparedAutomation) { + let controllers: [ControllerId; 1] = [prepared_automation.controller.id.clone()]; let controller: SetController = SetController { - scope: args.scope.clone().into(), + scope: prepared_automation.controller.scope.clone().into(), metadata: args.metadata.clone(), - expires_at: Some(time().saturating_add(expires_at)), + expires_at: Some(prepared_automation.controller.expires_at), }; set_controllers(&controllers, &controller); diff --git a/src/libs/satellite/src/controllers/automation.rs b/src/libs/satellite/src/controllers/automation.rs new file mode 100644 index 0000000000..c1595d5c27 --- /dev/null +++ b/src/libs/satellite/src/controllers/automation.rs @@ -0,0 +1,40 @@ +use junobuild_auth::automation; +use junobuild_auth::automation::types::{OpenIdPrepareAutomationArgs, PrepareAutomationError, PreparedAutomation, PreparedDelegation}; +use junobuild_auth::openid::credentials; +use junobuild_auth::openid::credentials::delegation::types::interface::OpenIdCredential; +use junobuild_auth::openid::types::provider::OpenIdDelegationProvider; +use junobuild_auth::state::types::automation::OpenIdAutomationProviders; +use crate::auth::strategy_impls::AuthHeap; + +pub type OpenIdPrepareAutomationResult = Result< + ( + PreparedAutomation, + OpenIdDelegationProvider, + // TODO: credential + OpenIdCredential, + ), + PrepareAutomationError, +>; + +pub async fn openid_prepare_delegation( + args: &OpenIdPrepareAutomationArgs, + providers: &OpenIdAutomationProviders, +) -> OpenIdPrepareAutomationResult { + let provider = + match credentials::automation::verify_openid_credentials_with_jwks_renewal( + &args.jwt, providers, &AuthHeap, + ) + .await + { + Ok(value) => value, + Err(err) => return Err(PrepareAutomationError::from(err)), + }; + + let result = automation::openid_prepare_automation( + &args.controller_id, + &provider, + &AuthHeap, + ); + + result.map(|prepared_delegation| (prepared_delegation, provider, credential)) +} diff --git a/src/libs/satellite/src/controllers/mod.rs b/src/libs/satellite/src/controllers/mod.rs index a1406ae0cf..d85fb30397 100644 --- a/src/libs/satellite/src/controllers/mod.rs +++ b/src/libs/satellite/src/controllers/mod.rs @@ -1,7 +1,7 @@ mod authenticate; -mod constants; mod impls; pub mod store; pub mod types; +mod automation; pub use authenticate::*; diff --git a/src/libs/satellite/src/controllers/types.rs b/src/libs/satellite/src/controllers/types.rs index b19fb3667c..3707bfa2d1 100644 --- a/src/libs/satellite/src/controllers/types.rs +++ b/src/libs/satellite/src/controllers/types.rs @@ -1,20 +1,10 @@ use candid::{CandidType, Deserialize}; -use junobuild_auth::openid::credentials::automation::types::errors::VerifyOpenidAutomationCredentialsError; -use junobuild_shared::types::state::{ControllerId, Metadata}; use serde::Serialize; +use junobuild_auth::automation::types::{OpenIdPrepareAutomationArgs, PrepareAutomationError}; #[derive(CandidType, Serialize, Deserialize)] pub enum AuthenticateControllerArgs { - OpenId(OpenIdAuthenticateControllerArgs), -} - -#[derive(CandidType, Serialize, Deserialize)] -pub struct OpenIdAuthenticateControllerArgs { - pub jwt: String, - pub controller_id: ControllerId, - pub scope: AutomationScope, - pub metadata: Metadata, - pub max_time_to_live: Option, + OpenId(OpenIdPrepareAutomationArgs), } #[derive(CandidType, Serialize, Deserialize, Clone)] @@ -24,9 +14,9 @@ pub enum AutomationScope { } #[derive(CandidType, Serialize, Deserialize)] -pub enum AuthenticationControllerError { - VerifyOpenIdCredentials(VerifyOpenidAutomationCredentialsError), +pub enum AuthenticationAutomationError { + PrepareAutomation(PrepareAutomationError), RegisterController(String), } -pub type AuthenticateControllerResult = Result<(), AuthenticationControllerError>; +pub type AuthenticateAutomationResult = Result<(), AuthenticationAutomationError>; diff --git a/src/libs/satellite/src/impls.rs b/src/libs/satellite/src/impls.rs index c66f4fd33a..8990b9ceb4 100644 --- a/src/libs/satellite/src/impls.rs +++ b/src/libs/satellite/src/impls.rs @@ -1,4 +1,4 @@ -use crate::controllers::types::AuthenticateControllerResult; +use crate::controllers::types::AuthenticateAutomationResult; use crate::memory::internal::init_stable_state; use crate::types::interface::{ AuthenticateControllerResultResponse, AuthenticateResultResponse, AuthenticationResult, @@ -49,8 +49,8 @@ impl From for AuthenticateResultResponse { } } -impl From for AuthenticateControllerResultResponse { - fn from(r: AuthenticateControllerResult) -> Self { +impl From for AuthenticateControllerResultResponse { + fn from(r: AuthenticateAutomationResult) -> Self { match r { Ok(v) => Self::Ok(v), Err(e) => Self::Err(e), diff --git a/src/libs/satellite/src/types.rs b/src/libs/satellite/src/types.rs index edc724250b..3c83cdc125 100644 --- a/src/libs/satellite/src/types.rs +++ b/src/libs/satellite/src/types.rs @@ -56,7 +56,7 @@ pub mod state { } pub mod interface { - use crate::controllers::types::AuthenticationControllerError; + use crate::controllers::types::AuthenticationAutomationError; use crate::db::types::config::DbConfig; use crate::Doc; use candid::CandidType; @@ -123,7 +123,7 @@ pub mod interface { #[derive(CandidType, Serialize, Deserialize)] pub enum AuthenticateControllerResultResponse { Ok(()), - Err(AuthenticationControllerError), + Err(AuthenticationAutomationError), } } From a0319b5243b4de7ec28e3b0dc31383de0bb2b73c Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 29 Jan 2026 13:56:10 +0100 Subject: [PATCH 31/32] feat: move in mod --- src/libs/satellite/src/api/automation.rs | 13 ++++++++ src/libs/satellite/src/api/controllers.rs | 10 ------ src/libs/satellite/src/api/mod.rs | 1 + .../authenticate.rs | 18 +++++------ .../{controllers => automation}/automation.rs | 31 +++++++++---------- .../src/{controllers => automation}/impls.rs | 2 +- src/libs/satellite/src/automation/mod.rs | 4 +++ .../src/{controllers => automation}/types.rs | 4 +-- src/libs/satellite/src/controllers/mod.rs | 6 ---- src/libs/satellite/src/impls.rs | 6 ++-- src/libs/satellite/src/lib.rs | 23 +++++++------- src/libs/satellite/src/types.rs | 4 +-- 12 files changed, 60 insertions(+), 62 deletions(-) create mode 100644 src/libs/satellite/src/api/automation.rs rename src/libs/satellite/src/{controllers => automation}/authenticate.rs (77%) rename src/libs/satellite/src/{controllers => automation}/automation.rs (55%) rename src/libs/satellite/src/{controllers => automation}/impls.rs (86%) create mode 100644 src/libs/satellite/src/automation/mod.rs rename src/libs/satellite/src/{controllers => automation}/types.rs (94%) diff --git a/src/libs/satellite/src/api/automation.rs b/src/libs/satellite/src/api/automation.rs new file mode 100644 index 0000000000..d46658c382 --- /dev/null +++ b/src/libs/satellite/src/api/automation.rs @@ -0,0 +1,13 @@ +use crate::automation::authenticate::openid_authenticate_automation; +use crate::automation::types::{AuthenticateAutomationArgs, AuthenticateAutomationResult}; +use junobuild_shared::ic::UnwrapOrTrap; + +pub async fn authenticate_automation( + args: AuthenticateAutomationArgs, +) -> AuthenticateAutomationResult { + match args { + AuthenticateAutomationArgs::OpenId(args) => { + openid_authenticate_automation(&args).await.unwrap_or_trap() + } + } +} diff --git a/src/libs/satellite/src/api/controllers.rs b/src/libs/satellite/src/api/controllers.rs index f2ddb7b89f..d11d23cdfb 100644 --- a/src/libs/satellite/src/api/controllers.rs +++ b/src/libs/satellite/src/api/controllers.rs @@ -1,6 +1,4 @@ -use crate::controllers::openid_authenticate_controller; use crate::controllers::store::{delete_controllers, set_controllers as set_controllers_store}; -use crate::controllers::types::{AuthenticateControllerArgs, AuthenticateAutomationResult}; use crate::{get_admin_controllers, get_controllers}; use ic_cdk::trap; use junobuild_shared::constants::shared::MAX_NUMBER_OF_SATELLITE_CONTROLLERS; @@ -51,11 +49,3 @@ pub fn del_controllers( pub fn list_controllers() -> Controllers { get_controllers() } - -pub async fn authenticate_controller( - args: AuthenticateControllerArgs, -) -> AuthenticateAutomationResult { - match args { - AuthenticateControllerArgs::OpenId(args) => openid_authenticate_controller(&args).await, - } -} diff --git a/src/libs/satellite/src/api/mod.rs b/src/libs/satellite/src/api/mod.rs index 061385cc3f..b5da645856 100644 --- a/src/libs/satellite/src/api/mod.rs +++ b/src/libs/satellite/src/api/mod.rs @@ -1,4 +1,5 @@ pub mod auth; +pub mod automation; pub mod cdn; pub mod config; pub mod controllers; diff --git a/src/libs/satellite/src/controllers/authenticate.rs b/src/libs/satellite/src/automation/authenticate.rs similarity index 77% rename from src/libs/satellite/src/controllers/authenticate.rs rename to src/libs/satellite/src/automation/authenticate.rs index 071c3acf57..21ceab530a 100644 --- a/src/libs/satellite/src/controllers/authenticate.rs +++ b/src/libs/satellite/src/automation/authenticate.rs @@ -1,26 +1,24 @@ use crate::auth::strategy_impls::AuthHeap; +use crate::automation::automation; +use crate::automation::types::{AuthenticateAutomationResult, AuthenticationAutomationError}; use crate::controllers::store::set_controllers; -use crate::controllers::types::{ - AuthenticateAutomationResult, AuthenticationAutomationError, -}; -use junobuild_auth::state::{get_automation_providers}; +use junobuild_auth::automation::types::{OpenIdPrepareAutomationArgs, PreparedAutomation}; +use junobuild_auth::state::get_automation_providers; use junobuild_shared::types::interface::SetController; use junobuild_shared::types::state::ControllerId; -use junobuild_auth::automation::types::{OpenIdPrepareAutomationArgs, PreparedAutomation}; -use crate::controllers::automation; -pub async fn openid_authenticate_controller( +pub async fn openid_authenticate_automation( args: &OpenIdPrepareAutomationArgs, // TODO: Result> ) -> Result { let providers = get_automation_providers(&AuthHeap)?; - + // TODO: rate? - let prepared_automation = automation::openid_prepare_delegation(args, &providers).await; + let prepared_automation = automation::openid_prepare_automation(args, &providers).await; let result = match prepared_automation { - Ok((automation, _ , __)) => { + Ok((automation, _, __)) => { register_controller(&automation); Ok(()) } diff --git a/src/libs/satellite/src/controllers/automation.rs b/src/libs/satellite/src/automation/automation.rs similarity index 55% rename from src/libs/satellite/src/controllers/automation.rs rename to src/libs/satellite/src/automation/automation.rs index c1595d5c27..3426697fcf 100644 --- a/src/libs/satellite/src/controllers/automation.rs +++ b/src/libs/satellite/src/automation/automation.rs @@ -1,10 +1,12 @@ +use crate::auth::strategy_impls::AuthHeap; use junobuild_auth::automation; -use junobuild_auth::automation::types::{OpenIdPrepareAutomationArgs, PrepareAutomationError, PreparedAutomation, PreparedDelegation}; +use junobuild_auth::automation::types::{ + OpenIdPrepareAutomationArgs, PrepareAutomationError, PreparedAutomation, PreparedDelegation, +}; use junobuild_auth::openid::credentials; use junobuild_auth::openid::credentials::delegation::types::interface::OpenIdCredential; use junobuild_auth::openid::types::provider::OpenIdDelegationProvider; use junobuild_auth::state::types::automation::OpenIdAutomationProviders; -use crate::auth::strategy_impls::AuthHeap; pub type OpenIdPrepareAutomationResult = Result< ( @@ -16,25 +18,20 @@ pub type OpenIdPrepareAutomationResult = Result< PrepareAutomationError, >; -pub async fn openid_prepare_delegation( +pub async fn openid_prepare_automation( args: &OpenIdPrepareAutomationArgs, providers: &OpenIdAutomationProviders, ) -> OpenIdPrepareAutomationResult { - let provider = - match credentials::automation::verify_openid_credentials_with_jwks_renewal( - &args.jwt, providers, &AuthHeap, - ) - .await - { - Ok(value) => value, - Err(err) => return Err(PrepareAutomationError::from(err)), - }; + let provider = match credentials::automation::verify_openid_credentials_with_jwks_renewal( + &args.jwt, providers, &AuthHeap, + ) + .await + { + Ok(value) => value, + Err(err) => return Err(PrepareAutomationError::from(err)), + }; - let result = automation::openid_prepare_automation( - &args.controller_id, - &provider, - &AuthHeap, - ); + let result = automation::openid_prepare_automation(&args.controller_id, &provider, &AuthHeap); result.map(|prepared_delegation| (prepared_delegation, provider, credential)) } diff --git a/src/libs/satellite/src/controllers/impls.rs b/src/libs/satellite/src/automation/impls.rs similarity index 86% rename from src/libs/satellite/src/controllers/impls.rs rename to src/libs/satellite/src/automation/impls.rs index d588dca7bc..971d7e0769 100644 --- a/src/libs/satellite/src/controllers/impls.rs +++ b/src/libs/satellite/src/automation/impls.rs @@ -1,4 +1,4 @@ -use crate::controllers::types::AutomationScope; +use crate::automation::types::AutomationScope; use junobuild_shared::types::state::ControllerScope; impl From for ControllerScope { diff --git a/src/libs/satellite/src/automation/mod.rs b/src/libs/satellite/src/automation/mod.rs new file mode 100644 index 0000000000..7f2c56e0eb --- /dev/null +++ b/src/libs/satellite/src/automation/mod.rs @@ -0,0 +1,4 @@ +pub mod authenticate; +mod automation; +mod impls; +pub mod types; diff --git a/src/libs/satellite/src/controllers/types.rs b/src/libs/satellite/src/automation/types.rs similarity index 94% rename from src/libs/satellite/src/controllers/types.rs rename to src/libs/satellite/src/automation/types.rs index 3707bfa2d1..00f6496828 100644 --- a/src/libs/satellite/src/controllers/types.rs +++ b/src/libs/satellite/src/automation/types.rs @@ -1,9 +1,9 @@ use candid::{CandidType, Deserialize}; -use serde::Serialize; use junobuild_auth::automation::types::{OpenIdPrepareAutomationArgs, PrepareAutomationError}; +use serde::Serialize; #[derive(CandidType, Serialize, Deserialize)] -pub enum AuthenticateControllerArgs { +pub enum AuthenticateAutomationArgs { OpenId(OpenIdPrepareAutomationArgs), } diff --git a/src/libs/satellite/src/controllers/mod.rs b/src/libs/satellite/src/controllers/mod.rs index d85fb30397..55c88cbf3d 100644 --- a/src/libs/satellite/src/controllers/mod.rs +++ b/src/libs/satellite/src/controllers/mod.rs @@ -1,7 +1 @@ -mod authenticate; -mod impls; pub mod store; -pub mod types; -mod automation; - -pub use authenticate::*; diff --git a/src/libs/satellite/src/impls.rs b/src/libs/satellite/src/impls.rs index 8990b9ceb4..3e2abd2eff 100644 --- a/src/libs/satellite/src/impls.rs +++ b/src/libs/satellite/src/impls.rs @@ -1,7 +1,7 @@ -use crate::controllers::types::AuthenticateAutomationResult; +use crate::automation::types::AuthenticateAutomationResult; use crate::memory::internal::init_stable_state; use crate::types::interface::{ - AuthenticateControllerResultResponse, AuthenticateResultResponse, AuthenticationResult, + AuthenticateAutomationResultResponse, AuthenticateResultResponse, AuthenticationResult, GetDelegationResultResponse, }; use crate::types::state::{CollectionType, HeapState, RuntimeState, State}; @@ -49,7 +49,7 @@ impl From for AuthenticateResultResponse { } } -impl From for AuthenticateControllerResultResponse { +impl From for AuthenticateAutomationResultResponse { fn from(r: AuthenticateAutomationResult) -> Self { match r { Ok(v) => Self::Ok(v), diff --git a/src/libs/satellite/src/lib.rs b/src/libs/satellite/src/lib.rs index f81da3a632..ef2c479a64 100644 --- a/src/libs/satellite/src/lib.rs +++ b/src/libs/satellite/src/lib.rs @@ -3,6 +3,7 @@ mod api; mod assets; mod auth; +mod automation; mod certification; mod controllers; mod db; @@ -18,17 +19,17 @@ mod sdk; mod types; mod user; -use crate::controllers::types::AuthenticateControllerArgs; use crate::db::types::config::DbConfig; use crate::db::types::interface::SetDbConfig; use crate::guards::{ caller_is_admin_controller, caller_is_controller, caller_is_controller_with_write, }; use crate::types::interface::{ - AuthenticateControllerResultResponse, AuthenticateResultResponse, AuthenticationArgs, Config, + AuthenticateAutomationResultResponse, AuthenticateResultResponse, AuthenticationArgs, Config, DeleteProposalAssets, GetDelegationArgs, GetDelegationResultResponse, }; use crate::types::state::CollectionType; +use automation::types::AuthenticateAutomationArgs; use ic_cdk_macros::{init, post_upgrade, pre_upgrade, query, update}; use junobuild_auth::state::types::config::AuthenticationConfig; use junobuild_auth::state::types::interface::SetAuthenticationConfig; @@ -177,6 +178,14 @@ pub fn get_delegation(args: GetDelegationArgs) -> GetDelegationResultResponse { api::auth::get_delegation(&args).into() } +#[doc(hidden)] +#[update] +pub async fn authenticate_automation( + args: AuthenticateAutomationArgs, +) -> AuthenticateAutomationResultResponse { + api::automation::authenticate_automation(args).await.into() +} + // --------------------------------------------------------- // Rules // --------------------------------------------------------- @@ -233,14 +242,6 @@ pub fn list_controllers() -> Controllers { api::controllers::list_controllers() } -#[doc(hidden)] -#[update] -pub async fn authenticate_controller( - args: AuthenticateControllerArgs, -) -> AuthenticateControllerResultResponse { - api::controllers::authenticate_controller(args).await.into() -} - // --------------------------------------------------------- // Proposal // --------------------------------------------------------- @@ -549,7 +550,7 @@ pub fn memory_size() -> MemorySize { macro_rules! include_satellite { () => { use junobuild_satellite::{ - authenticate, authenticate_controller, commit_asset_upload, commit_proposal, + authenticate, authenticate_automation, commit_asset_upload, commit_proposal, commit_proposal_asset_upload, commit_proposal_many_assets_upload, count_assets, count_collection_assets, count_collection_docs, count_docs, count_proposals, del_asset, del_assets, del_controllers, del_custom_domain, del_doc, del_docs, del_filtered_assets, diff --git a/src/libs/satellite/src/types.rs b/src/libs/satellite/src/types.rs index 3c83cdc125..3e5f975faa 100644 --- a/src/libs/satellite/src/types.rs +++ b/src/libs/satellite/src/types.rs @@ -56,7 +56,7 @@ pub mod state { } pub mod interface { - use crate::controllers::types::AuthenticationAutomationError; + use crate::automation::types::AuthenticationAutomationError; use crate::db::types::config::DbConfig; use crate::Doc; use candid::CandidType; @@ -121,7 +121,7 @@ pub mod interface { } #[derive(CandidType, Serialize, Deserialize)] - pub enum AuthenticateControllerResultResponse { + pub enum AuthenticateAutomationResultResponse { Ok(()), Err(AuthenticationAutomationError), } From 254af33e78db414a3caa37fbeede8dda4fcb9ac0 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 29 Jan 2026 13:57:00 +0100 Subject: [PATCH 32/32] fix: deleted types --- src/libs/auth/src/openid/credentials/automation/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/auth/src/openid/credentials/automation/mod.rs b/src/libs/auth/src/openid/credentials/automation/mod.rs index d9a483abcc..06e9b633bf 100644 --- a/src/libs/auth/src/openid/credentials/automation/mod.rs +++ b/src/libs/auth/src/openid/credentials/automation/mod.rs @@ -1,4 +1,3 @@ -pub mod types; mod verify; pub use verify::*;