canic_core/api/auth/
mod.rs1use crate::{
8 cdk::types::Principal,
9 dto::{
10 auth::{
11 ActiveDelegationProofStatusResponse, DelegatedToken, DelegatedTokenGetRequest,
12 DelegatedTokenPrepareRequest, DelegatedTokenPrepareResponse,
13 InstallActiveDelegationProofRequest, InstallActiveDelegationProofResponse,
14 RoleAttestationGetRequest, RoleAttestationPrepareResponse, RoleAttestationRequest,
15 RootDelegationProofBatchProof, RootIssuerPolicyResponse, RootIssuerPolicyUpsertRequest,
16 RootIssuerRenewalStatusRequest, RootIssuerRenewalStatusResponse,
17 RootIssuerRenewalTemplateResponse, RootIssuerRenewalTemplateUpsertRequest,
18 SignedRoleAttestation,
19 },
20 error::Error,
21 },
22 error::InternalErrorClass,
23 ops::{
24 auth::{AuthOps, VerifyDelegatedTokenRuntimeInput},
25 config::ConfigOps,
26 ic::IcOps,
27 runtime::env::EnvOps,
28 },
29 workflow::runtime::auth::RuntimeAuthWorkflow,
30};
31
32mod session;
35
36pub struct AuthApi;
44
45impl AuthApi {
46 const DELEGATED_TOKENS_DISABLED: &str =
47 "delegated token auth disabled; set auth.delegated_tokens.enabled=true in canic.toml";
48 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";
49 const MAX_DELEGATED_SESSION_TTL_SECS: u64 = 24 * 60 * 60;
50 const SESSION_BOOTSTRAP_TOKEN_FINGERPRINT_DOMAIN: &[u8] =
51 b"canic-session-bootstrap-token-fingerprint";
52
53 fn map_auth_error(err: crate::InternalError) -> Error {
55 match err.class() {
56 InternalErrorClass::Infra | InternalErrorClass::Ops | InternalErrorClass::Workflow => {
57 Error::internal(err.to_string())
58 }
59 _ => Error::from(err),
60 }
61 }
62
63 fn require_delegated_token_issuer_enabled() -> Result<(), Error> {
64 let delegated_tokens_cfg =
65 ConfigOps::delegated_tokens_config().map_err(Self::map_auth_error)?;
66 if !delegated_tokens_cfg.enabled {
67 return Err(Error::invalid(Self::DELEGATED_TOKENS_DISABLED));
68 }
69
70 let canister_cfg = ConfigOps::current_canister().map_err(Self::map_auth_error)?;
71 if !canister_cfg.auth.delegated_token_issuer {
72 return Err(Error::forbidden(Self::DELEGATED_TOKEN_ISSUER_DISABLED));
73 }
74
75 Ok(())
76 }
77
78 fn verify_token_material(
83 token: &DelegatedToken,
84 max_cert_ttl_ns: u64,
85 max_token_ttl_ns: u64,
86 required_scopes: &[String],
87 now_ns: u64,
88 ) -> Result<Principal, Error> {
89 AuthOps::verify_token(VerifyDelegatedTokenRuntimeInput {
90 token,
91 caller: IcOps::msg_caller(),
92 max_cert_ttl_ns,
93 max_token_ttl_ns,
94 required_scopes,
95 now_ns,
96 })
97 .map(|verified| verified.subject)
98 .map_err(Self::map_auth_error)
99 }
100
101 pub async fn prepare_delegated_token(
103 request: DelegatedTokenPrepareRequest,
104 ) -> Result<DelegatedTokenPrepareResponse, Error> {
105 Self::require_delegated_token_issuer_enabled()?;
106 RuntimeAuthWorkflow::prepare_delegated_token(request)
107 .await
108 .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 fn active_delegation_proof_status() -> Result<ActiveDelegationProofStatusResponse, Error> {
134 Self::require_delegated_token_issuer_enabled()?;
135 Ok(AuthOps::active_delegation_proof_status(IcOps::now_nanos()))
136 }
137
138 pub fn upsert_root_issuer_policy_root(
140 request: RootIssuerPolicyUpsertRequest,
141 ) -> Result<RootIssuerPolicyResponse, Error> {
142 EnvOps::require_root().map_err(Error::from)?;
143 AuthOps::upsert_root_issuer_policy(request, IcOps::now_nanos())
144 .map_err(Self::map_auth_error)
145 }
146
147 pub fn upsert_root_issuer_renewal_template_root(
149 request: RootIssuerRenewalTemplateUpsertRequest,
150 ) -> Result<RootIssuerRenewalTemplateResponse, Error> {
151 EnvOps::require_root().map_err(Error::from)?;
152 let response = AuthOps::upsert_root_issuer_renewal_template(request, IcOps::now_nanos())
153 .map_err(Self::map_auth_error)?;
154 if response.template.enabled {
155 RuntimeAuthWorkflow::start_root_delegation_renewal_timer_soon_if_configured()
156 .map_err(Self::map_auth_error)?;
157 }
158 Ok(response)
159 }
160
161 pub fn root_issuer_renewal_status_root(
163 request: RootIssuerRenewalStatusRequest,
164 ) -> Result<RootIssuerRenewalStatusResponse, Error> {
165 EnvOps::require_root().map_err(Error::from)?;
166 Ok(AuthOps::root_issuer_renewal_status(request))
167 }
168
169 pub async fn get_or_create_chain_key_delegation_proof_root()
171 -> Result<RootDelegationProofBatchProof, Error> {
172 EnvOps::require_root().map_err(Error::from)?;
173 RuntimeAuthWorkflow::get_or_create_chain_key_delegation_proof_for_issuer_root(
174 IcOps::msg_caller(),
175 )
176 .await
177 .map_err(Self::map_auth_error)
178 }
179
180 pub fn prepare_role_attestation_root(
182 request: RoleAttestationRequest,
183 ) -> Result<RoleAttestationPrepareResponse, Error> {
184 RuntimeAuthWorkflow::prepare_role_attestation_root(request).map_err(Self::map_auth_error)
185 }
186
187 pub fn get_role_attestation_root(
189 request: RoleAttestationGetRequest,
190 ) -> Result<SignedRoleAttestation, Error> {
191 EnvOps::require_root().map_err(Error::from)?;
192 AuthOps::get_role_attestation(IcOps::msg_caller(), request.payload_hash)
193 .map_err(Self::map_auth_error)
194 }
195
196 pub async fn verify_role_attestation(
198 attestation: &SignedRoleAttestation,
199 min_accepted_epoch: u64,
200 ) -> Result<(), Error> {
201 crate::workflow::runtime::auth::RuntimeAuthWorkflow::verify_role_attestation(
202 attestation,
203 min_accepted_epoch,
204 )
205 .await
206 .map_err(Self::map_auth_error)
207 }
208
209 fn delegated_token_max_ttl_ns() -> Result<u64, Error> {
211 let cfg = ConfigOps::delegated_tokens_config().map_err(Error::from)?;
212 if !cfg.enabled {
213 return Err(Error::forbidden(Self::DELEGATED_TOKENS_DISABLED));
214 }
215
216 let max_ttl_secs = cfg
217 .max_ttl_secs
218 .unwrap_or(Self::MAX_DELEGATED_SESSION_TTL_SECS);
219 max_ttl_secs.checked_mul(1_000_000_000).ok_or_else(|| {
220 Error::invalid("auth.delegated_tokens.max_ttl_secs overflows nanoseconds")
221 })
222 }
223}