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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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!();