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