1use crate::config::{AuthPasswordConfig, TokenStrategy};
2use platform_core::AppContext;
3use platform_http::ApiOpenApiRouter;
4use platform_module::{
5 LinkedBinding, LinkedHttpContribution, Module, ModuleHttpMethod, ModuleHttpRoute,
6 ModuleManifest,
7};
8
9pub const MODULE_NAME: &str = "auth-password";
10pub const AUTH_MODULE_DEPENDENCY: &str = "auth";
11
12pub fn http_routes() -> Vec<ModuleHttpRoute> {
13 vec![
14 ModuleHttpRoute {
15 method: ModuleHttpMethod::Post,
16 path: "/v1/auth/password/register".to_owned(),
17 capability: None,
18 display_name: Some("Register With Password".to_owned()),
19 story_title: Some("Password Registration".to_owned()),
20 },
21 ModuleHttpRoute {
22 method: ModuleHttpMethod::Post,
23 path: "/v1/auth/password/login".to_owned(),
24 capability: None,
25 display_name: Some("Login With Password".to_owned()),
26 story_title: Some("Password Login".to_owned()),
27 },
28 ]
29}
30
31pub fn manifest() -> ModuleManifest {
32 ModuleManifest::builder(MODULE_NAME)
33 .dependencies(vec![AUTH_MODULE_DEPENDENCY.to_owned()])
34 .http_routes(http_routes())
35 .build()
36}
37
38pub fn merge_http(base: ApiOpenApiRouter) -> ApiOpenApiRouter {
39 base.merge(crate::routes::router())
40}
41
42pub fn binding() -> LinkedBinding {
43 LinkedBinding::builder()
44 .http(LinkedHttpContribution {
45 public_prefixes: &["/v1/auth/password/"],
46 merge: merge_http,
47 })
48 .build()
49}
50
51pub fn module(_ctx: &AppContext) -> Module {
52 Module::linked(manifest(), binding())
53 .with_runtime_config_groups(crate::config::RUNTIME_CONFIG_GROUPS.as_slice())
54 .with_runtime_config(crate::config::RUNTIME_CONFIG.as_slice())
55}
56
57pub fn jwt_actor_resolver(
63 ctx: &AppContext,
64 fallback: std::sync::Arc<dyn platform_core::ActorResolver>,
65) -> platform_core::AppResult<Option<std::sync::Arc<dyn platform_core::ActorResolver>>> {
66 let config = AuthPasswordConfig::from_context(ctx)?;
67 if config.token_strategy == TokenStrategy::Jwt && config.jwt_secret.is_none() {
68 return Ok(None);
69 }
70 let jwt_config = match config.jwt_config()? {
71 Some(cfg) => cfg,
72 None => return Ok(None),
73 };
74 Ok(Some(std::sync::Arc::new(
75 crate::resolver::JwtActorResolver::new(jwt_config, fallback),
76 )))
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use platform_module::{ModuleManifestLintSeverity, ModuleSource, lint_module_manifest};
83
84 #[test]
85 fn manifest_declares_password_routes() {
86 let manifest = manifest();
87
88 assert_eq!(manifest.name, MODULE_NAME);
89 assert_eq!(manifest.http_routes, http_routes());
90
91 let lints = lint_module_manifest(ModuleSource::Linked, &manifest);
92 assert!(
93 lints
94 .iter()
95 .all(|lint| lint.severity == ModuleManifestLintSeverity::Ok),
96 "auth-password manifest should not have warning/error lints: {lints:?}"
97 );
98 }
99}