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 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 MAX_DELEGATED_SESSION_TTL_SECS: u64 = 24 * 60 * 60;
56 const SESSION_BOOTSTRAP_TOKEN_FINGERPRINT_DOMAIN: &[u8] =
57 b"canic-session-bootstrap-token-fingerprint";
58
59 fn map_auth_error(err: crate::InternalError) -> Error {
61 match err.class() {
62 InternalErrorClass::Infra | InternalErrorClass::Ops | InternalErrorClass::Workflow => {
63 Error::internal(err.to_string())
64 }
65 _ => Error::from(err),
66 }
67 }
68
69 fn require_delegated_token_issuer_enabled() -> Result<(), Error> {
70 let delegated_tokens_cfg =
71 ConfigOps::delegated_tokens_config().map_err(Self::map_auth_error)?;
72 if !delegated_tokens_cfg.enabled {
73 return Err(Error::invalid(Self::DELEGATED_TOKENS_DISABLED));
74 }
75
76 let canister_cfg = ConfigOps::current_canister().map_err(Self::map_auth_error)?;
77 if !canister_cfg.auth.delegated_token_issuer {
78 return Err(Error::forbidden(Self::DELEGATED_TOKEN_ISSUER_DISABLED));
79 }
80
81 Ok(())
82 }
83
84 fn require_delegation_renewal_provisioner_or_controller() -> Result<bool, Error> {
85 let caller = IcOps::msg_caller();
86 if caller_is_controller(&caller) {
87 return Ok(true);
88 }
89 if AuthOps::is_delegation_renewal_provisioner(caller) {
90 return Ok(false);
91 }
92
93 Err(Error::forbidden(
94 "caller is not a controller or enabled delegation renewal provisioner",
95 ))
96 }
97
98 fn verify_token_material(
103 token: &DelegatedToken,
104 max_cert_ttl_ns: u64,
105 max_token_ttl_ns: u64,
106 required_scopes: &[String],
107 now_ns: u64,
108 ) -> Result<Principal, Error> {
109 AuthOps::verify_token(VerifyDelegatedTokenRuntimeInput {
110 token,
111 caller: IcOps::msg_caller(),
112 max_cert_ttl_ns,
113 max_token_ttl_ns,
114 required_scopes,
115 now_ns,
116 })
117 .map(|verified| verified.subject)
118 .map_err(Self::map_auth_error)
119 }
120
121 pub fn prepare_delegated_token(
123 request: DelegatedTokenPrepareRequest,
124 ) -> Result<DelegatedTokenPrepareResponse, Error> {
125 Self::require_delegated_token_issuer_enabled()?;
126 RuntimeAuthWorkflow::prepare_delegated_token(request).map_err(Self::map_auth_error)
127 }
128
129 pub fn get_delegated_token(request: DelegatedTokenGetRequest) -> Result<DelegatedToken, Error> {
131 Self::require_delegated_token_issuer_enabled()?;
132
133 AuthOps::get_delegated_token_issuer_proof(request.claims_hash, IcOps::msg_caller())
134 .map_err(Self::map_auth_error)
135 }
136
137 pub fn install_active_delegation_proof(
139 request: InstallActiveDelegationProofRequest,
140 ) -> Result<InstallActiveDelegationProofResponse, Error> {
141 Self::require_delegated_token_issuer_enabled()?;
142
143 let active_proof =
144 AuthOps::install_active_delegation_proof(request.proof, IcOps::msg_caller())
145 .map_err(Self::map_auth_error)?;
146
147 Ok(InstallActiveDelegationProofResponse { active_proof })
148 }
149
150 pub fn active_delegation_proof_status() -> Result<ActiveDelegationProofStatusResponse, Error> {
152 Self::require_delegated_token_issuer_enabled()?;
153 Ok(AuthOps::active_delegation_proof_status(IcOps::now_nanos()))
154 }
155
156 pub fn upsert_root_issuer_policy_root(
158 request: RootIssuerPolicyUpsertRequest,
159 ) -> Result<RootIssuerPolicyResponse, Error> {
160 EnvOps::require_root().map_err(Error::from)?;
161 AuthOps::upsert_root_issuer_policy(request).map_err(Self::map_auth_error)
162 }
163
164 pub fn upsert_root_issuer_renewal_template_root(
166 request: RootIssuerRenewalTemplateUpsertRequest,
167 ) -> Result<RootIssuerRenewalTemplateResponse, Error> {
168 EnvOps::require_root().map_err(Error::from)?;
169 let response = AuthOps::upsert_root_issuer_renewal_template(request, IcOps::now_nanos())
170 .map_err(Self::map_auth_error)?;
171 if response.template.enabled {
172 RuntimeAuthWorkflow::start_root_delegation_renewal_timer_soon_if_configured()
173 .map_err(Self::map_auth_error)?;
174 }
175 Ok(response)
176 }
177
178 pub fn root_issuer_renewal_status_root(
180 request: RootIssuerRenewalStatusRequest,
181 ) -> Result<RootIssuerRenewalStatusResponse, Error> {
182 EnvOps::require_root().map_err(Error::from)?;
183 Ok(AuthOps::root_issuer_renewal_status(request))
184 }
185
186 pub fn upsert_delegation_renewal_provisioner_root(
188 request: RootDelegationRenewalProvisionerUpsertRequest,
189 ) -> Result<RootDelegationRenewalProvisionerResponse, Error> {
190 EnvOps::require_root().map_err(Error::from)?;
191 Ok(AuthOps::upsert_delegation_renewal_provisioner(request))
192 }
193
194 pub fn delegation_renewal_provisioners_root()
196 -> Result<RootDelegationRenewalProvisionerListResponse, Error> {
197 EnvOps::require_root().map_err(Error::from)?;
198 Ok(AuthOps::delegation_renewal_provisioners())
199 }
200
201 pub fn delegation_renewal_work_root() -> Result<RootDelegationRenewalWorkListResponse, Error> {
203 EnvOps::require_root().map_err(Error::from)?;
204 Self::require_delegation_renewal_provisioner_or_controller()?;
205 Ok(AuthOps::delegation_renewal_work(IcOps::now_nanos()))
206 }
207
208 pub fn prepare_delegation_proof_batch_root(
210 request: RootDelegationProofBatchPrepareRequest,
211 ) -> Result<RootDelegationProofBatchPrepareResponse, Error> {
212 EnvOps::require_root().map_err(Error::from)?;
213 let max_cert_ttl_ns = Self::delegated_token_max_ttl_ns()?;
214 AuthOps::prepare_delegation_proof_batch(request, max_cert_ttl_ns, IcOps::now_nanos())
215 .map_err(Self::map_auth_error)
216 }
217
218 pub fn get_delegation_proof_batch_root(
220 request: RootDelegationProofBatchGetRequest,
221 ) -> Result<RootDelegationProofBatchGetResponse, Error> {
222 EnvOps::require_root().map_err(Error::from)?;
223 AuthOps::get_delegation_proof_batch(request).map_err(Self::map_auth_error)
224 }
225
226 pub fn get_delegation_renewal_proof_batch_root(
228 request: RootDelegationRenewalProofBatchGetRequest,
229 ) -> Result<RootDelegationProofBatchGetResponse, Error> {
230 EnvOps::require_root().map_err(Error::from)?;
231 Self::require_delegation_renewal_provisioner_or_controller()?;
232 AuthOps::get_delegation_renewal_proof_batch(request).map_err(Self::map_auth_error)
233 }
234
235 pub async fn install_delegation_proof_batch_root(
237 request: RootDelegationProofBatchInstallRequest,
238 ) -> Result<RootDelegationProofBatchInstallResponse, Error> {
239 EnvOps::require_root().map_err(Error::from)?;
240 let caller_is_controller = Self::require_delegation_renewal_provisioner_or_controller()?;
241 if !caller_is_controller {
242 AuthOps::ensure_delegation_renewal_batch_scheduled(
243 request.batch_id,
244 IcOps::now_nanos(),
245 )
246 .map_err(Self::map_auth_error)?;
247 }
248 RuntimeAuthWorkflow::install_delegation_proof_batch_root(request)
249 .await
250 .map_err(Self::map_auth_error)
251 }
252
253 pub fn prepare_role_attestation_root(
255 request: RoleAttestationRequest,
256 ) -> Result<RoleAttestationPrepareResponse, Error> {
257 RuntimeAuthWorkflow::prepare_role_attestation_root(request).map_err(Self::map_auth_error)
258 }
259
260 pub fn get_role_attestation_root(
262 request: RoleAttestationGetRequest,
263 ) -> Result<SignedRoleAttestation, Error> {
264 EnvOps::require_root().map_err(Error::from)?;
265 AuthOps::get_role_attestation(IcOps::msg_caller(), request.payload_hash)
266 .map_err(Self::map_auth_error)
267 }
268
269 pub async fn verify_role_attestation(
271 attestation: &SignedRoleAttestation,
272 min_accepted_epoch: u64,
273 ) -> Result<(), Error> {
274 crate::workflow::runtime::auth::RuntimeAuthWorkflow::verify_role_attestation(
275 attestation,
276 min_accepted_epoch,
277 )
278 .await
279 .map_err(Self::map_auth_error)
280 }
281
282 fn delegated_token_max_ttl_ns() -> Result<u64, Error> {
284 let cfg = ConfigOps::delegated_tokens_config().map_err(Error::from)?;
285 if !cfg.enabled {
286 return Err(Error::forbidden(Self::DELEGATED_TOKENS_DISABLED));
287 }
288
289 let max_ttl_secs = cfg
290 .max_ttl_secs
291 .unwrap_or(Self::MAX_DELEGATED_SESSION_TTL_SECS);
292 max_ttl_secs.checked_mul(1_000_000_000).ok_or_else(|| {
293 Error::invalid("auth.delegated_tokens.max_ttl_secs overflows nanoseconds")
294 })
295 }
296}