1use crate::prelude::{OperationError, Url};
2use crate::server::identity::Source;
3use compact_jwt::JwsCompact;
4use crypto_glue::{s256::Sha256Output, x509::Certificate};
5use kanidm_proto::{
6 internal::UserAuthToken,
7 oauth2::{
8 AccessTokenIntrospectRequest, AccessTokenIntrospectResponse, AccessTokenRequest,
9 AccessTokenResponse, AuthorisationRequest,
10 },
11 v1::{
12 AuthAllowed, AuthCredential as ProtoAuthCredential, AuthIssueSession, AuthMech,
13 AuthStep as ProtoAuthStep,
14 },
15};
16use std::fmt;
17use webauthn_rs::prelude::PublicKeyCredential;
18
19#[derive(Debug)]
20pub enum AuthStep {
21 Init(String),
22 Init2 {
23 username: String,
24 issue: AuthIssueSession,
25 privileged: bool,
26 },
27 Begin(AuthMech),
28 Cred(AuthCredential),
29}
30
31impl From<ProtoAuthStep> for AuthStep {
32 fn from(proto: ProtoAuthStep) -> Self {
33 match proto {
34 ProtoAuthStep::Init(name) => Self::Init(name),
35 ProtoAuthStep::Init2 {
36 username,
37 issue,
38 privileged,
39 } => Self::Init2 {
40 username,
41 issue,
42 privileged,
43 },
44 ProtoAuthStep::Begin(mech) => Self::Begin(mech),
45 ProtoAuthStep::Cred(proto_cred) => Self::Cred(AuthCredential::from(proto_cred)),
46 }
47 }
48}
49
50pub enum AuthExternal {
51 OAuth2AuthorisationRequest {
52 authorisation_url: Url,
53 request: AuthorisationRequest,
54 },
55 OAuth2AccessTokenRequest {
56 token_url: Url,
57 client_id: String,
58 client_secret: String,
59 request: AccessTokenRequest,
60 },
61 OAuth2AccessTokenIntrospectionRequest {
62 introspection_url: Url,
63 client_id: String,
64 client_secret: String,
65 request: AccessTokenIntrospectRequest,
66 },
67}
68
69impl fmt::Debug for AuthExternal {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 match self {
72 Self::OAuth2AuthorisationRequest { .. } => write!(f, "OAuth2AuthorisationRequest"),
73 Self::OAuth2AccessTokenRequest { .. } => write!(f, "OAuth2AccessTokenRequest"),
74 Self::OAuth2AccessTokenIntrospectionRequest { .. } => {
75 write!(f, "OAuth2AccessTokenIntrospectionRequest")
76 }
77 }
78 }
79}
80
81#[allow(clippy::large_enum_variant)]
84pub enum AuthState {
85 Choose(Vec<AuthMech>),
86 Continue(Vec<AuthAllowed>),
87
88 External(AuthExternal),
92 Denied(String),
94 Success(Box<JwsCompact>, AuthIssueSession),
95}
96
97impl fmt::Debug for AuthState {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 match self {
100 AuthState::Choose(mechs) => write!(f, "AuthState::Choose({mechs:?})"),
101 AuthState::Continue(allow) => write!(f, "AuthState::Continue({allow:?})"),
102 AuthState::External(allow) => write!(f, "AuthState::External({allow:?})"),
103 AuthState::Denied(reason) => write!(f, "AuthState::Denied({reason:?})"),
104 AuthState::Success(_token, issue) => {
105 write!(f, "AuthState::Success({})", issue)
106 }
107 }
108 }
109}
110
111pub enum AuthCredential {
112 Anonymous,
113 Password(String),
114 Totp(u32),
115 SecurityKey(Box<PublicKeyCredential>),
116 BackupCode(String),
117 Passkey(Box<PublicKeyCredential>),
118
119 OAuth2AuthorisationResponse {
121 code: String,
122 state: Option<String>,
123 },
124 OAuth2AccessTokenResponse {
125 response: AccessTokenResponse,
126 },
127 OAuth2AccessTokenIntrospectResponse {
128 response: AccessTokenIntrospectResponse,
129 },
130}
131
132impl From<ProtoAuthCredential> for AuthCredential {
133 fn from(proto: ProtoAuthCredential) -> Self {
134 match proto {
135 ProtoAuthCredential::Anonymous => AuthCredential::Anonymous,
136 ProtoAuthCredential::Password(p) => AuthCredential::Password(p),
137 ProtoAuthCredential::Totp(t) => AuthCredential::Totp(t),
138 ProtoAuthCredential::SecurityKey(sk) => AuthCredential::SecurityKey(sk),
139 ProtoAuthCredential::BackupCode(bc) => AuthCredential::BackupCode(bc),
140 ProtoAuthCredential::Passkey(pkc) => AuthCredential::Passkey(pkc),
141 }
142 }
143}
144
145impl fmt::Debug for AuthCredential {
146 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
147 match self {
148 AuthCredential::Anonymous => write!(fmt, "Anonymous"),
149 AuthCredential::Password(_) => write!(fmt, "Password(_)"),
150 AuthCredential::Totp(_) => write!(fmt, "TOTP(_)"),
151 AuthCredential::SecurityKey(_) => write!(fmt, "SecurityKey(_)"),
152 AuthCredential::BackupCode(_) => write!(fmt, "BackupCode(_)"),
153 AuthCredential::Passkey(_) => write!(fmt, "Passkey(_)"),
154 AuthCredential::OAuth2AuthorisationResponse { .. } => {
155 write!(fmt, "OAuth2AuthorisationResponse{{..}}")
156 }
157 AuthCredential::OAuth2AccessTokenResponse { .. } => {
158 write!(fmt, "OAuth2AccessTokenResponse{{..}}")
159 }
160 AuthCredential::OAuth2AccessTokenIntrospectResponse { .. } => {
161 write!(fmt, "OAuth2AccessTokenIntrospectResponse{{..}}")
162 }
163 }
164 }
165}
166
167#[derive(Default)]
168pub enum ReauthRequest {
169 #[default]
170 VerifyCredentials,
171 GrantReadWrite,
172}
173
174#[derive(Debug, Clone, Default)]
175pub(crate) enum PreValidatedTokenStatus {
176 #[default]
177 None,
178 Valid(Box<UserAuthToken>),
179 NotAuthenticated,
180 SessionExpired,
181}
182
183#[derive(Debug, Clone)]
184pub struct ClientAuthInfo {
185 pub(crate) source: Source,
186 pub(crate) client_cert: Option<ClientCertInfo>,
187 pub(crate) bearer_token: Option<JwsCompact>,
188 pub(crate) basic_authz: Option<String>,
189 pub(crate) pre_validated_token: PreValidatedTokenStatus,
190}
191
192impl ClientAuthInfo {
193 pub fn new(
194 source: Source,
195 client_cert: Option<ClientCertInfo>,
196 bearer_token: Option<JwsCompact>,
197 basic_authz: Option<String>,
198 ) -> Self {
199 Self {
200 source,
201 client_cert,
202 bearer_token,
203 basic_authz,
204 pre_validated_token: Default::default(),
205 }
206 }
207
208 pub fn bearer_token(&self) -> Option<&JwsCompact> {
209 self.bearer_token.as_ref()
210 }
211
212 pub fn pre_validated_uat(&self) -> Result<&UserAuthToken, OperationError> {
213 match &self.pre_validated_token {
214 PreValidatedTokenStatus::Valid(uat) => Ok(uat),
215 PreValidatedTokenStatus::None => Err(OperationError::AU0008ClientAuthInfoPrevalidation),
216 PreValidatedTokenStatus::NotAuthenticated => Err(OperationError::NotAuthenticated),
217 PreValidatedTokenStatus::SessionExpired => Err(OperationError::SessionExpired),
218 }
219 }
220
221 pub(crate) fn set_pre_validated_uat(&mut self, status: PreValidatedTokenStatus) {
222 self.pre_validated_token = status
223 }
224}
225
226#[derive(Debug, Clone)]
227pub struct ClientCertInfo {
228 pub public_key_s256: Sha256Output,
229 pub certificate: Certificate,
230}
231
232#[cfg(test)]
233impl ClientAuthInfo {
234 pub(crate) fn none() -> Self {
235 ClientAuthInfo {
236 source: Source::Internal,
237 client_cert: None,
238 bearer_token: None,
239 basic_authz: None,
240 pre_validated_token: Default::default(),
241 }
242 }
243}
244
245#[cfg(test)]
246impl From<Source> for ClientAuthInfo {
247 fn from(value: Source) -> ClientAuthInfo {
248 ClientAuthInfo {
249 source: value,
250 client_cert: None,
251 bearer_token: None,
252 basic_authz: None,
253 pre_validated_token: Default::default(),
254 }
255 }
256}
257
258#[cfg(test)]
259impl From<JwsCompact> for ClientAuthInfo {
260 fn from(value: JwsCompact) -> ClientAuthInfo {
261 ClientAuthInfo {
262 source: Source::Internal,
263 client_cert: None,
264 bearer_token: Some(value),
265 basic_authz: None,
266 pre_validated_token: Default::default(),
267 }
268 }
269}
270
271#[cfg(test)]
272impl From<ClientCertInfo> for ClientAuthInfo {
273 fn from(value: ClientCertInfo) -> ClientAuthInfo {
274 ClientAuthInfo {
275 source: Source::Internal,
276 client_cert: Some(value),
277 bearer_token: None,
278 basic_authz: None,
279 pre_validated_token: Default::default(),
280 }
281 }
282}
283
284#[cfg(test)]
285impl From<&str> for ClientAuthInfo {
286 fn from(value: &str) -> ClientAuthInfo {
287 ClientAuthInfo {
288 source: Source::Internal,
289 client_cert: None,
290 bearer_token: None,
291 basic_authz: Some(value.to_string()),
292 pre_validated_token: Default::default(),
293 }
294 }
295}
296
297#[cfg(test)]
298impl ClientAuthInfo {
299 pub(crate) fn encode_basic(id: &str, secret: &str) -> ClientAuthInfo {
300 use base64::{engine::general_purpose, Engine as _};
301 let value = format!("{id}:{secret}");
302 let value = general_purpose::STANDARD.encode(&value);
303 ClientAuthInfo {
304 source: Source::Internal,
305 client_cert: None,
306 bearer_token: None,
307 basic_authz: Some(value),
308 pre_validated_token: Default::default(),
309 }
310 }
311}