Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion packages/sdk/src/cli/services/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { getDistDir } from "@/cli/shared/dist-dir";
import { resolveInlineSourcemap } from "@/cli/shared/inline-sourcemap";
import { logger } from "@/cli/shared/logger";
import { buildTriggerContext } from "@/cli/shared/trigger-context";
import { AuthConfigSchema } from "@/parser/service/auth";
import { IdPSchema } from "@/parser/service/idp";
import { SecretsSchema } from "@/parser/service/secrets";
import { StaticWebsiteSchema } from "@/parser/service/staticwebsite";
Expand Down Expand Up @@ -174,7 +175,11 @@ function defineAuth(

let authService: AuthService | undefined;
if (!("external" in config)) {
authService = createAuthService(config, tailorDBServices, externalTailorDBNamespaces);
const parsedConfig = {
...config,
...AuthConfigSchema.parse(config),
Comment on lines +178 to +180
} as typeof config;
authService = createAuthService(parsedConfig, tailorDBServices, externalTailorDBNamespaces);
Comment on lines +178 to +182
}
subgraphs.push({ Type: "auth", Name: config.name });

Expand Down
13 changes: 4 additions & 9 deletions packages/sdk/src/cli/services/auth/service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { type TailorDBService } from "@/cli/services/tailordb/service";
import { IdProviderSchema } from "@/parser/service/auth";
import { AuthConnectionConfigSchema } from "@/parser/service/auth-connection";
import type { AuthOwnConfig } from "@/types/auth";
import type { AuthConnectionConfig } from "@/types/auth-connection.generated";
import type { IdProvider as IdProviderConfig } from "@/types/auth.generated";
Expand Down Expand Up @@ -33,15 +31,12 @@ export function createAuthService(
): AuthService {
const parsedConfig = {
...config,
idProvider: IdProviderSchema.optional().parse(config.idProvider),
idProvider: config.idProvider as IdProviderConfig | undefined,
};

const connections: Record<string, AuthConnectionConfig> = {};
if (config.connections) {
for (const [name, connConfig] of Object.entries(config.connections)) {
connections[name] = AuthConnectionConfigSchema.parse(connConfig);
}
}
const connections: Record<string, AuthConnectionConfig> = config.connections
? { ...(config.connections as Record<string, AuthConnectionConfig>) }
: {};

let userProfile: UserProfile | undefined;

Expand Down
32 changes: 0 additions & 32 deletions packages/sdk/src/configure/services/auth/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,38 +154,6 @@ describe("defineAuth", () => {
});
});

it("rejects configs that include both userProfile and machineUserAttributes", () => {
expect(() => {
// @ts-ignore - @see https://github.com/microsoft/TypeScript/issues/63051
defineAuth("exclusive-attributes", {
// @ts-ignore - userProfile and machineUserAttributes are mutually exclusive; provide exactly one.
userProfile: {
type: db.type("User", {
email: db.string().unique(),
role: db.string(),
}),
usernameField: "email",
attributes: {
email: true,
role: true,
},
},
machineUserAttributes: {
role: t.string(),
email: t.string(),
},
machineUsers: {
admin: {
attributes: {
role: "ADMIN",
email: "admin@example.com",
},
},
},
});
}).toThrow();
});

describe("name literal type inference", () => {
it("infers name as literal type", () => {
const authConfig = defineAuth("my-auth-service", {
Expand Down
15 changes: 1 addition & 14 deletions packages/sdk/src/configure/services/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ export function defineAuth<
MachineUserOnlyAuthInput<MachineUserNames, MachineUserAttributes, ConnectionNames>,
MachineUserNames
>;
/* @__NO_SIDE_EFFECTS__ */
export function defineAuth<
const Name extends string,
const User extends TailorDBInstance,
Expand Down Expand Up @@ -175,19 +176,5 @@ export function defineAuth<
getConnectionToken<C extends string>(connectionName: C): Promise<AuthConnectionTokenResult>;
};

validateAuthConfig(result);

return result as typeof result & AuthDefinitionBrand;
}

function validateAuthConfig(config: {
userProfile?: unknown;
machineUserAttributes?: unknown;
}): void {
const hasUserProfile = config.userProfile !== undefined;
const hasMachineUserAttributes = config.machineUserAttributes !== undefined;

if (hasUserProfile && hasMachineUserAttributes) {
throw new Error("Provide either userProfile or machineUserAttributes, not both.");
}
}
4 changes: 3 additions & 1 deletion packages/sdk/src/parser/service/auth/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,8 @@ describe("AuthConfigSchema userProfile/machineUserAttributes validation", () =>
},
};

expect(() => AuthConfigSchema.parse(config)).toThrow();
expect(() => AuthConfigSchema.parse(config)).toThrow(
/Specify either `userProfile` or `machineUserAttributes`, not both/,
);
});
});
35 changes: 25 additions & 10 deletions packages/sdk/src/parser/service/auth/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,20 +257,35 @@ const AuthConfigBaseSchema = z.object({
});

export const AuthConfigSchema = z
.union([
AuthConfigBaseSchema.extend({
userProfile: z.undefined().optional(),
machineUserAttributes: z.undefined().optional(),
}),
z.xor([
.xor(
[
AuthConfigBaseSchema.extend({
userProfile: UserProfileSchema,
userProfile: UserProfileSchema.optional().describe("User profile configuration"),
machineUserAttributes: z.undefined().optional(),
}),
AuthConfigBaseSchema.extend({
userProfile: z.undefined().optional(),
machineUserAttributes: z.record(z.string(), TailorFieldSchema),
machineUserAttributes: z
.record(z.string(), TailorFieldSchema)
.describe("Machine user attribute fields"),
}),
]),
])
],
{
error: (iss) => {
if (iss.code !== "invalid_union") return undefined;
if (iss.errors.length < 2) return undefined;
const isOnlyMutexViolation = iss.errors.every((variantErrors) =>
variantErrors.every(
(e) =>
e.path.length === 1 &&
(e.path[0] === "userProfile" || e.path[0] === "machineUserAttributes"),
),
);
if (isOnlyMutexViolation) {
return "Specify either `userProfile` or `machineUserAttributes`, not both.";
}
return undefined;
},
},
)
.brand("AuthConfig");
Loading
Loading