1use crate::migrations::AUTH_OIDC_MIGRATIONS;
2use platform_core::AppContext;
3use platform_http::ApiOpenApiRouter;
4use platform_module::{
5 CONSOLE_BRIDGE_PROTOCOL, ConsoleNavigation, 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
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::Isolated {
64 entry: "oidc-provider".to_owned(),
65
66 bridge_protocol: CONSOLE_BRIDGE_PROTOCOL.to_owned(),
67 },
68 icon: Some("shield".to_owned()),
69 required_capabilities: vec![AUTH_PROVIDERS_READ.to_owned()],
70 navigation: Some(ConsoleNavigation {
71 workspace: auth_workspace(),
72 group: None,
73 order: Some(83),
74 }),
75 }]
76}
77
78pub fn manifest() -> ModuleManifest {
79 ModuleManifest::builder(MODULE_NAME)
80 .dependencies(vec![auth::module::MODULE_NAME.to_owned()])
81 .capabilities(vec![AUTH_PROVIDERS_READ.to_owned()])
82 .http_routes(http_routes())
83 .console(console_surfaces())
84 .build()
85}
86
87pub fn merge_http(base: ApiOpenApiRouter) -> ApiOpenApiRouter {
88 base.merge(crate::routes::router())
89}
90
91pub fn binding() -> LinkedBinding {
92 LinkedBinding::builder()
93 .http(LinkedHttpContribution {
94 public_prefixes: &["/.well-known/", "/oauth/"],
95 merge: merge_http,
96 })
97 .build()
98}
99
100pub fn module(_ctx: &AppContext) -> Module {
101 Module::linked(manifest(), binding())
102}
103
104pub fn linked_module() -> HostLinkedModule {
105 HostLinkedModule::linked(MODULE_NAME, manifest, module, AUTH_OIDC_MIGRATIONS)
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use platform_module::{ModuleManifestLintSeverity, lint_module_manifest};
112
113 #[test]
114 fn manifest_declares_oidc_routes() {
115 let manifest = manifest();
116
117 assert_eq!(manifest.module_id, format!("lenso/{MODULE_NAME}"));
118 assert_eq!(manifest.http_routes, http_routes());
119 assert_eq!(manifest.console, console_surfaces());
120
121 let lints = lint_module_manifest(&manifest);
122 assert!(
123 lints
124 .iter()
125 .all(|lint| lint.severity == ModuleManifestLintSeverity::Ok),
126 "auth-oidc manifest should not have warning/error lints: {lints:?}"
127 );
128 }
129}