@kustodian/plugin-authentik 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,290 @@
1
+ # @kustodian/plugin-authentik
2
+
3
+ Authentik authentication provider plugin for Kustodian. This plugin enables integration with Authentik for authentication and authorization in your Kubernetes applications.
4
+
5
+ ## Features
6
+
7
+ - 🔐 **OAuth2/OIDC Provider** - Configure OAuth2 and OIDC clients for modern applications
8
+ - 🛡️ **SAML Provider** - Support for legacy applications using SAML authentication
9
+ - 🔀 **Proxy/Forward Auth** - Protect applications with forward authentication
10
+ - 📝 **Blueprint Generation** - Generate Authentik blueprints for GitOps workflows
11
+ - 🔑 **Secret Management** - Automatically generate secure client secrets
12
+ - ✅ **Validation** - Validate Authentik blueprint configurations
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pnpm add @kustodian/plugin-authentik
18
+ ```
19
+
20
+ ## Prerequisites
21
+
22
+ Optional: For enhanced functionality, install the Authentik CLI:
23
+
24
+ ```bash
25
+ # Installation instructions available at:
26
+ # https://goauthentik.io/docs/installation/
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ### Basic Plugin Setup
32
+
33
+ ```typescript
34
+ import { create_authentik_plugin } from '@kustodian/plugin-authentik';
35
+
36
+ const authentik_plugin = create_authentik_plugin({
37
+ domain: 'auth.example.com',
38
+ default_authorization_flow: 'implicit-consent',
39
+ outpost_name: 'default-outpost',
40
+ auto_generate_secrets: true,
41
+ output_dir: './authentik-blueprints',
42
+ });
43
+ ```
44
+
45
+ ### CLI Commands
46
+
47
+ The plugin provides several CLI commands:
48
+
49
+ ```bash
50
+ # Check Authentik CLI availability
51
+ kustodian authentik check
52
+
53
+ # Generate a random secret for OAuth2 clients
54
+ kustodian authentik generate-secret [length]
55
+
56
+ # Generate Authentik blueprint
57
+ kustodian authentik generate-blueprint <app-name> <provider-type> <config-json>
58
+
59
+ # Validate blueprint file
60
+ kustodian authentik validate-blueprint <blueprint-path>
61
+ ```
62
+
63
+ ### Programmatic Usage
64
+
65
+ #### Generate OAuth2 Provider
66
+
67
+ ```typescript
68
+ import { generate_oauth2_provider } from '@kustodian/plugin-authentik';
69
+
70
+ const auth_config = {
71
+ provider: 'oauth2' as const,
72
+ app_name: 'grafana',
73
+ app_display_name: 'Grafana',
74
+ app_launch_url: 'https://grafana.example.com',
75
+ oauth2: {
76
+ client_id: 'grafana',
77
+ client_type: 'confidential' as const,
78
+ redirect_uris: ['https://grafana.example.com/login/generic_oauth'],
79
+ },
80
+ };
81
+
82
+ const options = {
83
+ domain: 'auth.example.com',
84
+ default_authorization_flow: 'implicit-consent' as const,
85
+ outpost_name: 'default-outpost',
86
+ auto_generate_secrets: true,
87
+ output_dir: './authentik-blueprints',
88
+ blueprint_version: 1,
89
+ };
90
+
91
+ const result = generate_oauth2_provider(auth_config, options);
92
+
93
+ if (result.success) {
94
+ console.log('Generated OAuth2 provider:', result.value);
95
+ }
96
+ ```
97
+
98
+ #### Generate SAML Provider
99
+
100
+ ```typescript
101
+ import { generate_saml_provider } from '@kustodian/plugin-authentik';
102
+
103
+ const auth_config = {
104
+ provider: 'saml' as const,
105
+ app_name: 'legacy-app',
106
+ saml: {
107
+ acs_url: 'https://app.example.com/saml/acs',
108
+ issuer: 'https://app.example.com',
109
+ sp_binding: 'post' as const,
110
+ },
111
+ };
112
+
113
+ const result = generate_saml_provider(auth_config, options);
114
+ ```
115
+
116
+ #### Generate Proxy Provider
117
+
118
+ ```typescript
119
+ import { generate_proxy_provider } from '@kustodian/plugin-authentik';
120
+
121
+ const auth_config = {
122
+ provider: 'proxy' as const,
123
+ app_name: 'qbittorrent',
124
+ proxy: {
125
+ external_host: 'https://qbittorrent.example.com',
126
+ internal_host: 'http://qbittorrent.media.svc:8080',
127
+ mode: 'forward_single' as const,
128
+ },
129
+ };
130
+
131
+ const result = generate_proxy_provider(auth_config, options);
132
+ ```
133
+
134
+ #### Generate Complete Blueprint
135
+
136
+ ```typescript
137
+ import { generate_authentik_blueprint, blueprint_to_yaml } from '@kustodian/plugin-authentik';
138
+
139
+ const auth_config = {
140
+ provider: 'oauth2' as const,
141
+ app_name: 'grafana',
142
+ app_display_name: 'Grafana',
143
+ app_description: 'Monitoring and observability platform',
144
+ app_icon: 'https://grafana.com/static/img/menu/grafana2.svg',
145
+ app_group: 'Monitoring',
146
+ app_launch_url: 'https://grafana.example.com',
147
+ oauth2: {
148
+ client_id: 'grafana',
149
+ redirect_uris: ['https://grafana.example.com/login/generic_oauth'],
150
+ },
151
+ };
152
+
153
+ const blueprint_result = generate_authentik_blueprint(auth_config, options);
154
+
155
+ if (blueprint_result.success) {
156
+ // Export as YAML
157
+ const yaml_content = blueprint_to_yaml(blueprint_result.value);
158
+ console.log(yaml_content);
159
+ }
160
+ ```
161
+
162
+ ## Provider Types
163
+
164
+ ### OAuth2/OIDC
165
+
166
+ Suitable for modern applications that support OpenID Connect:
167
+
168
+ ```typescript
169
+ {
170
+ provider: 'oauth2',
171
+ oauth2: {
172
+ client_id: 'my-app',
173
+ client_type: 'confidential', // or 'public'
174
+ redirect_uris: ['https://app.example.com/callback'],
175
+ // Optional configuration
176
+ authorization_flow: 'implicit-consent',
177
+ signing_key: 'certificate-slug',
178
+ include_claims_in_id_token: true,
179
+ access_token_validity: 'minutes=10',
180
+ refresh_token_validity: 'days=30',
181
+ }
182
+ }
183
+ ```
184
+
185
+ ### SAML
186
+
187
+ For legacy enterprise applications:
188
+
189
+ ```typescript
190
+ {
191
+ provider: 'saml',
192
+ saml: {
193
+ acs_url: 'https://app.example.com/saml/acs',
194
+ issuer: 'https://app.example.com',
195
+ sp_binding: 'post', // or 'redirect'
196
+ // Optional configuration
197
+ name_id_policy: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
198
+ signing_kp: 'certificate-slug',
199
+ }
200
+ }
201
+ ```
202
+
203
+ ### Proxy (Forward Auth)
204
+
205
+ For applications that don't natively support SSO:
206
+
207
+ ```typescript
208
+ {
209
+ provider: 'proxy',
210
+ proxy: {
211
+ external_host: 'https://app.example.com',
212
+ internal_host: 'http://app.svc:8080',
213
+ mode: 'forward_single', // 'proxy', 'forward_domain'
214
+ // Optional configuration
215
+ skip_path_regex: '/api/health|/public/.*',
216
+ intercept_header_auth: true,
217
+ }
218
+ }
219
+ ```
220
+
221
+ ## Blueprint Structure
222
+
223
+ Generated blueprints follow the Authentik Blueprint format:
224
+
225
+ ```yaml
226
+ version: 1
227
+ metadata:
228
+ name: app-name-blueprint
229
+ labels:
230
+ app.kubernetes.io/name: app-name
231
+ app.kubernetes.io/managed-by: kustodian
232
+ entries:
233
+ - model: authentik_providers_oauth2.oauth2provider
234
+ identifiers:
235
+ name: app-name-oauth2
236
+ attrs:
237
+ # Provider configuration
238
+ - model: authentik_core.application
239
+ identifiers:
240
+ slug: app-name
241
+ attrs:
242
+ # Application configuration
243
+ ```
244
+
245
+ ## Template Integration
246
+
247
+ Use in Kustodian templates:
248
+
249
+ ```yaml
250
+ apiVersion: kustodian.io/v1
251
+ kind: Template
252
+ metadata:
253
+ name: monitoring
254
+ spec:
255
+ kustomizations:
256
+ - name: grafana
257
+ path: grafana
258
+ auth:
259
+ provider: oauth2
260
+ app_name: grafana
261
+ app_display_name: Grafana
262
+ app_icon: https://grafana.com/static/img/menu/grafana2.svg
263
+ app_group: Monitoring
264
+ app_launch_url: https://grafana.${cluster_domain}
265
+ oauth2:
266
+ client_id: grafana
267
+ redirect_uris:
268
+ - https://grafana.${cluster_domain}/login/generic_oauth
269
+ ```
270
+
271
+ ## API Reference
272
+
273
+ See [TypeScript definitions](./src/types.ts) for complete type information.
274
+
275
+ ### Main Functions
276
+
277
+ - `create_authentik_plugin(options)` - Create plugin instance
278
+ - `generate_oauth2_provider(config, options)` - Generate OAuth2 provider
279
+ - `generate_saml_provider(config, options)` - Generate SAML provider
280
+ - `generate_proxy_provider(config, options)` - Generate Proxy provider
281
+ - `generate_authentik_blueprint(config, options)` - Generate complete blueprint
282
+ - `blueprint_to_yaml(blueprint)` - Convert blueprint to YAML
283
+ - `yaml_to_blueprint(yaml)` - Parse YAML to blueprint
284
+ - `check_authentik_available()` - Check CLI availability
285
+ - `validate_blueprint(path)` - Validate blueprint file
286
+ - `generate_random_secret(length)` - Generate secure random secret
287
+
288
+ ## License
289
+
290
+ MIT
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@kustodian/plugin-authentik",
3
+ "version": "1.0.0",
4
+ "description": "Authentik authentication provider plugin for Kustodian",
5
+ "type": "module",
6
+ "main": "./src/index.ts",
7
+ "types": "./src/index.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./src/index.ts",
11
+ "import": "./src/index.ts"
12
+ }
13
+ },
14
+ "files": ["src"],
15
+ "scripts": {
16
+ "test": "bun test",
17
+ "test:watch": "bun test --watch",
18
+ "typecheck": "bun run tsc --noEmit"
19
+ },
20
+ "keywords": [
21
+ "kustodian",
22
+ "plugin",
23
+ "authentik",
24
+ "authentication",
25
+ "oauth2",
26
+ "saml",
27
+ "proxy",
28
+ "kubernetes"
29
+ ],
30
+ "author": "Luca Silverentand <luca@onezero.company>",
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/lucasilverentand/kustodian.git",
35
+ "directory": "plugins/authentik"
36
+ },
37
+ "publishConfig": {
38
+ "registry": "https://npm.pkg.github.com"
39
+ },
40
+ "dependencies": {
41
+ "@kustodian/core": "^1.1.0",
42
+ "@kustodian/plugins": "^1.0.1",
43
+ "@kustodian/schema": "^1.2.0",
44
+ "js-yaml": "^4.1.1",
45
+ "zod": "^4.3.5"
46
+ },
47
+ "packageManager": "pnpm@10.19.0",
48
+ "devDependencies": {
49
+ "@types/js-yaml": "^4.0.9"
50
+ }
51
+ }
@@ -0,0 +1,119 @@
1
+ import { exec } from 'node:child_process';
2
+ import { readFileSync } from 'node:fs';
3
+ import { promisify } from 'node:util';
4
+
5
+ import { type ResultType, create_error, success } from '@kustodian/core';
6
+ import type { KustodianErrorType } from '@kustodian/core';
7
+
8
+ import { yaml_to_blueprint } from './generator.js';
9
+
10
+ const exec_async = promisify(exec);
11
+
12
+ /**
13
+ * Check if Authentik CLI is available.
14
+ */
15
+ export async function check_authentik_available(): Promise<ResultType<string, KustodianErrorType>> {
16
+ try {
17
+ const { stdout } = await exec_async('ak --version', { timeout: 5000 });
18
+ const version = stdout.trim();
19
+ return success(version);
20
+ } catch (error) {
21
+ return {
22
+ success: false,
23
+ error: create_error(
24
+ 'AUTHENTIK_CLI_NOT_FOUND',
25
+ 'Authentik CLI not found. Install from: https://goauthentik.io/docs/installation/',
26
+ error,
27
+ ),
28
+ };
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Validate Authentik blueprint file.
34
+ */
35
+ export async function validate_blueprint(
36
+ blueprint_path: string,
37
+ ): Promise<ResultType<void, KustodianErrorType>> {
38
+ try {
39
+ // Read the blueprint file
40
+ const blueprint_content = readFileSync(blueprint_path, 'utf-8');
41
+
42
+ // Parse YAML to validate structure
43
+ const parse_result = yaml_to_blueprint(blueprint_content);
44
+ if (!parse_result.success) {
45
+ return parse_result;
46
+ }
47
+
48
+ const blueprint = parse_result.value;
49
+
50
+ // Basic validation
51
+ if (!blueprint.version || !blueprint.metadata || !blueprint.entries) {
52
+ return {
53
+ success: false,
54
+ error: create_error(
55
+ 'INVALID_BLUEPRINT',
56
+ 'Blueprint must have version, metadata, and entries',
57
+ ),
58
+ };
59
+ }
60
+
61
+ if (blueprint.entries.length === 0) {
62
+ return {
63
+ success: false,
64
+ error: create_error('INVALID_BLUEPRINT', 'Blueprint must have at least one entry'),
65
+ };
66
+ }
67
+
68
+ // Validate each entry has required fields
69
+ for (const entry of blueprint.entries) {
70
+ if (!entry.model || !entry.identifiers) {
71
+ return {
72
+ success: false,
73
+ error: create_error(
74
+ 'INVALID_BLUEPRINT',
75
+ 'Each blueprint entry must have model and identifiers',
76
+ ),
77
+ };
78
+ }
79
+ }
80
+
81
+ return success(undefined);
82
+ } catch (error) {
83
+ return {
84
+ success: false,
85
+ error: create_error(
86
+ 'VALIDATION_ERROR',
87
+ `Failed to validate blueprint: ${error instanceof Error ? error.message : String(error)}`,
88
+ error,
89
+ ),
90
+ };
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Generate random secret (for OAuth2 clients).
96
+ */
97
+ export async function generate_random_secret(
98
+ length = 64,
99
+ ): Promise<ResultType<string, KustodianErrorType>> {
100
+ try {
101
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
102
+ let result = '';
103
+ const randomArray = new Uint8Array(length);
104
+ crypto.getRandomValues(randomArray);
105
+ for (const value of randomArray) {
106
+ result += chars[value % chars.length];
107
+ }
108
+ return success(result);
109
+ } catch (error) {
110
+ return {
111
+ success: false,
112
+ error: create_error(
113
+ 'GENERATION_ERROR',
114
+ `Failed to generate secret: ${error instanceof Error ? error.message : String(error)}`,
115
+ error,
116
+ ),
117
+ };
118
+ }
119
+ }
@@ -0,0 +1,319 @@
1
+ import { type ResultType, failure, success } from '@kustodian/core';
2
+ import type { KustodianErrorType } from '@kustodian/core';
3
+ import * as yaml from 'js-yaml';
4
+
5
+ import type {
6
+ AuthConfigType,
7
+ AuthentikApplicationType,
8
+ AuthentikBlueprintType,
9
+ AuthentikOAuth2ProviderType,
10
+ AuthentikPluginOptionsType,
11
+ AuthentikProxyProviderType,
12
+ AuthentikSAMLProviderType,
13
+ } from './types.js';
14
+
15
+ /**
16
+ * Generate a random secret for OAuth2 clients.
17
+ */
18
+ export function generate_client_secret(length = 64): string {
19
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
20
+ let result = '';
21
+ const randomArray = new Uint8Array(length);
22
+ crypto.getRandomValues(randomArray);
23
+ for (const value of randomArray) {
24
+ result += chars[value % chars.length];
25
+ }
26
+ return result;
27
+ }
28
+
29
+ /**
30
+ * Generate OAuth2 provider blueprint entry.
31
+ */
32
+ export function generate_oauth2_provider(
33
+ auth_config: AuthConfigType,
34
+ options: AuthentikPluginOptionsType,
35
+ ): ResultType<AuthentikOAuth2ProviderType, KustodianErrorType> {
36
+ if (auth_config.provider !== 'oauth2') {
37
+ return failure({
38
+ code: 'INVALID_CONFIG',
39
+ message: 'Auth config provider must be "oauth2"',
40
+ });
41
+ }
42
+
43
+ const oauth2_config = auth_config.oauth2 ?? {};
44
+ const provider_name = `${auth_config.app_name}-oauth2`;
45
+
46
+ const provider: AuthentikOAuth2ProviderType = {
47
+ identifiers: {
48
+ name: provider_name,
49
+ },
50
+ model: 'authentik_providers_oauth2.oauth2provider',
51
+ attrs: {
52
+ name: provider_name,
53
+ client_id: oauth2_config.client_id ?? auth_config.app_name,
54
+ client_type: oauth2_config.client_type ?? 'confidential',
55
+ redirect_uris: oauth2_config.redirect_uris?.join('\n') ?? '',
56
+ authorization_flow: oauth2_config.authorization_flow ?? options.default_authorization_flow,
57
+ include_claims_in_id_token: oauth2_config.include_claims_in_id_token ?? true,
58
+ access_token_validity: oauth2_config.access_token_validity ?? 'minutes=10',
59
+ refresh_token_validity: oauth2_config.refresh_token_validity ?? 'days=30',
60
+ sub_mode: oauth2_config.sub_mode ?? 'hashed_user_identifier',
61
+ issue_refresh_tokens: oauth2_config.issue_refresh_tokens ?? true,
62
+ },
63
+ };
64
+
65
+ // Add client secret if confidential and auto-generate is enabled
66
+ if (
67
+ provider.attrs.client_type === 'confidential' &&
68
+ options.auto_generate_secrets &&
69
+ !oauth2_config.client_secret
70
+ ) {
71
+ provider.attrs.client_secret = generate_client_secret();
72
+ } else if (oauth2_config.client_secret) {
73
+ provider.attrs.client_secret = oauth2_config.client_secret;
74
+ }
75
+
76
+ // Add optional signing key
77
+ if (oauth2_config.signing_key) {
78
+ provider.attrs.signing_key = oauth2_config.signing_key;
79
+ }
80
+
81
+ return success(provider);
82
+ }
83
+
84
+ /**
85
+ * Generate SAML provider blueprint entry.
86
+ */
87
+ export function generate_saml_provider(
88
+ auth_config: AuthConfigType,
89
+ options: AuthentikPluginOptionsType,
90
+ ): ResultType<AuthentikSAMLProviderType, KustodianErrorType> {
91
+ if (auth_config.provider !== 'saml') {
92
+ return failure({
93
+ code: 'INVALID_CONFIG',
94
+ message: 'Auth config provider must be "saml"',
95
+ });
96
+ }
97
+
98
+ const saml_config = auth_config.saml;
99
+ if (!saml_config?.acs_url || !saml_config?.issuer) {
100
+ return failure({
101
+ code: 'INVALID_CONFIG',
102
+ message: 'SAML provider requires acs_url and issuer',
103
+ });
104
+ }
105
+
106
+ const provider_name = `${auth_config.app_name}-saml`;
107
+
108
+ const provider: AuthentikSAMLProviderType = {
109
+ identifiers: {
110
+ name: provider_name,
111
+ },
112
+ model: 'authentik_providers_saml.samlprovider',
113
+ attrs: {
114
+ name: provider_name,
115
+ acs_url: saml_config.acs_url,
116
+ issuer: saml_config.issuer,
117
+ sp_binding: saml_config.sp_binding ?? 'post',
118
+ authorization_flow: saml_config.authorization_flow ?? options.default_authorization_flow,
119
+ assertion_valid_not_before: saml_config.assertion_valid_not_before ?? 'minutes=5',
120
+ assertion_valid_not_on_or_after: saml_config.assertion_valid_not_on_or_after ?? 'minutes=5',
121
+ session_valid_not_on_or_after: saml_config.session_valid_not_on_or_after ?? 'minutes=86400',
122
+ },
123
+ };
124
+
125
+ // Add optional fields
126
+ if (saml_config.audience) {
127
+ provider.attrs.audience = saml_config.audience;
128
+ }
129
+ if (saml_config.signing_kp) {
130
+ provider.attrs.signing_kp = saml_config.signing_kp;
131
+ }
132
+
133
+ return success(provider);
134
+ }
135
+
136
+ /**
137
+ * Generate Proxy provider blueprint entry.
138
+ */
139
+ export function generate_proxy_provider(
140
+ auth_config: AuthConfigType,
141
+ options: AuthentikPluginOptionsType,
142
+ ): ResultType<AuthentikProxyProviderType, KustodianErrorType> {
143
+ if (auth_config.provider !== 'proxy') {
144
+ return failure({
145
+ code: 'INVALID_CONFIG',
146
+ message: 'Auth config provider must be "proxy"',
147
+ });
148
+ }
149
+
150
+ const proxy_config = auth_config.proxy;
151
+ if (!proxy_config?.external_host) {
152
+ return failure({
153
+ code: 'INVALID_CONFIG',
154
+ message: 'Proxy provider requires external_host',
155
+ });
156
+ }
157
+
158
+ const provider_name = `${auth_config.app_name}-proxy`;
159
+
160
+ const provider: AuthentikProxyProviderType = {
161
+ identifiers: {
162
+ name: provider_name,
163
+ },
164
+ model: 'authentik_providers_proxy.proxyprovider',
165
+ attrs: {
166
+ name: provider_name,
167
+ external_host: proxy_config.external_host,
168
+ internal_host_ssl_validation: proxy_config.internal_host_ssl_validation ?? true,
169
+ basic_auth_enabled: proxy_config.basic_auth_enabled ?? false,
170
+ mode: proxy_config.mode ?? 'forward_single',
171
+ authorization_flow: proxy_config.authorization_flow ?? options.default_authorization_flow,
172
+ access_token_validity: proxy_config.access_token_validity ?? 'minutes=10',
173
+ intercept_header_auth: proxy_config.intercept_header_auth ?? true,
174
+ },
175
+ };
176
+
177
+ // Add optional fields
178
+ if (proxy_config.internal_host) {
179
+ provider.attrs.internal_host = proxy_config.internal_host;
180
+ }
181
+ if (proxy_config.certificate) {
182
+ provider.attrs.certificate = proxy_config.certificate;
183
+ }
184
+ if (proxy_config.skip_path_regex) {
185
+ provider.attrs.skip_path_regex = proxy_config.skip_path_regex;
186
+ }
187
+ if (proxy_config.basic_auth_password_attribute) {
188
+ provider.attrs.basic_auth_password_attribute = proxy_config.basic_auth_password_attribute;
189
+ }
190
+ if (proxy_config.basic_auth_user_attribute) {
191
+ provider.attrs.basic_auth_user_attribute = proxy_config.basic_auth_user_attribute;
192
+ }
193
+
194
+ return success(provider);
195
+ }
196
+
197
+ /**
198
+ * Generate application blueprint entry.
199
+ */
200
+ export function generate_application(
201
+ auth_config: AuthConfigType,
202
+ provider_name: string,
203
+ ): AuthentikApplicationType {
204
+ const attrs: AuthentikApplicationType['attrs'] = {
205
+ name: auth_config.app_display_name ?? auth_config.app_name,
206
+ slug: auth_config.app_name,
207
+ provider: provider_name,
208
+ policy_engine_mode: 'any',
209
+ };
210
+
211
+ // Add optional fields only if defined
212
+ if (auth_config.app_description) {
213
+ attrs.meta_description = auth_config.app_description;
214
+ }
215
+ if (auth_config.app_icon) {
216
+ attrs.meta_icon = auth_config.app_icon;
217
+ }
218
+ if (auth_config.app_group) {
219
+ attrs.group = auth_config.app_group;
220
+ }
221
+ if (auth_config.app_launch_url) {
222
+ attrs.meta_launch_url = auth_config.app_launch_url;
223
+ }
224
+
225
+ return {
226
+ identifiers: {
227
+ slug: auth_config.app_name,
228
+ },
229
+ model: 'authentik_core.application',
230
+ attrs,
231
+ };
232
+ }
233
+
234
+ /**
235
+ * Generate complete Authentik blueprint from auth configuration.
236
+ */
237
+ export function generate_authentik_blueprint(
238
+ auth_config: AuthConfigType,
239
+ options: AuthentikPluginOptionsType,
240
+ ): ResultType<AuthentikBlueprintType, KustodianErrorType> {
241
+ const entries: AuthentikBlueprintType['entries'] = [];
242
+
243
+ // Generate provider based on type
244
+ let provider_result:
245
+ | ResultType<AuthentikOAuth2ProviderType, KustodianErrorType>
246
+ | ResultType<AuthentikSAMLProviderType, KustodianErrorType>
247
+ | ResultType<AuthentikProxyProviderType, KustodianErrorType>;
248
+
249
+ switch (auth_config.provider) {
250
+ case 'oauth2':
251
+ provider_result = generate_oauth2_provider(auth_config, options);
252
+ break;
253
+ case 'saml':
254
+ provider_result = generate_saml_provider(auth_config, options);
255
+ break;
256
+ case 'proxy':
257
+ provider_result = generate_proxy_provider(auth_config, options);
258
+ break;
259
+ default:
260
+ return failure({
261
+ code: 'INVALID_CONFIG',
262
+ message: `Unknown provider type: ${auth_config.provider}`,
263
+ });
264
+ }
265
+
266
+ if (!provider_result.success) {
267
+ return provider_result;
268
+ }
269
+
270
+ const provider = provider_result.value;
271
+ entries.push(provider);
272
+
273
+ // Generate application
274
+ const application = generate_application(auth_config, provider.identifiers.name);
275
+ entries.push(application);
276
+
277
+ const blueprint: AuthentikBlueprintType = {
278
+ version: options.blueprint_version,
279
+ metadata: {
280
+ name: `${auth_config.app_name}-blueprint`,
281
+ labels: {
282
+ 'app.kubernetes.io/name': auth_config.app_name,
283
+ 'app.kubernetes.io/managed-by': 'kustodian',
284
+ },
285
+ },
286
+ entries,
287
+ };
288
+
289
+ return success(blueprint);
290
+ }
291
+
292
+ /**
293
+ * Convert blueprint to YAML string.
294
+ */
295
+ export function blueprint_to_yaml(blueprint: AuthentikBlueprintType): string {
296
+ return yaml.dump(blueprint, {
297
+ indent: 2,
298
+ lineWidth: -1,
299
+ noRefs: true,
300
+ sortKeys: false,
301
+ });
302
+ }
303
+
304
+ /**
305
+ * Parse YAML string to blueprint.
306
+ */
307
+ export function yaml_to_blueprint(
308
+ yaml_string: string,
309
+ ): ResultType<AuthentikBlueprintType, KustodianErrorType> {
310
+ try {
311
+ const blueprint = yaml.load(yaml_string) as AuthentikBlueprintType;
312
+ return success(blueprint);
313
+ } catch (error) {
314
+ return failure({
315
+ code: 'PARSE_ERROR',
316
+ message: `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`,
317
+ });
318
+ }
319
+ }
package/src/index.ts ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Authentik authentication provider plugin for Kustodian
3
+ *
4
+ * This plugin enables integration with Authentik for authentication and authorization.
5
+ * It can generate OAuth2, SAML, and Proxy provider configurations, create Authentik
6
+ * blueprints, and manage authentication requirements for deployed applications.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+
11
+ export { create_authentik_plugin, plugin as default } from './plugin.js';
12
+ export type {
13
+ AuthConfigType,
14
+ AuthProviderType,
15
+ AuthentikPluginOptionsType,
16
+ OAuth2ProviderConfigType,
17
+ SAMLProviderConfigType,
18
+ ProxyProviderConfigType,
19
+ AuthentikBlueprintType,
20
+ AuthentikApplicationType,
21
+ AuthentikOAuth2ProviderType,
22
+ AuthentikSAMLProviderType,
23
+ AuthentikProxyProviderType,
24
+ ClientTypeType,
25
+ ProxyModeType,
26
+ SAMLBindingType,
27
+ SAMLNameIDPolicyType,
28
+ AuthentikFlowType,
29
+ } from './types.js';
30
+ export {
31
+ generate_authentik_blueprint,
32
+ generate_oauth2_provider,
33
+ generate_saml_provider,
34
+ generate_proxy_provider,
35
+ generate_application,
36
+ generate_client_secret,
37
+ blueprint_to_yaml,
38
+ yaml_to_blueprint,
39
+ } from './generator.js';
40
+ export {
41
+ check_authentik_available,
42
+ validate_blueprint,
43
+ generate_random_secret,
44
+ } from './executor.js';
package/src/plugin.ts ADDED
@@ -0,0 +1,238 @@
1
+ import { success } from '@kustodian/core';
2
+ import type {
3
+ CommandType,
4
+ HookContextType,
5
+ HookEventType,
6
+ KustodianPluginType,
7
+ PluginCommandContributionType,
8
+ PluginHookContributionType,
9
+ PluginManifestType,
10
+ } from '@kustodian/plugins';
11
+
12
+ import {
13
+ check_authentik_available,
14
+ generate_random_secret,
15
+ validate_blueprint,
16
+ } from './executor.js';
17
+ import { blueprint_to_yaml, generate_authentik_blueprint } from './generator.js';
18
+ import type { AuthConfigType } from './types.js';
19
+ import { authentik_plugin_options_schema } from './types.js';
20
+
21
+ /**
22
+ * Authentik plugin manifest.
23
+ */
24
+ const manifest: PluginManifestType = {
25
+ name: '@kustodian/plugin-authentik',
26
+ version: '1.0.0',
27
+ description: 'Authentik authentication provider plugin for Kustodian',
28
+ capabilities: ['commands', 'hooks'],
29
+ };
30
+
31
+ /**
32
+ * Creates the Authentik plugin.
33
+ */
34
+ export function create_authentik_plugin(
35
+ options: Record<string, unknown> = {},
36
+ ): KustodianPluginType {
37
+ // Parse options through schema to apply defaults
38
+ const plugin_options = authentik_plugin_options_schema.parse(options);
39
+
40
+ return {
41
+ manifest,
42
+
43
+ async activate() {
44
+ // Verify CLI availability on activation (warning only)
45
+ const check_result = await check_authentik_available();
46
+ if (!check_result.success) {
47
+ console.warn('Authentik CLI not found - some features may be unavailable');
48
+ console.warn('Install from: https://goauthentik.io/docs/installation/');
49
+ }
50
+ return success(undefined);
51
+ },
52
+
53
+ async deactivate() {
54
+ return success(undefined);
55
+ },
56
+
57
+ get_commands(): PluginCommandContributionType[] {
58
+ const authentik_command: CommandType = {
59
+ name: 'authentik',
60
+ description: 'Authentik authentication provider commands',
61
+ subcommands: [
62
+ {
63
+ name: 'check',
64
+ description: 'Check Authentik CLI availability',
65
+ handler: async () => {
66
+ const result = await check_authentik_available();
67
+ if (result.success) {
68
+ console.log(`Authentik CLI: ${result.value}`);
69
+ return success(undefined);
70
+ }
71
+ console.error('Authentik CLI not available');
72
+ return result;
73
+ },
74
+ },
75
+ {
76
+ name: 'generate-secret',
77
+ description: 'Generate random secret for OAuth2 client',
78
+ arguments: [
79
+ {
80
+ name: 'length',
81
+ description: 'Secret length (default: 64)',
82
+ required: false,
83
+ },
84
+ ],
85
+ handler: async (ctx: {
86
+ args: string[];
87
+ options: Record<string, unknown>;
88
+ data: Record<string, unknown>;
89
+ }) => {
90
+ const length = ctx.args[0] ? Number.parseInt(ctx.args[0], 10) : 64;
91
+
92
+ const result = await generate_random_secret(length);
93
+ if (result.success) {
94
+ console.log('Generated secret:');
95
+ console.log(result.value);
96
+ return success(undefined);
97
+ }
98
+
99
+ console.error(`Failed to generate secret: ${result.error.message}`);
100
+ return result;
101
+ },
102
+ },
103
+ {
104
+ name: 'generate-blueprint',
105
+ description: 'Generate Authentik blueprint from auth configuration',
106
+ arguments: [
107
+ {
108
+ name: 'app-name',
109
+ description: 'Application name',
110
+ required: true,
111
+ },
112
+ {
113
+ name: 'provider',
114
+ description: 'Provider type (oauth2, saml, proxy)',
115
+ required: true,
116
+ },
117
+ {
118
+ name: 'config-json',
119
+ description: 'JSON configuration for the provider',
120
+ required: true,
121
+ },
122
+ ],
123
+ handler: async (ctx: {
124
+ args: string[];
125
+ options: Record<string, unknown>;
126
+ data: Record<string, unknown>;
127
+ }) => {
128
+ const app_name = ctx.args[0];
129
+ const provider = ctx.args[1] as 'oauth2' | 'saml' | 'proxy';
130
+ const config_json = ctx.args[2];
131
+
132
+ if (!app_name || !provider || !config_json) {
133
+ console.error('App name, provider, and config JSON are required');
134
+ return success(undefined);
135
+ }
136
+
137
+ try {
138
+ const provider_config = JSON.parse(config_json);
139
+ const auth_config: AuthConfigType = {
140
+ provider,
141
+ app_name,
142
+ [provider]: provider_config,
143
+ };
144
+
145
+ const result = generate_authentik_blueprint(auth_config, plugin_options);
146
+ if (result.success) {
147
+ console.log('Generated blueprint:');
148
+ console.log(blueprint_to_yaml(result.value));
149
+ return success(undefined);
150
+ }
151
+
152
+ console.error(`Failed to generate blueprint: ${result.error.message}`);
153
+ return result;
154
+ } catch (error) {
155
+ console.error(
156
+ `Failed to parse config JSON: ${error instanceof Error ? error.message : String(error)}`,
157
+ );
158
+ return success(undefined);
159
+ }
160
+ },
161
+ },
162
+ {
163
+ name: 'validate-blueprint',
164
+ description: 'Validate Authentik blueprint file',
165
+ arguments: [
166
+ {
167
+ name: 'blueprint-path',
168
+ description: 'Path to blueprint file',
169
+ required: true,
170
+ },
171
+ ],
172
+ handler: async (ctx: {
173
+ args: string[];
174
+ options: Record<string, unknown>;
175
+ data: Record<string, unknown>;
176
+ }) => {
177
+ const blueprint_path = ctx.args[0];
178
+
179
+ if (!blueprint_path) {
180
+ console.error('Blueprint path is required');
181
+ return success(undefined);
182
+ }
183
+
184
+ const result = await validate_blueprint(blueprint_path);
185
+ if (result.success) {
186
+ console.log('✓ Blueprint is valid');
187
+ return success(undefined);
188
+ }
189
+
190
+ console.error(`✗ Blueprint validation failed: ${result.error.message}`);
191
+ return result;
192
+ },
193
+ },
194
+ ],
195
+ };
196
+ return [{ command: authentik_command }];
197
+ },
198
+
199
+ get_hooks(): PluginHookContributionType[] {
200
+ return [
201
+ {
202
+ event: 'generator:after_resolve',
203
+ priority: 40, // Run before secret providers to allow auth configs to be processed
204
+ handler: async (_event: HookEventType, ctx: HookContextType) => {
205
+ // TODO: Implement auth config extraction from templates
206
+ // This will:
207
+ // 1. Extract auth configs from kustomizations
208
+ // 2. Generate Authentik blueprints
209
+ // 3. Write blueprints to output directory
210
+ // 4. Generate Kubernetes ConfigMaps with blueprints
211
+
212
+ // For now, just pass through
213
+ return success(ctx);
214
+ },
215
+ },
216
+ {
217
+ event: 'generator:before',
218
+ priority: 50,
219
+ handler: async (_event: HookEventType, ctx: HookContextType) => {
220
+ // TODO: This hook could be used to:
221
+ // 1. Inject generated Authentik blueprints as ConfigMaps
222
+ // 2. Generate documentation for configured applications
223
+ // 3. Validate auth configuration consistency
224
+
225
+ return success(ctx);
226
+ },
227
+ },
228
+ ];
229
+ },
230
+ };
231
+ }
232
+
233
+ /**
234
+ * Default plugin export.
235
+ */
236
+ export const plugin = create_authentik_plugin();
237
+
238
+ export default plugin;
package/src/types.ts ADDED
@@ -0,0 +1,296 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * Authentik authorization flow types
5
+ */
6
+ export const authentik_flow_schema = z.enum([
7
+ 'implicit-consent',
8
+ 'explicit-consent',
9
+ 'default-provider-authorization-implicit-consent',
10
+ 'default-provider-authorization-explicit-consent',
11
+ ]);
12
+ export type AuthentikFlowType = z.infer<typeof authentik_flow_schema>;
13
+
14
+ /**
15
+ * Authentik provider types
16
+ */
17
+ export const auth_provider_schema = z.enum(['oauth2', 'saml', 'proxy']);
18
+ export type AuthProviderType = z.infer<typeof auth_provider_schema>;
19
+
20
+ /**
21
+ * OAuth2/OIDC client types
22
+ */
23
+ export const client_type_schema = z.enum(['confidential', 'public']);
24
+ export type ClientTypeType = z.infer<typeof client_type_schema>;
25
+
26
+ /**
27
+ * Authentik proxy mode types
28
+ */
29
+ export const proxy_mode_schema = z.enum(['proxy', 'forward_single', 'forward_domain']);
30
+ export type ProxyModeType = z.infer<typeof proxy_mode_schema>;
31
+
32
+ /**
33
+ * SAML SP binding types
34
+ */
35
+ export const saml_binding_schema = z.enum(['post', 'redirect']);
36
+ export type SAMLBindingType = z.infer<typeof saml_binding_schema>;
37
+
38
+ /**
39
+ * SAML NameID policy types
40
+ */
41
+ export const saml_nameid_policy_schema = z.enum([
42
+ 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
43
+ 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
44
+ 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient',
45
+ 'urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName',
46
+ ]);
47
+ export type SAMLNameIDPolicyType = z.infer<typeof saml_nameid_policy_schema>;
48
+
49
+ /**
50
+ * OAuth2/OIDC provider configuration for Authentik
51
+ */
52
+ export const oauth2_provider_config_schema = z.object({
53
+ /** Unique client identifier */
54
+ client_id: z.string(),
55
+ /** Client type (confidential or public) */
56
+ client_type: client_type_schema.default('confidential'),
57
+ /** Client secret (will be generated if not provided) */
58
+ client_secret: z.string().optional(),
59
+ /** Redirect URIs for OAuth callbacks */
60
+ redirect_uris: z.array(z.string()),
61
+ /** Authorization flow slug */
62
+ authorization_flow: authentik_flow_schema.optional(),
63
+ /** Signing key (optional, for JWT signing) */
64
+ signing_key: z.string().optional(),
65
+ /** Include claims in ID token */
66
+ include_claims_in_id_token: z.boolean().default(true),
67
+ /** Additional scopes beyond openid */
68
+ additional_scopes: z.array(z.string()).optional(),
69
+ /** Access token validity in seconds */
70
+ access_token_validity: z.string().default('minutes=10'),
71
+ /** Refresh token validity in seconds */
72
+ refresh_token_validity: z.string().default('days=30'),
73
+ /** Subject mode: based_on_username, based_on_user_email, based_on_user_uuid, based_on_hashed_user_identifier */
74
+ sub_mode: z.string().default('hashed_user_identifier'),
75
+ /** Issue refresh tokens */
76
+ issue_refresh_tokens: z.boolean().default(true),
77
+ });
78
+ export type OAuth2ProviderConfigType = z.infer<typeof oauth2_provider_config_schema>;
79
+
80
+ /**
81
+ * SAML provider configuration for Authentik
82
+ */
83
+ export const saml_provider_config_schema = z.object({
84
+ /** ACS (Assertion Consumer Service) URL */
85
+ acs_url: z.string().url(),
86
+ /** Entity ID / Issuer */
87
+ issuer: z.string(),
88
+ /** SP (Service Provider) binding method */
89
+ sp_binding: saml_binding_schema.default('post'),
90
+ /** Audience for SAML assertions */
91
+ audience: z.string().optional(),
92
+ /** Authorization flow slug */
93
+ authorization_flow: authentik_flow_schema.optional(),
94
+ /** Signing certificate */
95
+ signing_kp: z.string().optional(),
96
+ /** NameID policy */
97
+ name_id_policy: saml_nameid_policy_schema.default(
98
+ 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
99
+ ),
100
+ /** Assertion validity (not before) in seconds */
101
+ assertion_valid_not_before: z.string().default('minutes=5'),
102
+ /** Assertion validity (not on or after) in seconds */
103
+ assertion_valid_not_on_or_after: z.string().default('minutes=5'),
104
+ /** Session validity (not on or after) in seconds */
105
+ session_valid_not_on_or_after: z.string().default('minutes=86400'),
106
+ });
107
+ export type SAMLProviderConfigType = z.infer<typeof saml_provider_config_schema>;
108
+
109
+ /**
110
+ * Proxy provider configuration for Authentik
111
+ */
112
+ export const proxy_provider_config_schema = z.object({
113
+ /** External host (public URL) */
114
+ external_host: z.string().url(),
115
+ /** Internal host (backend service URL) */
116
+ internal_host: z.string().url().optional(),
117
+ /** Internal host (SSL validation) */
118
+ internal_host_ssl_validation: z.boolean().default(true),
119
+ /** Certificate for internal SSL */
120
+ certificate: z.string().optional(),
121
+ /** Skip path regex (paths to skip authentication) */
122
+ skip_path_regex: z.string().optional(),
123
+ /** Basic auth enabled */
124
+ basic_auth_enabled: z.boolean().default(false),
125
+ /** Basic auth password attribute */
126
+ basic_auth_password_attribute: z.string().optional(),
127
+ /** Basic auth user attribute */
128
+ basic_auth_user_attribute: z.string().optional(),
129
+ /** Mode: proxy, forward_single, or forward_domain */
130
+ mode: proxy_mode_schema.default('forward_single'),
131
+ /** Authorization flow slug */
132
+ authorization_flow: authentik_flow_schema.optional(),
133
+ /** Access token validity in seconds */
134
+ access_token_validity: z.string().default('minutes=10'),
135
+ /** Intercept header auth */
136
+ intercept_header_auth: z.boolean().default(true),
137
+ });
138
+ export type ProxyProviderConfigType = z.infer<typeof proxy_provider_config_schema>;
139
+
140
+ /**
141
+ * Authentication configuration in template kustomizations
142
+ */
143
+ export const auth_config_schema = z.object({
144
+ /** Authentication provider type */
145
+ provider: auth_provider_schema,
146
+ /** Application name (used as identifier) */
147
+ app_name: z.string(),
148
+ /** Display name for the application */
149
+ app_display_name: z.string().optional(),
150
+ /** Application description */
151
+ app_description: z.string().optional(),
152
+ /** Application icon URL */
153
+ app_icon: z.string().optional(),
154
+ /** Application group/category */
155
+ app_group: z.string().optional(),
156
+ /** Application launch URL */
157
+ app_launch_url: z.string().optional(),
158
+ /** OAuth2/OIDC-specific configuration */
159
+ oauth2: oauth2_provider_config_schema.partial().optional(),
160
+ /** SAML-specific configuration */
161
+ saml: saml_provider_config_schema.partial().optional(),
162
+ /** Proxy-specific configuration */
163
+ proxy: proxy_provider_config_schema.partial().optional(),
164
+ });
165
+ export type AuthConfigType = z.infer<typeof auth_config_schema>;
166
+
167
+ /**
168
+ * Authentik plugin options
169
+ */
170
+ export const authentik_plugin_options_schema = z.object({
171
+ /** Authentik domain (e.g., authentik.example.com) */
172
+ domain: z.string().optional(),
173
+ /** Default authorization flow */
174
+ default_authorization_flow: authentik_flow_schema.default('implicit-consent'),
175
+ /** Default proxy outpost name */
176
+ outpost_name: z.string().default('default-outpost'),
177
+ /** Whether to generate client secrets automatically */
178
+ auto_generate_secrets: z.boolean().default(true),
179
+ /** Output directory for generated blueprints */
180
+ output_dir: z.string().default('./authentik-blueprints'),
181
+ /** Blueprint version */
182
+ blueprint_version: z.number().default(1),
183
+ });
184
+ export type AuthentikPluginOptionsType = z.infer<typeof authentik_plugin_options_schema>;
185
+
186
+ /**
187
+ * Authentik application blueprint
188
+ */
189
+ export interface AuthentikApplicationType {
190
+ identifiers: {
191
+ slug: string;
192
+ };
193
+ model: 'authentik_core.application';
194
+ attrs: {
195
+ name: string;
196
+ slug: string;
197
+ provider?: string;
198
+ meta_description?: string;
199
+ meta_icon?: string;
200
+ group?: string;
201
+ meta_launch_url?: string;
202
+ policy_engine_mode?: string;
203
+ };
204
+ }
205
+
206
+ /**
207
+ * Authentik provider blueprint (OAuth2)
208
+ */
209
+ export interface AuthentikOAuth2ProviderType {
210
+ identifiers: {
211
+ name: string;
212
+ };
213
+ model: 'authentik_providers_oauth2.oauth2provider';
214
+ attrs: {
215
+ name: string;
216
+ client_id: string;
217
+ client_type: string;
218
+ client_secret?: string;
219
+ redirect_uris: string;
220
+ authorization_flow?: string;
221
+ signing_key?: string;
222
+ include_claims_in_id_token: boolean;
223
+ access_token_validity: string;
224
+ refresh_token_validity: string;
225
+ sub_mode: string;
226
+ issue_refresh_tokens: boolean;
227
+ property_mappings?: string[];
228
+ };
229
+ }
230
+
231
+ /**
232
+ * Authentik provider blueprint (SAML)
233
+ */
234
+ export interface AuthentikSAMLProviderType {
235
+ identifiers: {
236
+ name: string;
237
+ };
238
+ model: 'authentik_providers_saml.samlprovider';
239
+ attrs: {
240
+ name: string;
241
+ acs_url: string;
242
+ issuer: string;
243
+ sp_binding: string;
244
+ audience?: string;
245
+ authorization_flow?: string;
246
+ signing_kp?: string;
247
+ name_id_mapping?: string;
248
+ assertion_valid_not_before: string;
249
+ assertion_valid_not_on_or_after: string;
250
+ session_valid_not_on_or_after: string;
251
+ property_mappings?: string[];
252
+ };
253
+ }
254
+
255
+ /**
256
+ * Authentik provider blueprint (Proxy)
257
+ */
258
+ export interface AuthentikProxyProviderType {
259
+ identifiers: {
260
+ name: string;
261
+ };
262
+ model: 'authentik_providers_proxy.proxyprovider';
263
+ attrs: {
264
+ name: string;
265
+ external_host: string;
266
+ internal_host?: string;
267
+ internal_host_ssl_validation: boolean;
268
+ certificate?: string;
269
+ skip_path_regex?: string;
270
+ basic_auth_enabled: boolean;
271
+ basic_auth_password_attribute?: string;
272
+ basic_auth_user_attribute?: string;
273
+ mode: string;
274
+ authorization_flow?: string;
275
+ access_token_validity: string;
276
+ intercept_header_auth: boolean;
277
+ property_mappings?: string[];
278
+ };
279
+ }
280
+
281
+ /**
282
+ * Authentik blueprint structure
283
+ */
284
+ export interface AuthentikBlueprintType {
285
+ version: number;
286
+ metadata: {
287
+ name: string;
288
+ labels?: Record<string, string>;
289
+ };
290
+ entries: Array<
291
+ | AuthentikApplicationType
292
+ | AuthentikOAuth2ProviderType
293
+ | AuthentikSAMLProviderType
294
+ | AuthentikProxyProviderType
295
+ >;
296
+ }