@lukoweb/apitogo 0.1.53 → 0.1.54

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/dist/cli/cli.js CHANGED
@@ -393,6 +393,7 @@ function normalizeApitogoConfig(config2) {
393
393
  delete normalized.projectName;
394
394
  migratePlanCatalog(normalized);
395
395
  syncBillingEnabled(normalized);
396
+ syncAuthentication(normalized);
396
397
  return normalized;
397
398
  }
398
399
  function isBillingEnabled(config2) {
@@ -402,6 +403,63 @@ function isBillingEnabled(config2) {
402
403
  );
403
404
  return plans?.enabled === true;
404
405
  }
406
+ function isUserPanelEnabled(normalized) {
407
+ const modules = asRecord(normalized.modules);
408
+ const userPanel = asRecord(modules?.userPanel);
409
+ if (userPanel?.enabled === true) {
410
+ return true;
411
+ }
412
+ const dashboard = asRecord(userPanel?.dashboard);
413
+ const userManagement = asRecord(userPanel?.userManagement);
414
+ const apiKeys = asRecord(userPanel?.apiKeys);
415
+ const plans = asRecord(userPanel?.plans);
416
+ return dashboard?.enabled === true || userManagement?.enabled === true || apiKeys?.enabled === true || plans?.enabled === true;
417
+ }
418
+ function syncAuthentication(normalized) {
419
+ const userPanelEnabled = isUserPanelEnabled(normalized);
420
+ const authentication = asRecord(normalized.authentication);
421
+ if (!userPanelEnabled && !authentication) {
422
+ return;
423
+ }
424
+ const auth = authentication ?? {};
425
+ auth.enabled = userPanelEnabled;
426
+ auth.type = "dev-portal";
427
+ if (userPanelEnabled) {
428
+ const native = asRecord(auth.native) ?? {};
429
+ if (native.enabled === void 0) {
430
+ native.enabled = true;
431
+ }
432
+ auth.native = native;
433
+ if (!Array.isArray(auth.providers)) {
434
+ auth.providers = [];
435
+ }
436
+ }
437
+ normalized.authentication = auth;
438
+ }
439
+ function stripAuthenticationSecrets(authentication) {
440
+ const {
441
+ gateway: _gateway,
442
+ oidc: _oidc,
443
+ stripe: _stripe,
444
+ type: _type,
445
+ email,
446
+ providers,
447
+ ...authPublic
448
+ } = authentication;
449
+ const emailRecord = asRecord(email);
450
+ if (emailRecord) {
451
+ const { connectionString: _connectionString, ...emailPublic } = emailRecord;
452
+ authPublic.email = emailPublic;
453
+ }
454
+ if (Array.isArray(providers)) {
455
+ authPublic.providers = providers.map((entry) => {
456
+ const provider = asRecord(entry) ?? {};
457
+ const { clientSecret: _clientSecret, ...providerPublic } = provider;
458
+ return providerPublic;
459
+ });
460
+ }
461
+ return authPublic;
462
+ }
405
463
  function syncBillingEnabled(normalized) {
406
464
  const legacyPricing = asRecord(normalized.pricing);
407
465
  const modules = ensureModules(normalized);
@@ -430,13 +488,7 @@ function deriveManifestFields(config2) {
430
488
  if (key === "authentication") {
431
489
  const authentication2 = asRecord(normalized.authentication);
432
490
  if (authentication2) {
433
- const {
434
- gateway: _gateway,
435
- oidc: _oidc,
436
- stripe: _stripe,
437
- ...authPublic
438
- } = authentication2;
439
- manifest.authentication = authPublic;
491
+ manifest.authentication = stripAuthenticationSecrets(authentication2);
440
492
  }
441
493
  continue;
442
494
  }
@@ -640,8 +692,23 @@ var init_local_manifest = __esm({
640
692
  }).optional(),
641
693
  authentication: z3.object({
642
694
  enabled: z3.boolean().optional(),
643
- type: z3.string().optional(),
644
- apiBaseUrl: z3.string().optional()
695
+ apiBaseUrl: z3.string().optional(),
696
+ native: z3.object({
697
+ enabled: z3.boolean().optional()
698
+ }).optional(),
699
+ email: z3.object({
700
+ provider: z3.string().optional(),
701
+ from: z3.string().optional()
702
+ }).optional(),
703
+ providers: z3.array(
704
+ z3.object({
705
+ id: z3.string().min(1),
706
+ displayName: z3.string().optional(),
707
+ authority: z3.string().optional(),
708
+ clientId: z3.string().optional(),
709
+ scopes: z3.string().optional()
710
+ })
711
+ ).optional()
645
712
  }).optional(),
646
713
  plans: z3.array(ManifestPlanInputSchema).optional(),
647
714
  backend: z3.object({
@@ -4094,8 +4161,26 @@ var init_ZudokuConfig = __esm({
4094
4161
  redirectToAfterSignOut: z9.string().optional()
4095
4162
  }),
4096
4163
  z9.object({
4097
- type: z9.literal("dev-portal"),
4164
+ type: z9.literal("dev-portal").optional().default("dev-portal"),
4098
4165
  apiBaseUrl: z9.string().optional(),
4166
+ native: z9.object({
4167
+ enabled: z9.boolean().optional()
4168
+ }).optional(),
4169
+ email: z9.object({
4170
+ provider: z9.string().optional(),
4171
+ from: z9.string().optional(),
4172
+ connectionString: z9.string().optional()
4173
+ }).optional(),
4174
+ providers: z9.array(
4175
+ z9.object({
4176
+ id: z9.string().min(1),
4177
+ displayName: z9.string().optional(),
4178
+ authority: z9.string().optional(),
4179
+ clientId: z9.string().optional(),
4180
+ clientSecret: z9.string().optional(),
4181
+ scopes: z9.string().optional()
4182
+ })
4183
+ ).optional(),
4099
4184
  redirectToAfterSignUp: z9.string().optional(),
4100
4185
  redirectToAfterSignIn: z9.string().optional(),
4101
4186
  redirectToAfterSignOut: z9.string().optional()
@@ -4716,7 +4801,7 @@ import {
4716
4801
  // package.json
4717
4802
  var package_default = {
4718
4803
  name: "@lukoweb/apitogo",
4719
- version: "0.1.53",
4804
+ version: "0.1.54",
4720
4805
  type: "module",
4721
4806
  sideEffects: [
4722
4807
  "**/*.css",
@@ -1,11 +1,19 @@
1
1
  export declare const APITOGO_CONFIG_FILENAME = "apitogo.config.json";
2
2
  export type ApitogoJsonConfig = Record<string, unknown>;
3
+ export type AuthProviderPublicConfig = {
4
+ id: string;
5
+ displayName?: string;
6
+ authority?: string;
7
+ clientId?: string;
8
+ scopes?: string;
9
+ };
3
10
  export type AuthenticationSecrets = {
4
- oidc?: {
5
- authority?: string;
6
- clientId?: string;
7
- clientSecret?: string;
11
+ email?: {
12
+ connectionString?: string;
8
13
  };
14
+ providers?: Record<string, {
15
+ clientSecret?: string;
16
+ }>;
9
17
  stripe?: {
10
18
  secretKey?: string;
11
19
  webhookSecret?: string;
@@ -18,6 +26,8 @@ type PlanCatalogItem = Record<string, unknown> & {
18
26
  export declare function readPlanCatalogItems(config: ApitogoJsonConfig): PlanCatalogItem[];
19
27
  export declare function normalizeApitogoConfig(config: ApitogoJsonConfig): ApitogoJsonConfig;
20
28
  export declare function isBillingEnabled(config: ApitogoJsonConfig): boolean;
29
+ export declare function readAuthProviders(config: ApitogoJsonConfig): AuthProviderPublicConfig[];
30
+ export declare function isNativeAuthEnabled(config: ApitogoJsonConfig): boolean;
21
31
  export declare function deriveManifestFields(config: ApitogoJsonConfig): ApitogoJsonConfig;
22
32
  export declare function extractManifest(config: ApitogoJsonConfig | null | undefined): ApitogoJsonConfig | null;
23
33
  export declare function extractSiteConfig(config: ApitogoJsonConfig | null | undefined): ApitogoJsonConfig;
@@ -35,8 +35,21 @@ export declare const DevPortalLocalManifestSchema: z.ZodObject<{
35
35
  }, z.core.$strip>>;
36
36
  authentication: z.ZodOptional<z.ZodObject<{
37
37
  enabled: z.ZodOptional<z.ZodBoolean>;
38
- type: z.ZodOptional<z.ZodString>;
39
38
  apiBaseUrl: z.ZodOptional<z.ZodString>;
39
+ native: z.ZodOptional<z.ZodObject<{
40
+ enabled: z.ZodOptional<z.ZodBoolean>;
41
+ }, z.core.$strip>>;
42
+ email: z.ZodOptional<z.ZodObject<{
43
+ provider: z.ZodOptional<z.ZodString>;
44
+ from: z.ZodOptional<z.ZodString>;
45
+ }, z.core.$strip>>;
46
+ providers: z.ZodOptional<z.ZodArray<z.ZodObject<{
47
+ id: z.ZodString;
48
+ displayName: z.ZodOptional<z.ZodString>;
49
+ authority: z.ZodOptional<z.ZodString>;
50
+ clientId: z.ZodOptional<z.ZodString>;
51
+ scopes: z.ZodOptional<z.ZodString>;
52
+ }, z.core.$strip>>>;
40
53
  }, z.core.$strip>>;
41
54
  plans: z.ZodOptional<z.ZodArray<z.ZodObject<{
42
55
  sku: z.ZodString;
@@ -91,6 +104,7 @@ export type DevPortalPreviewPlanCatalog = {
91
104
  };
92
105
  export declare function isPricingEnabled(manifest: DevPortalLocalManifest | null | undefined): boolean;
93
106
  export declare function isAuthenticationEnabled(manifest: DevPortalLocalManifest | null | undefined): boolean;
107
+ export declare function isNativeAuthEnabledInManifest(manifest: DevPortalLocalManifest | null | undefined): boolean;
94
108
  export declare function manifestToPreviewCatalog(manifest: DevPortalLocalManifest | null | undefined): DevPortalPreviewPlanCatalog;
95
109
  export declare function normalizeBillingPeriod(period?: string): string;
96
110
  export declare function plansToPreviewCatalog(plans: ManifestPlanInput[] | undefined): Pick<DevPortalPreviewPlanCatalog, "plans">;
@@ -367,8 +367,24 @@ declare const AuthenticationSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
367
367
  redirectToAfterSignIn: z.ZodOptional<z.ZodString>;
368
368
  redirectToAfterSignOut: z.ZodOptional<z.ZodString>;
369
369
  }, z.core.$strip>, z.ZodObject<{
370
- type: z.ZodLiteral<"dev-portal">;
370
+ type: z.ZodDefault<z.ZodOptional<z.ZodLiteral<"dev-portal">>>;
371
371
  apiBaseUrl: z.ZodOptional<z.ZodString>;
372
+ native: z.ZodOptional<z.ZodObject<{
373
+ enabled: z.ZodOptional<z.ZodBoolean>;
374
+ }, z.core.$strip>>;
375
+ email: z.ZodOptional<z.ZodObject<{
376
+ provider: z.ZodOptional<z.ZodString>;
377
+ from: z.ZodOptional<z.ZodString>;
378
+ connectionString: z.ZodOptional<z.ZodString>;
379
+ }, z.core.$strip>>;
380
+ providers: z.ZodOptional<z.ZodArray<z.ZodObject<{
381
+ id: z.ZodString;
382
+ displayName: z.ZodOptional<z.ZodString>;
383
+ authority: z.ZodOptional<z.ZodString>;
384
+ clientId: z.ZodOptional<z.ZodString>;
385
+ clientSecret: z.ZodOptional<z.ZodString>;
386
+ scopes: z.ZodOptional<z.ZodString>;
387
+ }, z.core.$strip>>>;
372
388
  redirectToAfterSignUp: z.ZodOptional<z.ZodString>;
373
389
  redirectToAfterSignIn: z.ZodOptional<z.ZodString>;
374
390
  redirectToAfterSignOut: z.ZodOptional<z.ZodString>;
@@ -6771,8 +6787,24 @@ export declare const ZudokuConfig: z.ZodObject<{
6771
6787
  redirectToAfterSignIn: z.ZodOptional<z.ZodString>;
6772
6788
  redirectToAfterSignOut: z.ZodOptional<z.ZodString>;
6773
6789
  }, z.core.$strip>, z.ZodObject<{
6774
- type: z.ZodLiteral<"dev-portal">;
6790
+ type: z.ZodDefault<z.ZodOptional<z.ZodLiteral<"dev-portal">>>;
6775
6791
  apiBaseUrl: z.ZodOptional<z.ZodString>;
6792
+ native: z.ZodOptional<z.ZodObject<{
6793
+ enabled: z.ZodOptional<z.ZodBoolean>;
6794
+ }, z.core.$strip>>;
6795
+ email: z.ZodOptional<z.ZodObject<{
6796
+ provider: z.ZodOptional<z.ZodString>;
6797
+ from: z.ZodOptional<z.ZodString>;
6798
+ connectionString: z.ZodOptional<z.ZodString>;
6799
+ }, z.core.$strip>>;
6800
+ providers: z.ZodOptional<z.ZodArray<z.ZodObject<{
6801
+ id: z.ZodString;
6802
+ displayName: z.ZodOptional<z.ZodString>;
6803
+ authority: z.ZodOptional<z.ZodString>;
6804
+ clientId: z.ZodOptional<z.ZodString>;
6805
+ clientSecret: z.ZodOptional<z.ZodString>;
6806
+ scopes: z.ZodOptional<z.ZodString>;
6807
+ }, z.core.$strip>>>;
6776
6808
  redirectToAfterSignUp: z.ZodOptional<z.ZodString>;
6777
6809
  redirectToAfterSignIn: z.ZodOptional<z.ZodString>;
6778
6810
  redirectToAfterSignOut: z.ZodOptional<z.ZodString>;
@@ -0,0 +1,15 @@
1
+ export declare function DevPortalLoginPage({ apiBaseUrl, }: {
2
+ apiBaseUrl?: string;
3
+ }): import("react/jsx-runtime").JSX.Element;
4
+ export declare function DevPortalRegisterPage({ apiBaseUrl, }: {
5
+ apiBaseUrl?: string;
6
+ }): import("react/jsx-runtime").JSX.Element;
7
+ export declare function DevPortalVerifyEmailPage({ apiBaseUrl }: {
8
+ apiBaseUrl?: string;
9
+ }): import("react/jsx-runtime").JSX.Element;
10
+ export declare function DevPortalForgotPasswordPage({ apiBaseUrl }: {
11
+ apiBaseUrl?: string;
12
+ }): import("react/jsx-runtime").JSX.Element;
13
+ export declare function DevPortalResetPasswordPage({ apiBaseUrl }: {
14
+ apiBaseUrl?: string;
15
+ }): import("react/jsx-runtime").JSX.Element;
@@ -14,5 +14,23 @@ export type EndUserMeResponse = {
14
14
  };
15
15
  export type DevPortalAuthStatusResponse = {
16
16
  available: boolean;
17
- oidcConfigured: boolean;
17
+ nativeEnabled?: boolean;
18
+ oidcConfigured?: boolean;
19
+ providerCount?: number;
20
+ };
21
+ export type DevPortalAuthConfigResponse = {
22
+ native: {
23
+ enabled: boolean;
24
+ };
25
+ email?: {
26
+ provider: string;
27
+ from: string;
28
+ } | null;
29
+ providers: Array<{
30
+ id: string;
31
+ displayName: string;
32
+ authority: string;
33
+ clientId: string;
34
+ scopes: string[];
35
+ }>;
18
36
  };
@@ -3,7 +3,11 @@ import { type EndUserMeResponse } from "./dev-portal-constants.js";
3
3
  export declare function resolveDevPortalApiBaseUrl(config: Pick<DevPortalAuthenticationConfig, "apiBaseUrl">): string | undefined;
4
4
  export declare function isPlaceholderDevPortalApiUrl(apiBaseUrl: string | undefined): boolean;
5
5
  export declare function toAbsoluteReturnUrl(pathOrUrl: string): string;
6
- export declare function buildDevPortalLoginUrl(apiBaseUrl: string, returnUrl: string): string;
6
+ export declare function buildDevPortalOidcChallengeUrl(apiBaseUrl: string, providerId: string, returnUrl: string): string;
7
7
  export declare function buildDevPortalLogoutUrl(apiBaseUrl: string, returnUrl: string): string;
8
+ export declare function normalizeDevPortalAuthConfig(raw: unknown): import("./dev-portal-constants.js").DevPortalAuthConfigResponse;
9
+ export declare function isNativeAuthEnabled(config: import("./dev-portal-constants.js").DevPortalAuthConfigResponse | null | undefined): boolean;
10
+ export declare function fetchDevPortalAuthConfig(apiBaseUrl: string, fetchImpl?: typeof fetch): Promise<import("./dev-portal-constants.js").DevPortalAuthConfigResponse>;
11
+ export declare function postDevPortalAuth<T>(apiBaseUrl: string, path: string, body: unknown, fetchImpl?: typeof fetch): Promise<T>;
8
12
  export declare function probeDevPortalBackend(apiBaseUrl: string, fetchImpl?: typeof fetch): Promise<boolean>;
9
13
  export declare function mapEndUserMeToProfile(me: EndUserMeResponse): import("../state.js").UserProfile;
@@ -21,7 +21,12 @@ export declare class DevPortalAuthenticationProvider extends CoreAuthenticationP
21
21
  private backendAvailable;
22
22
  private refreshGeneration;
23
23
  private refreshInFlight;
24
+ private authConfigNativeEnabled;
24
25
  constructor({ apiBaseUrl, redirectToAfterSignIn, redirectToAfterSignUp, redirectToAfterSignOut, }: DevPortalAuthenticationConfig);
26
+ getRoutes(): {
27
+ path: string;
28
+ element: import("react/jsx-runtime").JSX.Element;
29
+ }[];
25
30
  initialize(_context: ZudokuContext): Promise<void>;
26
31
  onPageLoad: () => Promise<void>;
27
32
  private syncBackendAvailability;
@@ -319,8 +319,24 @@ export interface FlatZudokuConfig {
319
319
  redirectToAfterSignIn?: string
320
320
  redirectToAfterSignOut?: string
321
321
  } | {
322
- type: "dev-portal"
322
+ type?: "dev-portal"
323
323
  apiBaseUrl?: string
324
+ native?: {
325
+ enabled?: boolean
326
+ }
327
+ email?: {
328
+ provider?: string
329
+ from?: string
330
+ connectionString?: string
331
+ }
332
+ providers?: {
333
+ id: string
334
+ displayName?: string
335
+ authority?: string
336
+ clientId?: string
337
+ clientSecret?: string
338
+ scopes?: string
339
+ }[]
324
340
  redirectToAfterSignUp?: string
325
341
  redirectToAfterSignIn?: string
326
342
  redirectToAfterSignOut?: string
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukoweb/apitogo",
3
- "version": "0.1.53",
3
+ "version": "0.1.54",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -34,12 +34,20 @@ const SITE_CONFIG_KEYS = [
34
34
 
35
35
  export type ApitogoJsonConfig = Record<string, unknown>;
36
36
 
37
+ export type AuthProviderPublicConfig = {
38
+ id: string;
39
+ displayName?: string;
40
+ authority?: string;
41
+ clientId?: string;
42
+ scopes?: string;
43
+ };
44
+
37
45
  export type AuthenticationSecrets = {
38
- oidc?: {
39
- authority?: string;
40
- clientId?: string;
41
- clientSecret?: string;
46
+ email?: {
47
+ connectionString?: string;
42
48
  };
49
+ /** Provider secrets keyed by provider `id` (from env: `authentication__providers__{id}__clientSecret`). */
50
+ providers?: Record<string, { clientSecret?: string }>;
43
51
  stripe?: {
44
52
  secretKey?: string;
45
53
  webhookSecret?: string;
@@ -351,6 +359,7 @@ export function normalizeApitogoConfig(
351
359
 
352
360
  migratePlanCatalog(normalized);
353
361
  syncBillingEnabled(normalized);
362
+ syncAuthentication(normalized);
354
363
 
355
364
  return normalized;
356
365
  }
@@ -364,6 +373,119 @@ export function isBillingEnabled(config: ApitogoJsonConfig): boolean {
364
373
  return plans?.enabled === true;
365
374
  }
366
375
 
376
+ function isUserPanelEnabled(normalized: ApitogoJsonConfig): boolean {
377
+ const modules = asRecord(normalized.modules);
378
+ const userPanel = asRecord(modules?.userPanel);
379
+ if (userPanel?.enabled === true) {
380
+ return true;
381
+ }
382
+
383
+ const dashboard = asRecord(userPanel?.dashboard);
384
+ const userManagement = asRecord(userPanel?.userManagement);
385
+ const apiKeys = asRecord(userPanel?.apiKeys);
386
+ const plans = asRecord(userPanel?.plans);
387
+
388
+ return (
389
+ dashboard?.enabled === true ||
390
+ userManagement?.enabled === true ||
391
+ apiKeys?.enabled === true ||
392
+ plans?.enabled === true
393
+ );
394
+ }
395
+
396
+ /** Read public OIDC provider entries from `authentication.providers` array. */
397
+ export function readAuthProviders(
398
+ config: ApitogoJsonConfig,
399
+ ): AuthProviderPublicConfig[] {
400
+ const normalized = normalizeApitogoConfig(config);
401
+ const authentication = asRecord(normalized.authentication);
402
+ const providers = authentication?.providers;
403
+ if (!Array.isArray(providers)) {
404
+ return [];
405
+ }
406
+
407
+ return providers
408
+ .map((entry) => asRecord(entry))
409
+ .filter((entry): entry is Record<string, unknown> => entry !== undefined)
410
+ .filter((entry) => typeof entry.id === "string" && entry.id.trim())
411
+ .map((entry) => ({
412
+ id: String(entry.id).trim(),
413
+ displayName:
414
+ typeof entry.displayName === "string" ? entry.displayName : undefined,
415
+ authority:
416
+ typeof entry.authority === "string" ? entry.authority : undefined,
417
+ clientId: typeof entry.clientId === "string" ? entry.clientId : undefined,
418
+ scopes: typeof entry.scopes === "string" ? entry.scopes : undefined,
419
+ }));
420
+ }
421
+
422
+ export function isNativeAuthEnabled(config: ApitogoJsonConfig): boolean {
423
+ const normalized = normalizeApitogoConfig(config);
424
+ if (!isUserPanelEnabled(normalized)) {
425
+ return false;
426
+ }
427
+
428
+ const native = asRecord(asRecord(normalized.authentication)?.native);
429
+ return native?.enabled !== false;
430
+ }
431
+
432
+ function syncAuthentication(normalized: ApitogoJsonConfig): void {
433
+ const userPanelEnabled = isUserPanelEnabled(normalized);
434
+ const authentication = asRecord(normalized.authentication);
435
+
436
+ if (!userPanelEnabled && !authentication) {
437
+ return;
438
+ }
439
+
440
+ const auth = authentication ?? {};
441
+ auth.enabled = userPanelEnabled;
442
+ auth.type = "dev-portal";
443
+
444
+ if (userPanelEnabled) {
445
+ const native = asRecord(auth.native) ?? {};
446
+ if (native.enabled === undefined) {
447
+ native.enabled = true;
448
+ }
449
+ auth.native = native;
450
+
451
+ if (!Array.isArray(auth.providers)) {
452
+ auth.providers = [];
453
+ }
454
+ }
455
+
456
+ normalized.authentication = auth;
457
+ }
458
+
459
+ function stripAuthenticationSecrets(
460
+ authentication: Record<string, unknown>,
461
+ ): Record<string, unknown> {
462
+ const {
463
+ gateway: _gateway,
464
+ oidc: _oidc,
465
+ stripe: _stripe,
466
+ type: _type,
467
+ email,
468
+ providers,
469
+ ...authPublic
470
+ } = authentication;
471
+
472
+ const emailRecord = asRecord(email);
473
+ if (emailRecord) {
474
+ const { connectionString: _connectionString, ...emailPublic } = emailRecord;
475
+ authPublic.email = emailPublic;
476
+ }
477
+
478
+ if (Array.isArray(providers)) {
479
+ authPublic.providers = providers.map((entry) => {
480
+ const provider = asRecord(entry) ?? {};
481
+ const { clientSecret: _clientSecret, ...providerPublic } = provider;
482
+ return providerPublic;
483
+ });
484
+ }
485
+
486
+ return authPublic;
487
+ }
488
+
367
489
  function syncBillingEnabled(normalized: ApitogoJsonConfig): void {
368
490
  const legacyPricing = asRecord(normalized.pricing);
369
491
  const modules = ensureModules(normalized);
@@ -400,13 +522,7 @@ export function deriveManifestFields(
400
522
  if (key === "authentication") {
401
523
  const authentication = asRecord(normalized.authentication);
402
524
  if (authentication) {
403
- const {
404
- gateway: _gateway,
405
- oidc: _oidc,
406
- stripe: _stripe,
407
- ...authPublic
408
- } = authentication;
409
- manifest.authentication = authPublic;
525
+ manifest.authentication = stripAuthenticationSecrets(authentication);
410
526
  }
411
527
  continue;
412
528
  }
@@ -36,19 +36,39 @@ export function readAuthenticationSecrets(
36
36
  ): AuthenticationSecrets {
37
37
  const overrides = applyEnvOverrides({}, env);
38
38
  const authentication = asRecord(overrides.authentication);
39
- const oidc = asRecord(authentication?.oidc);
39
+ const email = asRecord(authentication?.email);
40
+ const providers = asRecord(authentication?.providers);
40
41
  const stripe = asRecord(authentication?.stripe);
41
42
 
42
43
  const secrets: AuthenticationSecrets = {};
43
- if (oidc) {
44
- secrets.oidc = {
45
- authority:
46
- typeof oidc.authority === "string" ? oidc.authority : undefined,
47
- clientId: typeof oidc.clientId === "string" ? oidc.clientId : undefined,
48
- clientSecret:
49
- typeof oidc.clientSecret === "string" ? oidc.clientSecret : undefined,
44
+
45
+ if (email?.connectionString) {
46
+ secrets.email = {
47
+ connectionString:
48
+ typeof email.connectionString === "string"
49
+ ? email.connectionString
50
+ : undefined,
50
51
  };
51
52
  }
53
+
54
+ if (providers) {
55
+ const providerSecrets: Record<string, { clientSecret?: string }> = {};
56
+ for (const [id, value] of Object.entries(providers)) {
57
+ const provider = asRecord(value);
58
+ if (provider?.clientSecret) {
59
+ providerSecrets[id] = {
60
+ clientSecret:
61
+ typeof provider.clientSecret === "string"
62
+ ? provider.clientSecret
63
+ : undefined,
64
+ };
65
+ }
66
+ }
67
+ if (Object.keys(providerSecrets).length > 0) {
68
+ secrets.providers = providerSecrets;
69
+ }
70
+ }
71
+
52
72
  if (stripe) {
53
73
  secrets.stripe = {
54
74
  secretKey:
@@ -55,8 +55,29 @@ export const DevPortalLocalManifestSchema = z.object({
55
55
  authentication: z
56
56
  .object({
57
57
  enabled: z.boolean().optional(),
58
- type: z.string().optional(),
59
58
  apiBaseUrl: z.string().optional(),
59
+ native: z
60
+ .object({
61
+ enabled: z.boolean().optional(),
62
+ })
63
+ .optional(),
64
+ email: z
65
+ .object({
66
+ provider: z.string().optional(),
67
+ from: z.string().optional(),
68
+ })
69
+ .optional(),
70
+ providers: z
71
+ .array(
72
+ z.object({
73
+ id: z.string().min(1),
74
+ displayName: z.string().optional(),
75
+ authority: z.string().optional(),
76
+ clientId: z.string().optional(),
77
+ scopes: z.string().optional(),
78
+ }),
79
+ )
80
+ .optional(),
60
81
  })
61
82
  .optional(),
62
83
  plans: z.array(ManifestPlanInputSchema).optional(),
@@ -116,6 +137,16 @@ export function isAuthenticationEnabled(
116
137
  return manifest?.authentication?.enabled === true;
117
138
  }
118
139
 
140
+ export function isNativeAuthEnabledInManifest(
141
+ manifest: DevPortalLocalManifest | null | undefined,
142
+ ): boolean {
143
+ if (!isAuthenticationEnabled(manifest)) {
144
+ return false;
145
+ }
146
+
147
+ return manifest?.authentication?.native?.enabled !== false;
148
+ }
149
+
119
150
  export function manifestToPreviewCatalog(
120
151
  manifest: DevPortalLocalManifest | null | undefined,
121
152
  ): DevPortalPreviewPlanCatalog {
@@ -492,8 +492,32 @@ const AuthenticationSchema = z.discriminatedUnion("type", [
492
492
  redirectToAfterSignOut: z.string().optional(),
493
493
  }),
494
494
  z.object({
495
- type: z.literal("dev-portal"),
495
+ type: z.literal("dev-portal").optional().default("dev-portal"),
496
496
  apiBaseUrl: z.string().optional(),
497
+ native: z
498
+ .object({
499
+ enabled: z.boolean().optional(),
500
+ })
501
+ .optional(),
502
+ email: z
503
+ .object({
504
+ provider: z.string().optional(),
505
+ from: z.string().optional(),
506
+ connectionString: z.string().optional(),
507
+ })
508
+ .optional(),
509
+ providers: z
510
+ .array(
511
+ z.object({
512
+ id: z.string().min(1),
513
+ displayName: z.string().optional(),
514
+ authority: z.string().optional(),
515
+ clientId: z.string().optional(),
516
+ clientSecret: z.string().optional(),
517
+ scopes: z.string().optional(),
518
+ }),
519
+ )
520
+ .optional(),
497
521
  redirectToAfterSignUp: z.string().optional(),
498
522
  redirectToAfterSignIn: z.string().optional(),
499
523
  redirectToAfterSignOut: z.string().optional(),