1use crate::{
8 cdk::{api::is_controller as caller_is_controller, types::Principal},
9 dto::{
10 auth::{
11 ActiveDelegationProofStatusResponse, DelegatedToken, DelegatedTokenGetRequest,
12 DelegatedTokenPrepareRequest, DelegatedTokenPrepareResponse,
13 InstallActiveDelegationProofRequest, InstallActiveDelegationProofResponse,
14 RoleAttestationGetRequest, RoleAttestationPrepareResponse, RoleAttestationRequest,
15 RootDelegationProofBatchGetRequest, RootDelegationProofBatchGetResponse,
16 RootDelegationProofBatchInstallRequest, RootDelegationProofBatchInstallResponse,
17 RootDelegationProofBatchPrepareRequest, RootDelegationProofBatchPrepareResponse,
18 RootDelegationProofBatchProof, RootDelegationRenewalProofBatchGetRequest,
19 RootDelegationRenewalProvisionerListResponse, RootDelegationRenewalProvisionerResponse,
20 RootDelegationRenewalProvisionerUpsertRequest, RootDelegationRenewalWorkListResponse,
21 RootIssuerPolicyResponse, RootIssuerPolicyUpsertRequest,
22 RootIssuerRenewalStatusRequest, RootIssuerRenewalStatusResponse,
23 RootIssuerRenewalTemplateResponse, RootIssuerRenewalTemplateUpsertRequest,
24 SignedRoleAttestation,
25 },
26 error::Error,
27 },
28 error::InternalErrorClass,
29 ops::{
30 auth::{AuthOps, VerifyDelegatedTokenRuntimeInput},
31 config::ConfigOps,
32 ic::IcOps,
33 runtime::env::EnvOps,
34 },
35 workflow::runtime::auth::RuntimeAuthWorkflow,
36};
37
38mod session;
41
42pub struct AuthApi;
50
51impl AuthApi {
52 const DELEGATED_TOKENS_DISABLED: &str =
53 "delegated token auth disabled; set auth.delegated_tokens.enabled=true in canic.toml";
54 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";
55 const LEGACY_ROOT_PROOF_PROVISIONING_DISABLED: &str = "bridge-backed canister-signature root proof provisioning is disabled in 0.76 chain_key_batch mode";
56 const MAX_DELEGATED_SESSION_TTL_SECS: u64 = 24 * 60 * 60;
57 const SESSION_BOOTSTRAP_TOKEN_FINGERPRINT_DOMAIN: &[u8] =
58 b"canic-session-bootstrap-token-fingerprint";
59
60 fn map_auth_error(err: crate::InternalError) -> Error {
62 match err.class() {
63 InternalErrorClass::Infra | InternalErrorClass::Ops | InternalErrorClass::Workflow => {
64 Error::internal(err.to_string())
65 }
66 _ => Error::from(err),
67 }
68 }
69
70 fn require_delegated_token_issuer_enabled() -> Result<(), Error> {
71 let delegated_tokens_cfg =
72 ConfigOps::delegated_tokens_config().map_err(Self::map_auth_error)?;
73 if !delegated_tokens_cfg.enabled {
74 return Err(Error::invalid(Self::DELEGATED_TOKENS_DISABLED));
75 }
76
77 let canister_cfg = ConfigOps::current_canister().map_err(Self::map_auth_error)?;
78 if !canister_cfg.auth.delegated_token_issuer {
79 return Err(Error::forbidden(Self::DELEGATED_TOKEN_ISSUER_DISABLED));
80 }
81
82 Ok(())
83 }
84
85 fn require_delegation_renewal_provisioner_or_controller() -> Result<bool, Error> {
86 let caller = IcOps::msg_caller();
87 if caller_is_controller(&caller) {
88 return Ok(true);
89 }
90 if AuthOps::is_delegation_renewal_provisioner(caller) {
91 return Ok(false);
92 }
93
94 Err(Error::forbidden(
95 "caller is not a controller or enabled delegation renewal provisioner",
96 ))
97 }
98
99 fn require_legacy_root_proof_provisioning_enabled() -> Result<(), Error> {
100 let cfg = ConfigOps::delegated_tokens_config().map_err(Self::map_auth_error)?;
101 if cfg.root_proof_mode.trim() == "chain_key_batch" {
102 return Err(Error::forbidden(
103 Self::LEGACY_ROOT_PROOF_PROVISIONING_DISABLED,
104 ));
105 }
106 Ok(())
107 }
108
109 fn verify_token_material(
114 token: &DelegatedToken,
115 max_cert_ttl_ns: u64,
116 max_token_ttl_ns: u64,
117 required_scopes: &[String],
118 now_ns: u64,
119 ) -> Result<Principal, Error> {
120 AuthOps::verify_token(VerifyDelegatedTokenRuntimeInput {
121 token,
122 caller: IcOps::msg_caller(),
123 max_cert_ttl_ns,
124 max_token_ttl_ns,
125 required_scopes,
126 now_ns,
127 })
128 .map(|verified| verified.subject)
129 .map_err(Self::map_auth_error)
130 }
131
132 pub async fn prepare_delegated_token(
134 request: DelegatedTokenPrepareRequest,
135 ) -> Result<DelegatedTokenPrepareResponse, Error> {
136 Self::require_delegated_token_issuer_enabled()?;
137 RuntimeAuthWorkflow::prepare_delegated_token(request)
138 .await
139 .map_err(Self::map_auth_error)
140 }
141
142 pub fn get_delegated_token(request: DelegatedTokenGetRequest) -> Result<DelegatedToken, Error> {
144 Self::require_delegated_token_issuer_enabled()?;
145
146 AuthOps::get_delegated_token_issuer_proof(request.claims_hash, IcOps::msg_caller())
147 .map_err(Self::map_auth_error)
148 }
149
150 pub fn install_active_delegation_proof(
152 request: InstallActiveDelegationProofRequest,
153 ) -> Result<InstallActiveDelegationProofResponse, Error> {
154 Self::require_delegated_token_issuer_enabled()?;
155
156 let active_proof =
157 AuthOps::install_active_delegation_proof(request.proof, IcOps::msg_caller())
158 .map_err(Self::map_auth_error)?;
159
160 Ok(InstallActiveDelegationProofResponse { active_proof })
161 }
162
163 pub fn active_delegation_proof_status() -> Result<ActiveDelegationProofStatusResponse, Error> {
165 Self::require_delegated_token_issuer_enabled()?;
166 Ok(AuthOps::active_delegation_proof_status(IcOps::now_nanos()))
167 }
168
169 pub fn upsert_root_issuer_policy_root(
171 request: RootIssuerPolicyUpsertRequest,
172 ) -> Result<RootIssuerPolicyResponse, Error> {
173 EnvOps::require_root().map_err(Error::from)?;
174 AuthOps::upsert_root_issuer_policy(request, IcOps::now_nanos())
175 .map_err(Self::map_auth_error)
176 }
177
178 pub fn upsert_root_issuer_renewal_template_root(
180 request: RootIssuerRenewalTemplateUpsertRequest,
181 ) -> Result<RootIssuerRenewalTemplateResponse, Error> {
182 EnvOps::require_root().map_err(Error::from)?;
183 let response = AuthOps::upsert_root_issuer_renewal_template(request, IcOps::now_nanos())
184 .map_err(Self::map_auth_error)?;
185 if response.template.enabled {
186 RuntimeAuthWorkflow::start_root_delegation_renewal_timer_soon_if_configured()
187 .map_err(Self::map_auth_error)?;
188 }
189 Ok(response)
190 }
191
192 pub fn root_issuer_renewal_status_root(
194 request: RootIssuerRenewalStatusRequest,
195 ) -> Result<RootIssuerRenewalStatusResponse, Error> {
196 EnvOps::require_root().map_err(Error::from)?;
197 Ok(AuthOps::root_issuer_renewal_status(request))
198 }
199
200 pub fn upsert_delegation_renewal_provisioner_root(
202 request: RootDelegationRenewalProvisionerUpsertRequest,
203 ) -> Result<RootDelegationRenewalProvisionerResponse, Error> {
204 EnvOps::require_root().map_err(Error::from)?;
205 Self::require_legacy_root_proof_provisioning_enabled()?;
206 Ok(AuthOps::upsert_delegation_renewal_provisioner(request))
207 }
208
209 pub fn delegation_renewal_provisioners_root()
211 -> Result<RootDelegationRenewalProvisionerListResponse, Error> {
212 EnvOps::require_root().map_err(Error::from)?;
213 Self::require_legacy_root_proof_provisioning_enabled()?;
214 Ok(AuthOps::delegation_renewal_provisioners())
215 }
216
217 pub fn delegation_renewal_work_root() -> Result<RootDelegationRenewalWorkListResponse, Error> {
219 EnvOps::require_root().map_err(Error::from)?;
220 Self::require_legacy_root_proof_provisioning_enabled()?;
221 Self::require_delegation_renewal_provisioner_or_controller()?;
222 Ok(AuthOps::delegation_renewal_work(IcOps::now_nanos()))
223 }
224
225 pub fn prepare_delegation_proof_batch_root(
227 request: RootDelegationProofBatchPrepareRequest,
228 ) -> Result<RootDelegationProofBatchPrepareResponse, Error> {
229 EnvOps::require_root().map_err(Error::from)?;
230 Self::require_legacy_root_proof_provisioning_enabled()?;
231 let max_cert_ttl_ns = Self::delegated_token_max_ttl_ns()?;
232 AuthOps::prepare_delegation_proof_batch(request, max_cert_ttl_ns, IcOps::now_nanos())
233 .map_err(Self::map_auth_error)
234 }
235
236 pub fn get_delegation_proof_batch_root(
238 request: RootDelegationProofBatchGetRequest,
239 ) -> Result<RootDelegationProofBatchGetResponse, Error> {
240 EnvOps::require_root().map_err(Error::from)?;
241 Self::require_legacy_root_proof_provisioning_enabled()?;
242 AuthOps::get_delegation_proof_batch(request).map_err(Self::map_auth_error)
243 }
244
245 pub fn get_delegation_renewal_proof_batch_root(
247 request: RootDelegationRenewalProofBatchGetRequest,
248 ) -> Result<RootDelegationProofBatchGetResponse, Error> {
249 EnvOps::require_root().map_err(Error::from)?;
250 Self::require_legacy_root_proof_provisioning_enabled()?;
251 Self::require_delegation_renewal_provisioner_or_controller()?;
252 AuthOps::get_delegation_renewal_proof_batch(request).map_err(Self::map_auth_error)
253 }
254
255 pub async fn install_delegation_proof_batch_root(
257 request: RootDelegationProofBatchInstallRequest,
258 ) -> Result<RootDelegationProofBatchInstallResponse, Error> {
259 EnvOps::require_root().map_err(Error::from)?;
260 Self::require_legacy_root_proof_provisioning_enabled()?;
261 let caller_is_controller = Self::require_delegation_renewal_provisioner_or_controller()?;
262 if !caller_is_controller {
263 AuthOps::ensure_delegation_renewal_batch_scheduled(
264 request.batch_id,
265 IcOps::now_nanos(),
266 )
267 .map_err(Self::map_auth_error)?;
268 }
269 RuntimeAuthWorkflow::install_delegation_proof_batch_root(request)
270 .await
271 .map_err(Self::map_auth_error)
272 }
273
274 pub async fn get_or_create_chain_key_delegation_proof_root()
276 -> Result<RootDelegationProofBatchProof, Error> {
277 EnvOps::require_root().map_err(Error::from)?;
278 RuntimeAuthWorkflow::get_or_create_chain_key_delegation_proof_for_issuer_root(
279 IcOps::msg_caller(),
280 )
281 .await
282 .map_err(Self::map_auth_error)
283 }
284
285 pub fn prepare_role_attestation_root(
287 request: RoleAttestationRequest,
288 ) -> Result<RoleAttestationPrepareResponse, Error> {
289 RuntimeAuthWorkflow::prepare_role_attestation_root(request).map_err(Self::map_auth_error)
290 }
291
292 pub fn get_role_attestation_root(
294 request: RoleAttestationGetRequest,
295 ) -> Result<SignedRoleAttestation, Error> {
296 EnvOps::require_root().map_err(Error::from)?;
297 AuthOps::get_role_attestation(IcOps::msg_caller(), request.payload_hash)
298 .map_err(Self::map_auth_error)
299 }
300
301 pub async fn verify_role_attestation(
303 attestation: &SignedRoleAttestation,
304 min_accepted_epoch: u64,
305 ) -> Result<(), Error> {
306 crate::workflow::runtime::auth::RuntimeAuthWorkflow::verify_role_attestation(
307 attestation,
308 min_accepted_epoch,
309 )
310 .await
311 .map_err(Self::map_auth_error)
312 }
313
314 fn delegated_token_max_ttl_ns() -> Result<u64, Error> {
316 let cfg = ConfigOps::delegated_tokens_config().map_err(Error::from)?;
317 if !cfg.enabled {
318 return Err(Error::forbidden(Self::DELEGATED_TOKENS_DISABLED));
319 }
320
321 let max_ttl_secs = cfg
322 .max_ttl_secs
323 .unwrap_or(Self::MAX_DELEGATED_SESSION_TTL_SECS);
324 max_ttl_secs.checked_mul(1_000_000_000).ok_or_else(|| {
325 Error::invalid("auth.delegated_tokens.max_ttl_secs overflows nanoseconds")
326 })
327 }
328}