Skip to main content

static_authn_plugin/
module.rs

1//! Static `AuthN` resolver plugin module.
2
3use std::sync::{Arc, OnceLock};
4
5use async_trait::async_trait;
6use authn_resolver_sdk::{AuthNResolverPluginClient, AuthNResolverPluginSpecV1};
7use modkit::Module;
8use modkit::client_hub::ClientScope;
9use modkit::context::ModuleCtx;
10use modkit::gts::BaseModkitPluginV1;
11use tracing::info;
12use types_registry_sdk::{RegisterResult, TypesRegistryClient};
13
14use crate::config::StaticAuthNPluginConfig;
15use crate::domain::Service;
16
17/// Static `AuthN` resolver plugin module.
18///
19/// Provides token-to-identity mapping from configuration.
20///
21/// **Plugin registration pattern:**
22/// - Gateway registers the plugin schema (GTS type definition)
23/// - This plugin registers its instance (implementation metadata)
24/// - This plugin registers its scoped client (implementation in `ClientHub`)
25#[modkit::module(
26    name = "static-authn-plugin",
27    deps = ["types-registry"]
28)]
29pub struct StaticAuthNPlugin {
30    service: OnceLock<Arc<Service>>,
31}
32
33impl Default for StaticAuthNPlugin {
34    fn default() -> Self {
35        Self {
36            service: OnceLock::new(),
37        }
38    }
39}
40
41#[async_trait]
42impl Module for StaticAuthNPlugin {
43    async fn init(&self, ctx: &ModuleCtx) -> anyhow::Result<()> {
44        info!("Initializing static_authn_plugin");
45
46        // Load configuration
47        let cfg: StaticAuthNPluginConfig = ctx.config()?;
48        if matches!(cfg.mode, crate::config::AuthNMode::AcceptAll) {
49            tracing::warn!(
50                "Static AuthN plugin is running in `accept_all` mode \u{2014} \
51                 all bearer tokens will be accepted with a hardcoded identity. \
52                 Do NOT use this mode in production."
53            );
54        }
55
56        info!(
57            vendor = %cfg.vendor,
58            priority = cfg.priority,
59            mode = ?cfg.mode,
60            token_count = cfg.tokens.len(),
61            "Loaded plugin configuration"
62        );
63
64        // Generate plugin instance ID
65        let instance_id = AuthNResolverPluginSpecV1::gts_make_instance_id(
66            "hyperspot.builtin.static_authn_resolver.plugin.v1",
67        );
68
69        // Register plugin instance in types-registry
70        let registry = ctx.client_hub().get::<dyn TypesRegistryClient>()?;
71        let instance = BaseModkitPluginV1::<AuthNResolverPluginSpecV1> {
72            id: instance_id.clone(),
73            vendor: cfg.vendor.clone(),
74            priority: cfg.priority,
75            properties: AuthNResolverPluginSpecV1,
76        };
77        let instance_json = serde_json::to_value(&instance)?;
78
79        let results = registry.register(vec![instance_json]).await?;
80        RegisterResult::ensure_all_ok(&results)?;
81
82        // Create service from config
83        let service = Arc::new(Service::from_config(&cfg));
84        self.service
85            .set(service.clone())
86            .map_err(|_| anyhow::anyhow!("Service already initialized"))?;
87
88        // Register scoped client in ClientHub
89        let api: Arc<dyn AuthNResolverPluginClient> = service;
90        ctx.client_hub()
91            .register_scoped::<dyn AuthNResolverPluginClient>(
92                ClientScope::gts_id(&instance_id),
93                api,
94            );
95
96        info!(instance_id = %instance_id, "Static authn plugin initialized");
97        Ok(())
98    }
99}