diff --git a/app/auth-portal/src/store/auth.store.ts b/app/auth-portal/src/store/auth.store.ts index ac416e1dff..d409c56e6a 100644 --- a/app/auth-portal/src/store/auth.store.ts +++ b/app/auth-portal/src/store/auth.store.ts @@ -164,7 +164,7 @@ export const useAuthStore = defineStore("auth", { // split from LOAD_USER since it will likely change // and because this request loading blocks the whole page/app async CHECK_AUTH() { - return new ApiRequest<{ user: User }>({ + const req = new ApiRequest<{ user: User }>({ url: "/whoami", onSuccess: (response) => { this.user = response.user; @@ -173,10 +173,46 @@ export const useAuthStore = defineStore("auth", { posthog.alias(this.user.id, this.user.email); } }, - onFail(e) { + onFail: async (e) => { /* eslint-disable-next-line no-console */ console.log("RESTORE AUTH FAILED!", e); - // trigger logout? + + // Try local login - backend will reject if LOCAL_AUTH_MODE is not enabled + // This auto-detects local mode without requiring frontend config + /* eslint-disable-next-line no-console */ + console.log("Attempting local auth auto-detection..."); + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this.LOCAL_LOGIN(); + }, + }); + return req; + }, + + /** + * LOCAL AUTH MODE ONLY + * Authenticates with local development auth (no Auth0) + * Backend will reject this if LOCAL_AUTH_MODE is not enabled + */ + async LOCAL_LOGIN(email?: string) { + return new ApiRequest<{ user: User; token: string }>({ + method: "post", + url: "/auth/local-login", + params: { email }, + onSuccess: (response) => { + this.user = response.user; + posthog.identify(this.user.id); + if (this.user.email) { + posthog.alias(this.user.id, this.user.email); + } + /* eslint-disable-next-line no-console */ + console.log("🔧 LOCAL AUTH MODE: Local login successful", { + userId: response.user.id, + email: response.user.email, + }); + }, + onFail: (e) => { + // Silently fail - this is expected when not in local mode + // Backend returns 403 "LocalAuthDisabled" when LOCAL_AUTH_MODE is not enabled }, }); }, diff --git a/app/auth-portal/vite.config.ts b/app/auth-portal/vite.config.ts index 63e3f7c16f..afc9d3bcd4 100644 --- a/app/auth-portal/vite.config.ts +++ b/app/auth-portal/vite.config.ts @@ -37,12 +37,13 @@ export default defineConfig({ process.env.NODE_ENV !== "production" && checkerPlugin({ vueTsc: true, - eslint: { - lintCommand: packageJson.scripts.lint, - // I _think_ we only want to pop up an error on the screen for proper errors - // otherwise we can get a lot of unused var errors when you comment something out temporarily - dev: { logLevel: ["error"] }, - }, + // NOTE: ESLint checker disabled due to incompatibility between + // vite-plugin-checker@0.12.0 and ESLint 9 + // ESLint can still be run manually via: pnpm lint + // eslint: { + // lintCommand: packageJson.scripts.lint, + // dev: { logLevel: ["error"] }, + // }, }), // https://github.com/btd/rollup-plugin-visualizer/issues/176 diff --git a/app/web/.env b/app/web/.env index 55b821db32..ce7cba16ab 100644 --- a/app/web/.env +++ b/app/web/.env @@ -5,8 +5,6 @@ VITE_SI_ENV=local VITE_API_PROXY_PATH=/api -VITE_AUTH_API_URL=https://auth-api.systeminit.com -VITE_AUTH_PORTAL_URL=https://auth.systeminit.com VITE_AUTH0_DOMAIN=systeminit.auth0.com VITE_BACKEND_HOSTS=["/localhost/g","/si.keeb.dev/g","/app.systeminit.com/g","/tools.systeminit.com/g"] diff --git a/bin/auth-api/src/main.ts b/bin/auth-api/src/main.ts index 1cced19795..d380ce101c 100644 --- a/bin/auth-api/src/main.ts +++ b/bin/auth-api/src/main.ts @@ -15,6 +15,7 @@ import { httpRequestLoggingMiddleware } from "./lib/request-logger"; import { loadAuthMiddleware, requireWebTokenMiddleware } from "./services/auth.service"; import { detectClientIp } from "./lib/client-ip"; import { CustomAppContext, CustomAppState } from "./custom-state"; +import { logLocalAuthWarning } from "./services/auth0-local.service"; import './lib/posthog'; @@ -47,6 +48,10 @@ if (process.env.NODE_ENV !== 'test') { await routesLoaded; app.listen(process.env.PORT); console.log(chalk.green.bold(`Auth API listening on port ${process.env.PORT}`)); + + // Log warning if local auth mode is enabled + logLocalAuthWarning(); + // await prisma.$disconnect(); } catch (err) { console.log('ERROR!', err); diff --git a/bin/auth-api/src/routes/auth.routes.ts b/bin/auth-api/src/routes/auth.routes.ts index 464f769ee3..2c7062ce77 100644 --- a/bin/auth-api/src/routes/auth.routes.ts +++ b/bin/auth-api/src/routes/auth.routes.ts @@ -8,6 +8,10 @@ import { getAuth0LogoutUrl, getAuth0UserCredential, } from "../services/auth0.service"; +import { + completeLocalAuth0TokenExchange, + isLocalAuth, +} from "../services/auth0-local.service"; import { createAuthToken, createSdfAuthToken, @@ -49,6 +53,23 @@ const parseCliRedirectPort = (port: string[] | string): number => { }; router.get("/auth/login", async (ctx) => { + // LOCAL AUTH MODE: bypass Auth0 entirely for local development + if (isLocalAuth()) { + // eslint-disable-next-line no-console + console.log(JSON.stringify({ + timestamp: new Date().toISOString(), + level: "info", + type: "local-auth", + action: "login_redirect", + message: "🔧 LOCAL AUTH MODE: Redirecting to local login - NO Auth0 calls", + })); + + // Redirect directly to local login success (auto-authenticate) + ctx.redirect(`${process.env.AUTH_PORTAL_URL}/login-success?local=true`); + return; + } + + // PRODUCTION MODE: Normal Auth0 OAuth flow // passing in cli_redir=PORT_NO will begin the auth flow for the si cli const cliRedirParam = ctx.request.query.cli_redir; const cliRedirect = cliRedirParam @@ -178,6 +199,67 @@ router.get("/auth/login-callback", async (ctx) => { } }); +/** + * LOCAL AUTH MODE ONLY + * Simple login endpoint that bypasses Auth0 entirely + * Creates/updates local user and returns session token + */ +router.post("/auth/local-login", async (ctx) => { + if (!isLocalAuth()) { + throw new ApiError("Forbidden", "LocalAuthDisabled", "Local auth mode is not enabled"); + } + + // eslint-disable-next-line no-console + console.log(JSON.stringify({ + timestamp: new Date().toISOString(), + level: "info", + type: "local-auth", + action: "local_login", + message: "🔧 LOCAL AUTH MODE: Processing local login - NO Auth0 interaction", + })); + + const reqBody = validate( + ctx.request.body, + z.object({ + email: z.string().email().optional(), + }), + ); + + // Use local mock Auth0 profile + const { profile } = await completeLocalAuth0TokenExchange(reqBody.email); + const user = await createOrUpdateUserFromAuth0Details(profile); + + // eslint-disable-next-line no-console + console.log(JSON.stringify({ + timestamp: new Date().toISOString(), + level: "info", + type: "local-auth", + action: "user_authenticated", + userId: user.id, + email: user.email, + message: "🔧 LOCAL AUTH MODE: User authenticated locally", + })); + + // Create session token for auth-api communication + const siToken = createAuthToken(user.id); + + ctx.cookies.set(SI_COOKIE_NAME, siToken, { + httpOnly: true, + secure: false, // Local development doesn't use HTTPS + }); + + ctx.body = { + user: { + id: user.id, + email: user.email, + firstName: user.firstName, + lastName: user.lastName, + nickname: user.nickname, + }, + token: siToken, + }; +}); + router.get("/auth/cli-auth-api-token", async (ctx) => { const nonce = ctx.request.query.nonce; if (!nonce) { diff --git a/bin/auth-api/src/routes/user.routes.ts b/bin/auth-api/src/routes/user.routes.ts index 93d20e6ac5..b5569c51d7 100644 --- a/bin/auth-api/src/routes/user.routes.ts +++ b/bin/auth-api/src/routes/user.routes.ts @@ -28,6 +28,7 @@ import { refreshUserAuth0Profile, saveUser, } from "../services/users.service"; +import { isLocalAuth } from "../services/auth0-local.service"; import { resendAuth0EmailVerification } from "../services/auth0.service"; import { tracker } from "../lib/tracker"; import { createProductionWorkspaceForUser } from "../services/workspaces.service"; @@ -484,6 +485,25 @@ router.post("/users/:userId/dismissFirstTimeModal", async (ctx) => { router.get("/users/:userId/firstTimeModal", async (ctx) => { const user = await extractOwnUserIdParam(ctx); + // LOCAL AUTH MODE: Always skip onboarding for local users + if (isLocalAuth()) { + // eslint-disable-next-line no-console + console.log(JSON.stringify({ + timestamp: new Date().toISOString(), + level: "info", + type: "local-auth", + action: "check_onboarding", + userId: user.id, + message: "🔧 LOCAL AUTH MODE: Returning firstTimeModal=false to skip onboarding", + })); + + ctx.body = { + firstTimeModal: false, + }; + return; + } + + // PRODUCTION MODE: Return actual value from database ctx.body = { firstTimeModal: (user?.onboardingDetails)?.firstTimeModal, }; diff --git a/bin/auth-api/src/routes/workspace.routes.ts b/bin/auth-api/src/routes/workspace.routes.ts index 4055a16f9c..e47a7eab70 100644 --- a/bin/auth-api/src/routes/workspace.routes.ts +++ b/bin/auth-api/src/routes/workspace.routes.ts @@ -44,6 +44,7 @@ import { tracker } from "../lib/tracker"; import { posthog } from "../lib/posthog"; import { findLatestTosForUser } from "../services/tos.service"; import { automationApiRouter, extractAuthUser, router } from "."; +import { isLocalAuth } from "../services/auth0-local.service"; // When we send a hubspot email via the posthog event // if the workspace name is a domain name like string e.g. bing.com @@ -560,29 +561,44 @@ router.patch("/workspaces/:workspaceId/setHidden", async (ctx) => { router.get("/workspaces/:workspaceId/go", async (ctx) => { const { authUser, workspace } = await authorizeWorkspaceRoute(ctx); - // we require the user to have verified their email before they can log into a workspace - if (!authUser.emailVerified) { - // we'll first refresh from auth0 to make sure its actually not verified - await refreshUserAuth0Profile(authUser); - // then throw an error + // LOCAL AUTH MODE: Skip email verification and ToS checks + if (isLocalAuth()) { + // eslint-disable-next-line no-console + console.log(JSON.stringify({ + timestamp: new Date().toISOString(), + level: "info", + type: "local-auth", + action: "skip_verification", + userId: authUser.id, + workspaceId: workspace.id, + message: "🔧 LOCAL AUTH MODE: Skipping email verification and ToS checks", + })); + } else { + // PRODUCTION MODE: Enforce email verification and ToS acceptance + // we require the user to have verified their email before they can log into a workspace if (!authUser.emailVerified) { + // we'll first refresh from auth0 to make sure its actually not verified + await refreshUserAuth0Profile(authUser); + // then throw an error + if (!authUser.emailVerified) { + throw new ApiError( + "Unauthorized", + "EmailNotVerified", + "System Initiative Requires Verified Emails to access Workspaces. Check your registered email for Verification email from SI Auth Portal.", + ); + } + } + + const latestTos = await findLatestTosForUser(authUser); + if (latestTos > authUser.agreedTosVersion) { throw new ApiError( "Unauthorized", - "EmailNotVerified", - "System Initiative Requires Verified Emails to access Workspaces. Check your registered email for Verification email from SI Auth Portal.", + "MissingTosAcceptance", + "Terms of Service have been updated, return to the SI auth portal to accept them.", ); } } - const latestTos = await findLatestTosForUser(authUser); - if (latestTos > authUser.agreedTosVersion) { - throw new ApiError( - "Unauthorized", - "MissingTosAcceptance", - "Terms of Service have been updated, return to the SI auth portal to accept them.", - ); - } - const { redirect } = validate( ctx.request.query, z.object({ diff --git a/bin/auth-api/src/services/auth0-local.service.ts b/bin/auth-api/src/services/auth0-local.service.ts new file mode 100644 index 0000000000..6b9fc81ad6 --- /dev/null +++ b/bin/auth-api/src/services/auth0-local.service.ts @@ -0,0 +1,155 @@ +/* eslint-disable no-console */ +/** + * Local development Auth0 mock service + * + * This service provides a local-only authentication bypass for development. + * It replaces Auth0 calls with mock data when LOCAL_AUTH_MODE=true. + * + * NO REMOTE CALLS ARE MADE - all authentication is local. + */ + +import { ApiResponse } from "auth0"; +import type { GetUsers200ResponseOneOfInner as Auth0User } from "auth0"; +import { ulid } from "ulidx"; + +const isLocalAuthMode = process.env.LOCAL_AUTH_MODE === "true"; + +// Hardcoded local development user configuration +const LOCAL_USER_EMAIL = "dev@systeminit.local"; +const LOCAL_USER_FIRST_NAME = "Local"; +const LOCAL_USER_LAST_NAME = "Developer"; +const LOCAL_USER_NICKNAME = "localdev"; + +/** + * Creates a mock Auth0 user profile for local development + */ +function createLocalAuth0Profile(auth0Id: string): ApiResponse { + console.log(JSON.stringify({ + timestamp: new Date().toISOString(), + level: "info", + type: "local-auth", + action: "create_mock_profile", + auth0Id, + email: LOCAL_USER_EMAIL, + message: "🔧 LOCAL AUTH MODE: Creating mock Auth0 profile - NO REMOTE CALLS", + })); + + const profile: Auth0User = { + user_id: auth0Id, + email: LOCAL_USER_EMAIL, + email_verified: true, // Always verified in local mode + nickname: LOCAL_USER_NICKNAME, + given_name: LOCAL_USER_FIRST_NAME, + family_name: LOCAL_USER_LAST_NAME, + name: `${LOCAL_USER_FIRST_NAME} ${LOCAL_USER_LAST_NAME}`, + picture: `https://ui-avatars.com/api/?name=${LOCAL_USER_FIRST_NAME}+${LOCAL_USER_LAST_NAME}&background=random`, + updated_at: new Date().toISOString(), + created_at: new Date().toISOString(), + }; + + return { + data: profile, + status: 200, + statusText: "OK (Mock)", + headers: {}, + } as ApiResponse; +} + +/** + * Mock implementation of completeAuth0TokenExchange for local development + * Bypasses Auth0 OAuth flow entirely + */ +export async function completeLocalAuth0TokenExchange(email?: string) { + if (!isLocalAuthMode) { + throw new Error("completeLocalAuth0TokenExchange can only be called when LOCAL_AUTH_MODE=true"); + } + + console.log(JSON.stringify({ + timestamp: new Date().toISOString(), + level: "info", + type: "local-auth", + action: "token_exchange", + email: email || LOCAL_USER_EMAIL, + message: "🔧 LOCAL AUTH MODE: Skipping Auth0 token exchange - using local credentials", + })); + + // Generate a stable auth0Id based on email for consistency across restarts + const auth0Id = `local|${Buffer.from(email || LOCAL_USER_EMAIL).toString('base64').replace(/=/g, '')}`; + + const profile = createLocalAuth0Profile(auth0Id); + + // Return mock access token (not actually used in local mode) + const mockToken = `local_dev_token_${ulid()}`; + + console.log(JSON.stringify({ + timestamp: new Date().toISOString(), + level: "info", + type: "local-auth", + action: "token_exchange_complete", + auth0Id, + message: "🔧 LOCAL AUTH MODE: Token exchange complete - user authenticated locally", + })); + + return { profile, token: mockToken }; +} + +/** + * Mock implementation of fetchAuth0Profile for local development + * Returns a mock profile without calling Auth0 Management API + */ +export async function fetchLocalAuth0Profile(auth0Id: string): Promise> { + if (!isLocalAuthMode) { + throw new Error("fetchLocalAuth0Profile can only be called when LOCAL_AUTH_MODE=true"); + } + + console.log(JSON.stringify({ + timestamp: new Date().toISOString(), + level: "info", + type: "local-auth", + action: "fetch_profile", + auth0Id, + message: "🔧 LOCAL AUTH MODE: Fetching mock profile - NO Auth0 Management API call", + })); + + return createLocalAuth0Profile(auth0Id); +} + +/** + * Returns the mock logout URL for local development + */ +export function getLocalAuth0LogoutUrl(): string { + console.log(JSON.stringify({ + timestamp: new Date().toISOString(), + level: "info", + type: "local-auth", + action: "logout_url", + message: "🔧 LOCAL AUTH MODE: Generating local logout URL", + })); + + return `${process.env.AUTH_PORTAL_URL}?logged_out=true`; +} + +/** + * Check if local auth mode is enabled + */ +export function isLocalAuth(): boolean { + return isLocalAuthMode; +} + +/** + * Logs a warning if local auth mode is enabled at startup + */ +export function logLocalAuthWarning(): void { + if (isLocalAuthMode) { + console.log(JSON.stringify({ + timestamp: new Date().toISOString(), + level: "warn", + type: "local-auth", + action: "startup", + message: "🔧🔧🔧 LOCAL AUTH MODE ENABLED 🔧🔧🔧", + details: "Auth0 is BYPASSED - all authentication is local - DO NOT USE IN PRODUCTION", + user: `${LOCAL_USER_EMAIL} (${LOCAL_USER_FIRST_NAME} ${LOCAL_USER_LAST_NAME})`, + workspace: "Local Development @ http://localhost:8080", + })); + } +} diff --git a/bin/auth-api/src/services/users.service.ts b/bin/auth-api/src/services/users.service.ts index 9b39bf5a42..f8ac77ad5e 100644 --- a/bin/auth-api/src/services/users.service.ts +++ b/bin/auth-api/src/services/users.service.ts @@ -3,9 +3,10 @@ import { ulid } from "ulidx"; import * as Auth0 from "auth0"; import { InstanceEnvType, Prisma, PrismaClient } from "@prisma/client"; -import { createWorkspace, SAAS_WORKSPACE_URL } from "./workspaces.service"; +import { createWorkspace, SAAS_WORKSPACE_URL, LOCAL_WORKSPACE_URL } from "./workspaces.service"; import { tracker } from "../lib/tracker"; import { fetchAuth0Profile } from "./auth0.service"; +import { isLocalAuth } from "./auth0-local.service"; import { ApiError } from "../lib/api-error"; import { findLatestTosForUser } from "./tos.service"; import { @@ -133,22 +134,63 @@ export async function createOrUpdateUserFromAuth0Details( setUserDataFromAuth0Details(user, auth0UserData, isSignup); + // LOCAL AUTH MODE: Mark onboarding as complete for ALL local users + // (not just signups - in case user already exists from previous run) + if (isLocalAuth()) { + user.onboardingDetails = { + ...(typeof user.onboardingDetails === 'object' ? user.onboardingDetails : {}), + reviewedProfile: new Date().toISOString(), + firstTimeModal: false, // Skip the onboarding modal + } as Prisma.JsonObject; + + // eslint-disable-next-line no-console + console.log(JSON.stringify({ + timestamp: new Date().toISOString(), + level: "info", + type: "local-auth", + action: "skip_onboarding", + userId: user.id, + isSignup, + message: "🔧 LOCAL AUTH MODE: Marking onboarding as complete for local user", + })); + } + await prisma.user.update({ where: { id: user.id }, data: { ..._.omit(user, "id", "onboardingDetails"), auth0Details: auth0UserData as Prisma.JsonObject, + ...(user.onboardingDetails && { onboardingDetails: user.onboardingDetails }), }, }); tracker.identifyUser(user); } else { isSignup = true; + const userData = setUserDataFromAuth0Details({}, auth0UserData, isSignup); + + // LOCAL AUTH MODE: Mark onboarding as complete + if (isLocalAuth()) { + userData.onboardingDetails = { + reviewedProfile: new Date().toISOString(), + firstTimeModal: false, // Skip the onboarding modal + } as Prisma.JsonObject; + + // eslint-disable-next-line no-console + console.log(JSON.stringify({ + timestamp: new Date().toISOString(), + level: "info", + type: "local-auth", + action: "skip_onboarding", + message: "🔧 LOCAL AUTH MODE: Marking onboarding as complete for local user", + })); + } + user = await prisma.user.create({ data: { id: ulid(), signupAt: new Date(), auth0Id, - ...setUserDataFromAuth0Details({}, auth0UserData, isSignup), + ...userData, }, }); @@ -163,14 +205,39 @@ export async function createOrUpdateUserFromAuth0Details( lastName: user.lastName, }); - await createWorkspace( - user, - InstanceEnvType.SI, - SAAS_WORKSPACE_URL, - `${user.nickname}'s Production Workspace`, - true, - "", - ); + // LOCAL AUTH MODE: Create local development workspace + if (isLocalAuth()) { + // eslint-disable-next-line no-console + console.log(JSON.stringify({ + timestamp: new Date().toISOString(), + level: "info", + type: "local-auth", + action: "create_workspace", + userId: user.id, + workspaceName: "Local Development", + workspaceUrl: LOCAL_WORKSPACE_URL, + message: "🔧 LOCAL AUTH MODE: Auto-creating local development workspace", + })); + + await createWorkspace( + user, + InstanceEnvType.LOCAL, + LOCAL_WORKSPACE_URL, + "Local Development", + true, + "", + ); + } else { + // PRODUCTION MODE: Create SaaS production workspace + await createWorkspace( + user, + InstanceEnvType.SI, + SAAS_WORKSPACE_URL, + `${user.nickname}'s Production Workspace`, + true, + "", + ); + } } return user; diff --git a/dev/Tiltfile b/dev/Tiltfile index fcec962c12..aec406216c 100644 --- a/dev/Tiltfile +++ b/dev/Tiltfile @@ -6,6 +6,9 @@ config.define_bool("debug-no-rebaser-rustc-build-mode") cfg = config.parse() +# Auth API URL for services that need to talk to auth-api +SI_AUTH_API_URL = "http://localhost:9001" + # Define groups of services groups = { "platform": [ @@ -17,6 +20,7 @@ groups = { ], "backend": [ "auth-api", + "auth-db-seed", "edda", "forklift", "module-index", @@ -266,6 +270,11 @@ si_buck2_resource( "edda", "forklift", ], + serve_env = { + "SI_FORCE_COLOR": "true", + "SI_LOG_FILE_DIRECTORY": "../log", + "SI_AUTH_API_URL": SI_AUTH_API_URL, + }, readiness_probe = probe( period_secs = 5, http_get = http_get_action( @@ -276,7 +285,7 @@ si_buck2_resource( links = [ link("http://127.0.0.1:5156", "api"), ], - **RUST_RESOURCE_ARGS + trigger_mode = TRIGGER_MODE_MANUAL, ) # Locally build and run `luminork` @@ -294,6 +303,11 @@ si_buck2_resource( "edda", "forklift", ], + serve_env = { + "SI_FORCE_COLOR": "true", + "SI_LOG_FILE_DIRECTORY": "../log", + "SI_AUTH_API_URL": SI_AUTH_API_URL, + }, readiness_probe = probe( period_secs = 5, http_get = http_get_action( @@ -304,7 +318,7 @@ si_buck2_resource( links = [ link("http://127.0.0.1:5380/swagger-ui", "swagger-ui"), ], - **RUST_RESOURCE_ARGS + trigger_mode = TRIGGER_MODE_MANUAL, ) # Locally build and run `web` in dev mode @@ -313,6 +327,10 @@ si_buck2_resource( resource_deps = [ "sdf", ], + serve_env = { + "VITE_AUTH_API_URL": "http://localhost:9001", + "VITE_AUTH_PORTAL_URL": "https://localhost:9000" + }, readiness_probe = probe( period_secs = 5, http_get = http_get_action( @@ -365,6 +383,11 @@ si_buck2_resource( "otelcol", "postgres", ], + serve_env = { + "SI_FORCE_COLOR": "true", + "SI_LOG_FILE_DIRECTORY": "../log", + "SI_AUTH_API_URL": SI_AUTH_API_URL, + }, readiness_probe = probe( period_secs = 5, http_get = http_get_action( @@ -372,16 +395,31 @@ si_buck2_resource( path = "/", ), ), - **RUST_RESOURCE_ARGS + trigger_mode = TRIGGER_MODE_MANUAL, +) + +# Reset and seed the auth database before starting auth-api +# This runs Prisma migrations and generates the client +local_resource( + "auth-db-seed", + cmd = "buck2 run //bin/auth-api:db-reset", + labels = resource_labels("auth-db-seed"), + resource_deps = [ + "postgres", + ], + trigger_mode = TRIGGER_MODE_MANUAL, ) # Locally build and run `auth-api` si_buck2_resource( "//bin/auth-api:dev", - auto_init = False, resource_deps = [ "postgres", + "auth-db-seed", ], + serve_env = { + "LOCAL_AUTH_MODE": "true", + }, # readiness_probe = probe( # period_secs = 5, # http_get = http_get_action( @@ -395,7 +433,6 @@ si_buck2_resource( # Locally build and run `auth-portal` in dev mode si_buck2_resource( "//app/auth-portal:dev", - auto_init = False, resource_deps = [ "auth-api", ],