Skip to main content

auth_oidc/
module.rs

1use crate::migrations::AUTH_OIDC_MIGRATIONS;
2use platform_core::AppContext;
3use platform_http::ApiOpenApiRouter;
4use platform_module::{
5    ConsoleNavigation, ConsoleSurface, ConsoleSurfacePresentation, ConsoleWorkspaceRef,
6    HostLinkedModule, LinkedBinding, LinkedHttpContribution, Module, ModuleHttpMethod,
7    ModuleHttpRoute, ModuleManifest,
8};
9
10pub const MODULE_NAME: &str = "auth-oidc";
11const AUTH_PROVIDERS_READ: &str = "auth.providers.read";
12
13fn auth_workspace() -> ConsoleWorkspaceRef {
14    ConsoleWorkspaceRef {
15        id: "auth".to_owned(),
16        label: "Auth".to_owned(),
17        icon: Some("shield".to_owned()),
18    }
19}
20
21pub fn http_routes() -> Vec<ModuleHttpRoute> {
22    vec![
23        ModuleHttpRoute {
24            method: ModuleHttpMethod::Get,
25            path: "/.well-known/openid-configuration".to_owned(),
26            capability: None,
27            operation: None,
28            display_name: Some("OIDC Provider Metadata".to_owned()),
29            story_title: Some("OIDC Discovery".to_owned()),
30        },
31        ModuleHttpRoute {
32            method: ModuleHttpMethod::Get,
33            path: "/.well-known/jwks.json".to_owned(),
34            capability: None,
35            operation: None,
36            display_name: Some("OIDC JSON Web Key Set".to_owned()),
37            story_title: Some("OIDC JWKS".to_owned()),
38        },
39        ModuleHttpRoute {
40            method: ModuleHttpMethod::Get,
41            path: "/oauth/authorize".to_owned(),
42            capability: None,
43            operation: None,
44            display_name: Some("OIDC Authorization".to_owned()),
45            story_title: Some("OIDC Authorization".to_owned()),
46        },
47        ModuleHttpRoute {
48            method: ModuleHttpMethod::Post,
49            path: "/oauth/token".to_owned(),
50            capability: None,
51            operation: None,
52            display_name: Some("OIDC Token Exchange".to_owned()),
53            story_title: Some("OIDC Token Exchange".to_owned()),
54        },
55    ]
56}
57
58pub fn console_surfaces() -> Vec<ConsoleSurface> {
59    vec![ConsoleSurface {
60        name: "oidc-provider".to_owned(),
61        label: "OIDC Provider".to_owned(),
62        route: "/data/auth/providers/oidc".to_owned(),
63        presentation: ConsoleSurfacePresentation::Esm {
64            entry: "oidc-provider".to_owned(),
65        },
66        icon: Some("shield".to_owned()),
67        required_capabilities: vec![AUTH_PROVIDERS_READ.to_owned()],
68        navigation: Some(ConsoleNavigation {
69            workspace: auth_workspace(),
70            group: None,
71            order: Some(83),
72        }),
73    }]
74}
75
76pub fn manifest() -> ModuleManifest {
77    ModuleManifest::builder(MODULE_NAME)
78        .dependencies(vec![auth::module::MODULE_NAME.to_owned()])
79        .capabilities(vec![AUTH_PROVIDERS_READ.to_owned()])
80        .http_routes(http_routes())
81        .console(console_surfaces())
82        .build()
83}
84
85pub fn merge_http(base: ApiOpenApiRouter) -> ApiOpenApiRouter {
86    base.merge(crate::routes::router())
87}
88
89pub fn binding() -> LinkedBinding {
90    LinkedBinding::builder()
91        .http(LinkedHttpContribution {
92            public_prefixes: &["/.well-known/", "/oauth/"],
93            merge: merge_http,
94        })
95        .build()
96}
97
98pub fn module(_ctx: &AppContext) -> Module {
99    Module::linked(manifest(), binding())
100}
101
102pub fn linked_module() -> HostLinkedModule {
103    HostLinkedModule::linked(MODULE_NAME, manifest, module, AUTH_OIDC_MIGRATIONS)
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109    use platform_module::{ModuleManifestLintSeverity, lint_module_manifest};
110
111    #[test]
112    fn manifest_declares_oidc_routes() {
113        let manifest = manifest();
114
115        assert_eq!(manifest.module_id, format!("lenso/{MODULE_NAME}"));
116        assert_eq!(manifest.http_routes, http_routes());
117        assert_eq!(manifest.console, console_surfaces());
118
119        let lints = lint_module_manifest(&manifest);
120        assert!(
121            lints
122                .iter()
123                .all(|lint| lint.severity == ModuleManifestLintSeverity::Ok),
124            "auth-oidc manifest should not have warning/error lints: {lints:?}"
125        );
126    }
127
128    #[test]
129    fn generated_console_manifest_matches_checked_in_artifact_manifest() {
130        let generated =
131            serde_json::to_value(manifest().console_module_manifest("^1.0.0", "^2.0.0"))
132                .expect("console module manifest should serialize");
133        let checked_in: serde_json::Value =
134            serde_json::from_str(include_str!("../console-module.json"))
135                .expect("console module manifest fixture should be valid JSON");
136
137        assert_eq!(generated, checked_in);
138    }
139}