1pub mod jwt_server {
9 use crate::errors::Result;
10 use crate::storage::AuthStorage;
11 use serde::{Deserialize, Serialize};
12 use std::sync::Arc;
13
14 #[derive(Debug, Clone)]
21 pub struct JwtServerConfig {
22 pub issuer: String,
23 pub key_id: String,
24 }
25
26 impl Default for JwtServerConfig {
27 fn default() -> Self {
28 Self {
29 issuer: "https://auth.example.com".to_string(),
30 key_id: "default".to_string(),
31 }
32 }
33 }
34
35 pub struct JwtServer {
36 config: JwtServerConfig,
37 storage: Arc<dyn AuthStorage>,
38 }
39
40 impl JwtServer {
41 pub async fn new(config: JwtServerConfig, storage: Arc<dyn AuthStorage>) -> Result<Self> {
48 Ok(Self { config, storage })
49 }
50
51 pub async fn initialize(&self) -> Result<()> {
62 Ok(())
63 }
64
65 pub async fn get_well_known_jwt_configuration(&self) -> Result<JwtWellKnownConfiguration> {
73 Ok(JwtWellKnownConfiguration {
74 issuer: self.config.issuer.clone(),
75 jwks_uri: format!("{}/jwks", self.config.issuer),
76 })
77 }
78
79 pub async fn store_jwt_metadata(&self, metadata: &JwtWellKnownConfiguration) -> Result<()> {
87 let key = format!("jwt_metadata:{}", self.config.issuer);
88 let value = serde_json::to_string(metadata).map_err(|e| {
89 crate::errors::AuthError::internal(format!("Serialization error: {}", e))
90 })?;
91
92 self.storage.store_kv(&key, value.as_bytes(), None).await?;
93 tracing::info!("Stored JWT metadata for issuer: {}", self.config.issuer);
94 Ok(())
95 }
96
97 pub async fn get_stored_metadata(&self) -> Result<Option<JwtWellKnownConfiguration>> {
106 let key = format!("jwt_metadata:{}", self.config.issuer);
107
108 if let Some(value_bytes) = self.storage.get_kv(&key).await? {
109 let value = String::from_utf8(value_bytes).map_err(|e| {
110 crate::errors::AuthError::internal(format!("UTF-8 conversion error: {}", e))
111 })?;
112 let metadata: JwtWellKnownConfiguration =
113 serde_json::from_str(&value).map_err(|e| {
114 crate::errors::AuthError::internal(format!("Deserialization error: {}", e))
115 })?;
116 Ok(Some(metadata))
117 } else {
118 Ok(None)
119 }
120 }
121
122 pub async fn store_signing_key(&self, key_data: &str) -> Result<()> {
129 let key = format!("jwt_key:{}", self.config.key_id);
130 self.storage
131 .store_kv(&key, key_data.as_bytes(), None)
132 .await?;
133 tracing::info!("Stored JWT signing key: {}", self.config.key_id);
134 Ok(())
135 }
136 }
137
138 #[derive(Debug, Clone, Serialize, Deserialize)]
145 pub struct JwtWellKnownConfiguration {
146 pub issuer: String,
147 pub jwks_uri: String,
148 }
149}
150
151pub mod api_gateway {
153 use crate::errors::Result;
154 use crate::storage::AuthStorage;
155 use std::sync::Arc;
156
157 #[derive(Debug, Clone)]
164 pub struct ApiGatewayConfig {
165 pub name: String,
166 }
167
168 impl Default for ApiGatewayConfig {
169 fn default() -> Self {
170 Self {
171 name: "API Gateway".to_string(),
172 }
173 }
174 }
175
176 pub struct ApiGateway {
177 config: ApiGatewayConfig,
178 storage: Arc<dyn AuthStorage>,
179 }
180
181 impl ApiGateway {
182 pub async fn new(config: ApiGatewayConfig, storage: Arc<dyn AuthStorage>) -> Result<Self> {
189 Ok(Self { config, storage })
190 }
191
192 pub async fn initialize(&self) -> Result<()> {
203 Ok(())
204 }
205
206 pub async fn store_gateway_metadata(&self) -> Result<()> {
213 let key = format!("api_gateway_config:{}", self.config.name);
214 let metadata = serde_json::json!({
215 "name": self.config.name,
216 "initialized_at": chrono::Utc::now().to_rfc3339()
217 });
218 let value = serde_json::to_string(&metadata).map_err(|e| {
219 crate::errors::AuthError::internal(format!("Serialization error: {}", e))
220 })?;
221
222 self.storage.store_kv(&key, value.as_bytes(), None).await?;
223 tracing::info!("Stored API Gateway metadata for: {}", self.config.name);
224 Ok(())
225 }
226
227 pub async fn store_route_config(&self, route_path: &str, config_data: &str) -> Result<()> {
234 let key = format!("api_gateway_route:{}:{}", self.config.name, route_path);
235 self.storage
236 .store_kv(&key, config_data.as_bytes(), None)
237 .await?;
238 tracing::info!(
239 "Stored route config for {} on gateway: {}",
240 route_path,
241 self.config.name
242 );
243 Ok(())
244 }
245
246 pub async fn get_route_config(&self, route_path: &str) -> Result<Option<String>> {
255 let key = format!("api_gateway_route:{}:{}", self.config.name, route_path);
256
257 if let Some(config_bytes) = self.storage.get_kv(&key).await? {
258 let config = String::from_utf8(config_bytes).map_err(|e| {
259 crate::errors::AuthError::internal(format!("UTF-8 conversion error: {}", e))
260 })?;
261 Ok(Some(config))
262 } else {
263 Ok(None)
264 }
265 }
266
267 pub fn get_gateway_name(&self) -> &str {
274 &self.config.name
275 }
276 }
277}
278
279pub mod saml_idp {
281 use crate::errors::Result;
282 use crate::storage::AuthStorage;
283 use serde::{Deserialize, Serialize};
284 use std::sync::Arc;
285
286 #[derive(Debug, Clone)]
293 pub struct SamlIdpConfig {
294 pub entity_id: String,
295 }
296
297 impl Default for SamlIdpConfig {
298 fn default() -> Self {
299 Self {
300 entity_id: "https://auth.example.com".to_string(),
301 }
302 }
303 }
304
305 pub struct SamlIdentityProvider {
306 config: SamlIdpConfig,
307 storage: Arc<dyn AuthStorage>,
308 }
309
310 impl SamlIdentityProvider {
311 pub async fn new(config: SamlIdpConfig, storage: Arc<dyn AuthStorage>) -> Result<Self> {
318 Ok(Self { config, storage })
319 }
320
321 pub async fn initialize(&self) -> Result<()> {
332 Ok(())
333 }
334
335 pub async fn get_metadata(&self) -> Result<SamlMetadata> {
343 Ok(SamlMetadata {
344 entity_id: self.config.entity_id.clone(),
345 })
346 }
347
348 pub async fn store_saml_metadata(&self, metadata: &SamlMetadata) -> Result<()> {
355 let key = format!("saml_metadata:{}", self.config.entity_id);
356 let value = serde_json::to_string(metadata).map_err(|e| {
357 crate::errors::AuthError::internal(format!("Serialization error: {}", e))
358 })?;
359
360 self.storage.store_kv(&key, value.as_bytes(), None).await?;
361 tracing::info!("Stored SAML metadata for entity: {}", self.config.entity_id);
362 Ok(())
363 }
364
365 pub async fn store_assertion(
372 &self,
373 assertion_id: &str,
374 assertion_data: &str,
375 ) -> Result<()> {
376 let key = format!("saml_assertion:{}:{}", self.config.entity_id, assertion_id);
377 self.storage
378 .store_kv(
379 &key,
380 assertion_data.as_bytes(),
381 Some(std::time::Duration::from_secs(3600)),
382 )
383 .await?;
384 tracing::info!(
385 "Stored SAML assertion {} for entity: {}",
386 assertion_id,
387 self.config.entity_id
388 );
389 Ok(())
390 }
391
392 pub async fn get_assertion(&self, assertion_id: &str) -> Result<Option<String>> {
401 let key = format!("saml_assertion:{}:{}", self.config.entity_id, assertion_id);
402
403 if let Some(assertion_bytes) = self.storage.get_kv(&key).await? {
404 let assertion = String::from_utf8(assertion_bytes).map_err(|e| {
405 crate::errors::AuthError::internal(format!("UTF-8 conversion error: {}", e))
406 })?;
407 Ok(Some(assertion))
408 } else {
409 Ok(None)
410 }
411 }
412 }
413
414 #[derive(Debug, Clone, Serialize, Deserialize)]
421 pub struct SamlMetadata {
422 pub entity_id: String,
423 }
424}
425
426pub mod consent {
429 use crate::errors::Result;
432 use crate::storage::AuthStorage;
433 use serde::{Deserialize, Serialize};
434 use std::collections::HashMap;
435 use std::sync::Arc;
436
437 #[derive(Debug, Clone, Serialize, Deserialize)]
444 pub struct ConsentConfig {
445 pub require_explicit_consent: bool,
447 pub remember_consent_ttl_secs: u64,
449 }
450
451 impl Default for ConsentConfig {
452 fn default() -> Self {
453 Self {
454 require_explicit_consent: true,
455 remember_consent_ttl_secs: 86_400, }
457 }
458 }
459
460 #[derive(Debug, Clone, Serialize, Deserialize)]
470 pub struct ConsentRecord {
471 pub user_id: String,
472 pub client_id: String,
473 pub scopes: Vec<String>,
474 pub granted_at: u64,
475 pub expires_at: Option<u64>,
476 }
477
478 pub struct ConsentManager {
485 config: ConsentConfig,
486 records: HashMap<String, ConsentRecord>,
488 storage: Option<Arc<dyn AuthStorage>>,
489 }
490
491 impl std::fmt::Debug for ConsentManager {
492 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
493 f.debug_struct("ConsentManager")
494 .field("config", &self.config)
495 .field("records", &self.records)
496 .field("storage", &self.storage.is_some())
497 .finish()
498 }
499 }
500
501 impl Default for ConsentManager {
502 fn default() -> Self {
503 Self::new(ConsentConfig::default())
504 }
505 }
506
507 impl ConsentManager {
508 pub fn new(config: ConsentConfig) -> Self {
515 Self {
516 config,
517 records: HashMap::new(),
518 storage: None,
519 }
520 }
521
522 pub fn new_with_storage(config: ConsentConfig, storage: Arc<dyn AuthStorage>) -> Self {
530 Self {
531 config,
532 records: HashMap::new(),
533 storage: Some(storage),
534 }
535 }
536
537 fn storage_key(user_id: &str, client_id: &str) -> String {
539 format!("consent:{}:{}", user_id, client_id)
540 }
541
542 pub async fn grant(&mut self, record: ConsentRecord) -> Result<()> {
549 let key = format!("{}:{}", record.user_id, record.client_id);
550 if let Some(storage) = &self.storage {
551 let storage_key = Self::storage_key(&record.user_id, &record.client_id);
552 let ttl = if record.expires_at.is_some() {
553 Some(std::time::Duration::from_secs(
554 self.config.remember_consent_ttl_secs,
555 ))
556 } else {
557 None
558 };
559 let bytes = serde_json::to_vec(&record).map_err(|e| {
560 crate::errors::AuthError::internal(format!(
561 "Consent serialization error: {}",
562 e
563 ))
564 })?;
565 storage.store_kv(&storage_key, &bytes, ttl).await?;
566 }
567 self.records.insert(key, record);
568 Ok(())
569 }
570
571 pub async fn revoke(&mut self, user_id: &str, client_id: &str) -> Result<bool> {
578 let key = format!("{}:{}", user_id, client_id);
579 if let Some(storage) = &self.storage {
580 let storage_key = Self::storage_key(user_id, client_id);
581 let _ = storage.delete_kv(&storage_key).await;
583 }
584 Ok(self.records.remove(&key).is_some())
585 }
586
587 pub async fn has_consent(
595 &mut self,
596 user_id: &str,
597 client_id: &str,
598 scopes: &[String],
599 ) -> Result<bool> {
600 if !self.config.require_explicit_consent {
601 return Ok(true);
602 }
603 let key = format!("{}:{}", user_id, client_id);
604
605 if !self.records.contains_key(&key)
607 && let Some(storage) = &self.storage
608 {
609 let storage_key = Self::storage_key(user_id, client_id);
610 if let Ok(Some(bytes)) = storage.get_kv(&storage_key).await
611 && let Ok(record) = serde_json::from_slice::<ConsentRecord>(&bytes)
612 {
613 self.records.insert(key.clone(), record);
614 }
615 }
616
617 Ok(self
618 .records
619 .get(&key)
620 .is_some_and(|record| scopes.iter().all(|s| record.scopes.contains(s))))
621 }
622
623 pub fn config(&self) -> &ConsentConfig {
631 &self.config
632 }
633 }
634}
635
636pub mod introspection {
637 use serde::{Deserialize, Serialize};
640
641 #[derive(Debug, Clone, Serialize, Deserialize)]
648 pub struct IntrospectionConfig {
649 pub restrict_to_registered_servers: bool,
651 pub include_claims: bool,
653 }
654
655 impl Default for IntrospectionConfig {
656 fn default() -> Self {
657 Self {
658 restrict_to_registered_servers: false,
659 include_claims: true,
660 }
661 }
662 }
663
664 #[derive(Debug, Clone, Serialize, Deserialize)]
672 pub struct IntrospectionResponse {
673 pub active: bool,
674 #[serde(skip_serializing_if = "Option::is_none")]
675 pub scope: Option<String>,
676 #[serde(skip_serializing_if = "Option::is_none")]
677 pub client_id: Option<String>,
678 #[serde(skip_serializing_if = "Option::is_none")]
679 pub sub: Option<String>,
680 #[serde(skip_serializing_if = "Option::is_none")]
681 pub exp: Option<i64>,
682 #[serde(skip_serializing_if = "Option::is_none")]
683 pub iat: Option<i64>,
684 #[serde(skip_serializing_if = "Option::is_none")]
685 pub token_type: Option<String>,
686 }
687
688 impl IntrospectionResponse {
689 pub fn inactive() -> Self {
696 Self {
697 active: false,
698 scope: None,
699 client_id: None,
700 sub: None,
701 exp: None,
702 iat: None,
703 token_type: None,
704 }
705 }
706 }
707
708 #[derive(Debug, Default)]
710 pub struct IntrospectionManager {
711 config: IntrospectionConfig,
712 }
713
714 impl IntrospectionManager {
715 pub fn new(config: IntrospectionConfig) -> Self {
722 Self { config }
723 }
724
725 pub fn config(&self) -> &IntrospectionConfig {
732 &self.config
733 }
734 }
735}
736
737pub mod device_flow_server {
738 use crate::errors::Result;
741 use crate::storage::AuthStorage;
742 use serde::{Deserialize, Serialize};
743 use std::collections::HashMap;
744 use std::sync::Arc;
745
746 #[derive(Debug, Clone, Serialize, Deserialize)]
754 pub struct DeviceFlowConfig {
755 pub user_code_length: usize,
757 pub device_code_ttl_secs: u64,
759 pub polling_interval_secs: u64,
761 pub verification_uri: String,
763 }
764
765 impl Default for DeviceFlowConfig {
766 fn default() -> Self {
767 Self {
768 user_code_length: 8,
769 device_code_ttl_secs: 1800, polling_interval_secs: 5,
771 verification_uri: "https://example.com/device".to_string(),
772 }
773 }
774 }
775
776 #[derive(Debug, Clone, Serialize, Deserialize)]
783 pub enum DeviceAuthState {
784 Pending,
785 Authorized { access_token: String },
786 Denied,
787 Expired,
788 }
789
790 #[derive(Debug, Clone, Serialize, Deserialize)]
800 pub struct DeviceAuthRecord {
801 pub device_code: String,
802 pub user_code: String,
803 pub client_id: String,
804 pub scopes: Vec<String>,
805 pub state: DeviceAuthState,
806 pub expires_at: u64,
807 }
808
809 pub struct DeviceFlowManager {
817 config: DeviceFlowConfig,
818 records: HashMap<String, DeviceAuthRecord>,
820 storage: Option<Arc<dyn AuthStorage>>,
821 }
822
823 impl std::fmt::Debug for DeviceFlowManager {
824 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
825 f.debug_struct("DeviceFlowManager")
826 .field("config", &self.config)
827 .field("records", &self.records)
828 .field("storage", &self.storage.is_some())
829 .finish()
830 }
831 }
832
833 impl Default for DeviceFlowManager {
834 fn default() -> Self {
835 Self::new(DeviceFlowConfig::default())
836 }
837 }
838
839 impl DeviceFlowManager {
840 pub fn new(config: DeviceFlowConfig) -> Self {
847 Self {
848 config,
849 records: HashMap::new(),
850 storage: None,
851 }
852 }
853
854 pub fn new_with_storage(config: DeviceFlowConfig, storage: Arc<dyn AuthStorage>) -> Self {
862 Self {
863 config,
864 records: HashMap::new(),
865 storage: Some(storage),
866 }
867 }
868
869 fn storage_key(device_code: &str) -> String {
871 format!("device_flow:{}", device_code)
872 }
873
874 pub async fn register(&mut self, record: DeviceAuthRecord) -> Result<()> {
881 if let Some(storage) = &self.storage {
882 let key = Self::storage_key(&record.device_code);
883 let ttl = Some(std::time::Duration::from_secs(
884 self.config.device_code_ttl_secs,
885 ));
886 let bytes = serde_json::to_vec(&record).map_err(|e| {
887 crate::errors::AuthError::internal(format!(
888 "DeviceFlowRecord serialization error: {}",
889 e
890 ))
891 })?;
892 storage.store_kv(&key, &bytes, ttl).await?;
893 }
894 self.records.insert(record.device_code.clone(), record);
895 Ok(())
896 }
897
898 pub async fn get(&mut self, device_code: &str) -> Result<Option<DeviceAuthRecord>> {
907 if let Some(record) = self.records.get(device_code) {
908 return Ok(Some(record.clone()));
909 }
910 if let Some(storage) = &self.storage {
911 let key = Self::storage_key(device_code);
912 if let Some(bytes) = storage.get_kv(&key).await?
913 && let Ok(record) = serde_json::from_slice::<DeviceAuthRecord>(&bytes)
914 {
915 self.records.insert(device_code.to_string(), record.clone());
916 return Ok(Some(record));
917 }
918 }
919 Ok(None)
920 }
921
922 pub async fn approve(&mut self, user_code: &str, access_token: String) -> Result<bool> {
929 let device_code = self
931 .records
932 .values()
933 .find(|r| r.user_code == user_code)
934 .map(|r| r.device_code.clone());
935
936 if let Some(dc) = device_code {
937 if let Some(record) = self.records.get_mut(&dc) {
938 record.state = DeviceAuthState::Authorized {
939 access_token: access_token.clone(),
940 };
941 if let Some(storage) = &self.storage {
942 let key = Self::storage_key(&dc);
943 let bytes = serde_json::to_vec(&*record).map_err(|e| {
944 crate::errors::AuthError::internal(format!(
945 "DeviceFlowRecord serialization error: {}",
946 e
947 ))
948 })?;
949 storage.store_kv(&key, &bytes, None).await?;
951 }
952 }
953 return Ok(true);
954 }
955 Ok(false)
956 }
957
958 pub fn config(&self) -> &DeviceFlowConfig {
966 &self.config
967 }
968 }
969}
970
971#[cfg(test)]
972mod tests {
973 use super::*;
974 use crate::storage::MemoryStorage;
975 use std::sync::Arc;
976 use std::time::{SystemTime, UNIX_EPOCH};
977
978 fn now_secs() -> u64 {
979 SystemTime::now()
980 .duration_since(UNIX_EPOCH)
981 .unwrap()
982 .as_secs()
983 }
984
985 #[tokio::test]
988 async fn test_jwt_server_store_and_get_metadata() {
989 let storage: Arc<dyn crate::storage::AuthStorage> = Arc::new(MemoryStorage::new());
990 let config = jwt_server::JwtServerConfig {
991 issuer: "https://auth.example.com".into(),
992 key_id: "key_1".into(),
993 };
994 let server = jwt_server::JwtServer::new(config, storage).await.unwrap();
995 server.initialize().await.unwrap();
996
997 let wkc = server.get_well_known_jwt_configuration().await.unwrap();
998 assert_eq!(wkc.issuer, "https://auth.example.com");
999
1000 server.store_jwt_metadata(&wkc).await.unwrap();
1001 let retrieved = server.get_stored_metadata().await.unwrap();
1002 assert!(retrieved.is_some());
1003 }
1004
1005 #[tokio::test]
1006 async fn test_jwt_server_store_signing_key() {
1007 let storage: Arc<dyn crate::storage::AuthStorage> = Arc::new(MemoryStorage::new());
1008 let config = jwt_server::JwtServerConfig {
1009 issuer: "https://auth.example.com".into(),
1010 key_id: "key_2".into(),
1011 };
1012 let server = jwt_server::JwtServer::new(config, storage).await.unwrap();
1013 server.store_signing_key("test-key-data").await.unwrap();
1014 }
1015
1016 #[tokio::test]
1019 async fn test_api_gateway_store_and_get_route() {
1020 let storage: Arc<dyn crate::storage::AuthStorage> = Arc::new(MemoryStorage::new());
1021 let config = api_gateway::ApiGatewayConfig {
1022 name: "test-gw".into(),
1023 };
1024 let gw = api_gateway::ApiGateway::new(config, storage).await.unwrap();
1025 gw.initialize().await.unwrap();
1026 assert_eq!(gw.get_gateway_name(), "test-gw");
1027
1028 gw.store_route_config("/api/v1/users", r#"{"auth":"required"}"#)
1029 .await
1030 .unwrap();
1031 let route = gw.get_route_config("/api/v1/users").await.unwrap();
1032 assert!(route.is_some());
1033 }
1034
1035 #[tokio::test]
1036 async fn test_api_gateway_get_route_not_found() {
1037 let storage: Arc<dyn crate::storage::AuthStorage> = Arc::new(MemoryStorage::new());
1038 let config = api_gateway::ApiGatewayConfig { name: "gw2".into() };
1039 let gw = api_gateway::ApiGateway::new(config, storage).await.unwrap();
1040 assert!(gw.get_route_config("/nope").await.unwrap().is_none());
1041 }
1042
1043 #[tokio::test]
1046 async fn test_saml_idp_store_and_get_assertion() {
1047 let storage: Arc<dyn crate::storage::AuthStorage> = Arc::new(MemoryStorage::new());
1048 let config = saml_idp::SamlIdpConfig {
1049 entity_id: "urn:example:idp".into(),
1050 };
1051 let saml = saml_idp::SamlIdentityProvider::new(config, storage)
1052 .await
1053 .unwrap();
1054 saml.initialize().await.unwrap();
1055 saml.store_assertion("assert_1", "<Assertion/>")
1056 .await
1057 .unwrap();
1058 let a = saml.get_assertion("assert_1").await.unwrap();
1059 assert_eq!(a.as_deref(), Some("<Assertion/>"));
1060 }
1061
1062 #[tokio::test]
1063 async fn test_saml_idp_get_assertion_not_found() {
1064 let storage: Arc<dyn crate::storage::AuthStorage> = Arc::new(MemoryStorage::new());
1065 let config = saml_idp::SamlIdpConfig {
1066 entity_id: "urn:example:idp2".into(),
1067 };
1068 let saml = saml_idp::SamlIdentityProvider::new(config, storage)
1069 .await
1070 .unwrap();
1071 assert!(saml.get_assertion("nope").await.unwrap().is_none());
1072 }
1073
1074 #[tokio::test]
1075 async fn test_saml_idp_metadata() {
1076 let storage: Arc<dyn crate::storage::AuthStorage> = Arc::new(MemoryStorage::new());
1077 let config = saml_idp::SamlIdpConfig {
1078 entity_id: "urn:example:idp3".into(),
1079 };
1080 let saml = saml_idp::SamlIdentityProvider::new(config, storage)
1081 .await
1082 .unwrap();
1083 let meta = saml.get_metadata().await.unwrap();
1084 assert_eq!(meta.entity_id, "urn:example:idp3");
1085 }
1086
1087 #[tokio::test]
1090 async fn test_consent_grant_and_check() {
1091 let config = consent::ConsentConfig {
1092 require_explicit_consent: true,
1093 remember_consent_ttl_secs: 3600,
1094 };
1095 let mut cm = consent::ConsentManager::new(config);
1096 let record = consent::ConsentRecord {
1097 user_id: "user1".into(),
1098 client_id: "client1".into(),
1099 scopes: vec!["read".into(), "write".into()],
1100 granted_at: now_secs(),
1101 expires_at: Some(now_secs() + 3600),
1102 };
1103 cm.grant(record).await.unwrap();
1104 assert!(
1105 cm.has_consent("user1", "client1", &["read".into()])
1106 .await
1107 .unwrap()
1108 );
1109 }
1110
1111 #[tokio::test]
1112 async fn test_consent_no_consent_by_default() {
1113 let config = consent::ConsentConfig {
1114 require_explicit_consent: true,
1115 remember_consent_ttl_secs: 3600,
1116 };
1117 let mut cm = consent::ConsentManager::new(config);
1118 assert!(
1119 !cm.has_consent("user1", "client1", &["read".into()])
1120 .await
1121 .unwrap()
1122 );
1123 }
1124
1125 #[tokio::test]
1126 async fn test_consent_revoke() {
1127 let config = consent::ConsentConfig {
1128 require_explicit_consent: true,
1129 remember_consent_ttl_secs: 3600,
1130 };
1131 let mut cm = consent::ConsentManager::new(config);
1132 cm.grant(consent::ConsentRecord {
1133 user_id: "user2".into(),
1134 client_id: "client2".into(),
1135 scopes: vec!["read".into()],
1136 granted_at: now_secs(),
1137 expires_at: None,
1138 })
1139 .await
1140 .unwrap();
1141 let revoked = cm.revoke("user2", "client2").await.unwrap();
1142 assert!(revoked);
1143 assert!(
1144 !cm.has_consent("user2", "client2", &["read".into()])
1145 .await
1146 .unwrap()
1147 );
1148 }
1149
1150 #[tokio::test]
1151 async fn test_consent_revoke_nonexistent() {
1152 let config = consent::ConsentConfig {
1153 require_explicit_consent: true,
1154 remember_consent_ttl_secs: 3600,
1155 };
1156 let mut cm = consent::ConsentManager::new(config);
1157 let revoked = cm.revoke("ghost", "ghost_client").await.unwrap();
1158 assert!(!revoked);
1159 }
1160
1161 #[tokio::test]
1162 async fn test_consent_with_storage() {
1163 let storage: Arc<dyn crate::storage::AuthStorage> = Arc::new(MemoryStorage::new());
1164 let config = consent::ConsentConfig {
1165 require_explicit_consent: true,
1166 remember_consent_ttl_secs: 3600,
1167 };
1168 let mut cm = consent::ConsentManager::new_with_storage(config, storage);
1169 cm.grant(consent::ConsentRecord {
1170 user_id: "user3".into(),
1171 client_id: "client3".into(),
1172 scopes: vec!["openid".into()],
1173 granted_at: now_secs(),
1174 expires_at: None,
1175 })
1176 .await
1177 .unwrap();
1178 assert!(
1179 cm.has_consent("user3", "client3", &["openid".into()])
1180 .await
1181 .unwrap()
1182 );
1183 }
1184
1185 #[tokio::test]
1188 async fn test_device_flow_register_and_get() {
1189 let config = device_flow_server::DeviceFlowConfig {
1190 user_code_length: 8,
1191 device_code_ttl_secs: 300,
1192 polling_interval_secs: 5,
1193 verification_uri: "https://auth.example.com/device".into(),
1194 };
1195 let mut df = device_flow_server::DeviceFlowManager::new(config);
1196 df.register(device_flow_server::DeviceAuthRecord {
1197 device_code: "dev_code_1".into(),
1198 user_code: "ABCD-EFGH".into(),
1199 client_id: "mobile_app".into(),
1200 scopes: vec!["read".into()],
1201 state: device_flow_server::DeviceAuthState::Pending,
1202 expires_at: now_secs() + 300,
1203 })
1204 .await
1205 .unwrap();
1206 let record = df.get("dev_code_1").await.unwrap();
1207 assert!(record.is_some());
1208 assert_eq!(record.unwrap().user_code, "ABCD-EFGH");
1209 }
1210
1211 #[tokio::test]
1212 async fn test_device_flow_get_not_found() {
1213 let config = device_flow_server::DeviceFlowConfig {
1214 user_code_length: 8,
1215 device_code_ttl_secs: 300,
1216 polling_interval_secs: 5,
1217 verification_uri: "https://auth.example.com/device".into(),
1218 };
1219 let mut df = device_flow_server::DeviceFlowManager::new(config);
1220 assert!(df.get("nonexistent").await.unwrap().is_none());
1221 }
1222
1223 #[tokio::test]
1224 async fn test_device_flow_approve() {
1225 let config = device_flow_server::DeviceFlowConfig {
1226 user_code_length: 8,
1227 device_code_ttl_secs: 300,
1228 polling_interval_secs: 5,
1229 verification_uri: "https://auth.example.com/device".into(),
1230 };
1231 let mut df = device_flow_server::DeviceFlowManager::new(config);
1232 df.register(device_flow_server::DeviceAuthRecord {
1233 device_code: "dev_code_2".into(),
1234 user_code: "WXYZ-1234".into(),
1235 client_id: "tv_app".into(),
1236 scopes: vec!["read".into()],
1237 state: device_flow_server::DeviceAuthState::Pending,
1238 expires_at: now_secs() + 300,
1239 })
1240 .await
1241 .unwrap();
1242 let approved = df
1243 .approve("WXYZ-1234", "access_token_here".into())
1244 .await
1245 .unwrap();
1246 assert!(approved);
1247
1248 let record = df.get("dev_code_2").await.unwrap().unwrap();
1249 matches!(
1250 record.state,
1251 device_flow_server::DeviceAuthState::Authorized { .. }
1252 );
1253 }
1254
1255 #[tokio::test]
1256 async fn test_device_flow_approve_nonexistent() {
1257 let config = device_flow_server::DeviceFlowConfig {
1258 user_code_length: 8,
1259 device_code_ttl_secs: 300,
1260 polling_interval_secs: 5,
1261 verification_uri: "https://auth.example.com/device".into(),
1262 };
1263 let mut df = device_flow_server::DeviceFlowManager::new(config);
1264 let approved = df.approve("NO_CODE", "token".into()).await.unwrap();
1265 assert!(!approved);
1266 }
1267
1268 #[tokio::test]
1269 async fn test_device_flow_with_storage() {
1270 let storage: Arc<dyn crate::storage::AuthStorage> = Arc::new(MemoryStorage::new());
1271 let config = device_flow_server::DeviceFlowConfig {
1272 user_code_length: 8,
1273 device_code_ttl_secs: 300,
1274 polling_interval_secs: 5,
1275 verification_uri: "https://auth.example.com/device".into(),
1276 };
1277 let mut df = device_flow_server::DeviceFlowManager::new_with_storage(config, storage);
1278 df.register(device_flow_server::DeviceAuthRecord {
1279 device_code: "stored_code".into(),
1280 user_code: "ST0R-ED00".into(),
1281 client_id: "app".into(),
1282 scopes: vec![],
1283 state: device_flow_server::DeviceAuthState::Pending,
1284 expires_at: now_secs() + 300,
1285 })
1286 .await
1287 .unwrap();
1288 let record = df.get("stored_code").await.unwrap();
1289 assert!(record.is_some());
1290 }
1291
1292 #[test]
1295 fn test_introspection_inactive_response() {
1296 let resp = introspection::IntrospectionResponse::inactive();
1297 assert!(!resp.active);
1298 }
1299}