canic_core/api/auth/
mod.rs1use crate::{
8 cdk::types::Principal,
9 dto::{
10 auth::{
11 DelegatedToken, DelegatedTokenGetRequest, DelegatedTokenPrepareRequest,
12 DelegatedTokenPrepareResponse, DelegationProof, DelegationProofGetRequest,
13 DelegationProofIssueRequest, DelegationProofPrepareResponse,
14 InstallActiveDelegationProofRequest, InstallActiveDelegationProofResponse,
15 RoleAttestationGetRequest, RoleAttestationPrepareResponse, RoleAttestationRequest,
16 SignedRoleAttestation,
17 },
18 error::Error,
19 },
20 error::InternalErrorClass,
21 ops::{
22 auth::{AuthOps, VerifyDelegatedTokenRuntimeInput},
23 config::ConfigOps,
24 ic::IcOps,
25 runtime::env::EnvOps,
26 },
27 workflow::runtime::auth::RuntimeAuthWorkflow,
28};
29use root_delegation_client::RootDelegationProofClient;
30
31mod metadata;
35mod root_delegation_client;
36mod session;
37
38pub struct AuthApi;
46
47impl AuthApi {
48 const DELEGATED_TOKENS_DISABLED: &str =
49 "delegated token auth disabled; set auth.delegated_tokens.enabled=true in canic.toml";
50 const DELEGATED_TOKEN_ISSUER_DISABLED: &str = "delegated token issuer disabled for this canister; set subnets.<subnet>.canisters.<role>.auth.delegated_token_issuer=true in canic.toml";
51 const MAX_DELEGATED_SESSION_TTL_SECS: u64 = 24 * 60 * 60;
52 const SESSION_BOOTSTRAP_TOKEN_FINGERPRINT_DOMAIN: &[u8] =
53 b"canic-session-bootstrap-token-fingerprint";
54
55 fn map_auth_error(err: crate::InternalError) -> Error {
57 match err.class() {
58 InternalErrorClass::Infra | InternalErrorClass::Ops | InternalErrorClass::Workflow => {
59 Error::internal(err.to_string())
60 }
61 _ => Error::from(err),
62 }
63 }
64
65 fn require_delegated_token_issuer_enabled() -> Result<(), Error> {
66 let delegated_tokens_cfg =
67 ConfigOps::delegated_tokens_config().map_err(Self::map_auth_error)?;
68 if !delegated_tokens_cfg.enabled {
69 return Err(Error::invalid(Self::DELEGATED_TOKENS_DISABLED));
70 }
71
72 let canister_cfg = ConfigOps::current_canister().map_err(Self::map_auth_error)?;
73 if !canister_cfg.auth.delegated_token_issuer {
74 return Err(Error::forbidden(Self::DELEGATED_TOKEN_ISSUER_DISABLED));
75 }
76
77 Ok(())
78 }
79
80 fn verify_token_material(
85 token: &DelegatedToken,
86 max_cert_ttl_ns: u64,
87 max_token_ttl_ns: u64,
88 required_scopes: &[String],
89 now_ns: u64,
90 ) -> Result<Principal, Error> {
91 AuthOps::verify_token(VerifyDelegatedTokenRuntimeInput {
92 token,
93 caller: IcOps::msg_caller(),
94 max_cert_ttl_ns,
95 max_token_ttl_ns,
96 required_scopes,
97 now_ns,
98 })
99 .map(|verified| verified.subject)
100 .map_err(Self::map_auth_error)
101 }
102
103 pub fn prepare_delegated_token(
105 request: DelegatedTokenPrepareRequest,
106 ) -> Result<DelegatedTokenPrepareResponse, Error> {
107 Self::require_delegated_token_issuer_enabled()?;
108 RuntimeAuthWorkflow::prepare_delegated_token(request).map_err(Self::map_auth_error)
109 }
110
111 pub fn get_delegated_token(request: DelegatedTokenGetRequest) -> Result<DelegatedToken, Error> {
113 Self::require_delegated_token_issuer_enabled()?;
114
115 AuthOps::get_delegated_token_issuer_proof(request.claims_hash, IcOps::msg_caller())
116 .map_err(Self::map_auth_error)
117 }
118
119 pub fn install_active_delegation_proof(
121 request: InstallActiveDelegationProofRequest,
122 ) -> Result<InstallActiveDelegationProofResponse, Error> {
123 Self::require_delegated_token_issuer_enabled()?;
124
125 let active_proof =
126 AuthOps::install_active_delegation_proof(request.proof, IcOps::msg_caller())
127 .map_err(Self::map_auth_error)?;
128
129 Ok(InstallActiveDelegationProofResponse { active_proof })
130 }
131
132 pub async fn prepare_delegation_proof(
134 request: DelegationProofIssueRequest,
135 ) -> Result<DelegationProofPrepareResponse, Error> {
136 let request = metadata::with_delegation_request_metadata(request);
137 Self::prepare_delegation_proof_remote(request).await
138 }
139
140 pub fn prepare_delegation_proof_root(
142 request: DelegationProofIssueRequest,
143 ) -> Result<DelegationProofPrepareResponse, Error> {
144 RuntimeAuthWorkflow::prepare_delegation_proof_root(request).map_err(Self::map_auth_error)
145 }
146
147 pub fn get_delegation_proof_root(
149 request: DelegationProofGetRequest,
150 ) -> Result<DelegationProof, Error> {
151 EnvOps::require_root().map_err(Error::from)?;
152 let caller = IcOps::msg_caller();
153 AuthOps::get_delegation_proof(caller, request.cert_hash).map_err(Self::map_auth_error)
154 }
155
156 pub fn prepare_role_attestation_root(
158 request: RoleAttestationRequest,
159 ) -> Result<RoleAttestationPrepareResponse, Error> {
160 RuntimeAuthWorkflow::prepare_role_attestation_root(request).map_err(Self::map_auth_error)
161 }
162
163 pub fn get_role_attestation_root(
165 request: RoleAttestationGetRequest,
166 ) -> Result<SignedRoleAttestation, Error> {
167 EnvOps::require_root().map_err(Error::from)?;
168 AuthOps::get_role_attestation(IcOps::msg_caller(), request.payload_hash)
169 .map_err(Self::map_auth_error)
170 }
171
172 pub async fn verify_role_attestation(
174 attestation: &SignedRoleAttestation,
175 min_accepted_epoch: u64,
176 ) -> Result<(), Error> {
177 crate::workflow::runtime::auth::RuntimeAuthWorkflow::verify_role_attestation(
178 attestation,
179 min_accepted_epoch,
180 )
181 .await
182 .map_err(Self::map_auth_error)
183 }
184
185 fn delegated_token_max_ttl_ns() -> Result<u64, Error> {
187 let cfg = ConfigOps::delegated_tokens_config().map_err(Error::from)?;
188 if !cfg.enabled {
189 return Err(Error::forbidden(Self::DELEGATED_TOKENS_DISABLED));
190 }
191
192 let max_ttl_secs = cfg
193 .max_ttl_secs
194 .unwrap_or(Self::MAX_DELEGATED_SESSION_TTL_SECS);
195 max_ttl_secs.checked_mul(1_000_000_000).ok_or_else(|| {
196 Error::invalid("auth.delegated_tokens.max_ttl_secs overflows nanoseconds")
197 })
198 }
199}
200
201impl AuthApi {
202 async fn prepare_delegation_proof_remote(
204 request: DelegationProofIssueRequest,
205 ) -> Result<DelegationProofPrepareResponse, Error> {
206 let root_pid = EnvOps::root_pid().map_err(Error::from)?;
207 RootDelegationProofClient::new(root_pid)
208 .prepare_delegation_proof(request)
209 .await
210 .map_err(Self::map_auth_error)
211 }
212}