diff --git a/libs/common/src/admin-console/abstractions/policy/vnext-policy.service.ts b/libs/common/src/admin-console/abstractions/policy/vnext-policy.service.ts
new file mode 100644
index 00000000000..1e96d6b8d00
--- /dev/null
+++ b/libs/common/src/admin-console/abstractions/policy/vnext-policy.service.ts
@@ -0,0 +1,68 @@
+import { Observable } from "rxjs";
+
+import { UserId } from "../../../types/guid";
+import { PolicyType } from "../../enums";
+import { PolicyData } from "../../models/data/policy.data";
+import { MasterPasswordPolicyOptions } from "../../models/domain/master-password-policy-options";
+import { Policy } from "../../models/domain/policy";
+import { ResetPasswordPolicyOptions } from "../../models/domain/reset-password-policy-options";
+
+export abstract class vNextPolicyService {
+  /**
+   * All policies for the provided user from sync data.
+   * May include policies that are disabled or otherwise do not apply to the user. Be careful using this!
+   * Consider {@link policiesByType$} instead, which will only return policies that should be enforced against the user.
+   */
+  abstract policies$: (userId: UserId) => Observable<Policy[]>;
+
+  /**
+   * @returns all {@link Policy} objects of a given type that apply to the specified user.
+   * A policy "applies" if it is enabled and the user is not exempt (e.g. because they are an Owner).
+   * @param policyType the {@link PolicyType} to search for
+   * @param userId the {@link UserId} to search against
+   */
+  abstract policiesByType$: (policyType: PolicyType, userId: UserId) => Observable<Policy[]>;
+
+  /**
+   * @returns true if a policy of the specified type applies to the specified user, otherwise false.
+   * A policy "applies" if it is enabled and the user is not exempt (e.g. because they are an Owner).
+   * This does not take into account the policy's configuration - if that is important, use {@link policiesByType$} to get the
+   * {@link Policy} objects and then filter by Policy.data.
+   */
+  abstract policyAppliesToUser$: (policyType: PolicyType, userId: UserId) => Observable<boolean>;
+
+  // Policy specific interfaces
+
+  /**
+   * Combines all Master Password policies that apply to the user.
+   * @returns a set of options which represent the minimum Master Password settings that the user must
+   * comply with in order to comply with **all** Master Password policies.
+   */
+  abstract masterPasswordPolicyOptions$: (
+    userId: UserId,
+    policies?: Policy[],
+  ) => Observable<MasterPasswordPolicyOptions | undefined>;
+
+  /**
+   * Evaluates whether a proposed Master Password complies with all Master Password policies that apply to the user.
+   */
+  abstract evaluateMasterPassword: (
+    passwordStrength: number,
+    newPassword: string,
+    enforcedPolicyOptions?: MasterPasswordPolicyOptions,
+  ) => boolean;
+
+  /**
+   * @returns {@link ResetPasswordPolicyOptions} for the specified organization and a boolean indicating whether the policy
+   * is enabled
+   */
+  abstract getResetPasswordPolicyOptions: (
+    policies: Policy[],
+    orgId: string,
+  ) => [ResetPasswordPolicyOptions, boolean];
+}
+
+export abstract class vNextInternalPolicyService extends vNextPolicyService {
+  abstract upsert: (policy: PolicyData, userId: UserId) => Promise<void>;
+  abstract replace: (policies: { [id: string]: PolicyData }, userId: UserId) => Promise<void>;
+}
diff --git a/libs/common/src/admin-console/services/policy/default-vnext-policy.service.spec.ts b/libs/common/src/admin-console/services/policy/default-vnext-policy.service.spec.ts
new file mode 100644
index 00000000000..f58e1d27ee6
--- /dev/null
+++ b/libs/common/src/admin-console/services/policy/default-vnext-policy.service.spec.ts
@@ -0,0 +1,590 @@
+import { mock, MockProxy } from "jest-mock-extended";
+import { firstValueFrom, of } from "rxjs";
+
+import { FakeStateProvider, mockAccountServiceWith } from "../../../../spec";
+import { FakeSingleUserState } from "../../../../spec/fake-state";
+import {
+  OrganizationUserStatusType,
+  OrganizationUserType,
+  PolicyType,
+} from "../../../admin-console/enums";
+import { PermissionsApi } from "../../../admin-console/models/api/permissions.api";
+import { OrganizationData } from "../../../admin-console/models/data/organization.data";
+import { PolicyData } from "../../../admin-console/models/data/policy.data";
+import { MasterPasswordPolicyOptions } from "../../../admin-console/models/domain/master-password-policy-options";
+import { Organization } from "../../../admin-console/models/domain/organization";
+import { Policy } from "../../../admin-console/models/domain/policy";
+import { ResetPasswordPolicyOptions } from "../../../admin-console/models/domain/reset-password-policy-options";
+import { POLICIES } from "../../../admin-console/services/policy/policy.service";
+import { PolicyId, UserId } from "../../../types/guid";
+import { OrganizationService } from "../../abstractions/organization/organization.service.abstraction";
+
+import { DefaultvNextPolicyService, getFirstPolicy } from "./default-vnext-policy.service";
+
+describe("PolicyService", () => {
+  const userId = "userId" as UserId;
+  let stateProvider: FakeStateProvider;
+  let organizationService: MockProxy<OrganizationService>;
+  let singleUserState: FakeSingleUserState<Record<PolicyId, PolicyData>>;
+
+  let policyService: DefaultvNextPolicyService;
+
+  beforeEach(() => {
+    const accountService = mockAccountServiceWith(userId);
+    stateProvider = new FakeStateProvider(accountService);
+    organizationService = mock<OrganizationService>();
+    singleUserState = stateProvider.singleUser.getFake(userId, POLICIES);
+
+    const organizations$ = of([
+      // User
+      organization("org1", true, true, OrganizationUserStatusType.Confirmed, false),
+      // Owner
+      organization(
+        "org2",
+        true,
+        true,
+        OrganizationUserStatusType.Confirmed,
+        false,
+        OrganizationUserType.Owner,
+      ),
+      // Does not use policies
+      organization("org3", true, false, OrganizationUserStatusType.Confirmed, false),
+      // Another User
+      organization("org4", true, true, OrganizationUserStatusType.Confirmed, false),
+      // Another User
+      organization("org5", true, true, OrganizationUserStatusType.Confirmed, false),
+      // Can manage policies
+      organization("org6", true, true, OrganizationUserStatusType.Confirmed, true),
+    ]);
+
+    organizationService.organizations$.calledWith(userId).mockReturnValue(organizations$);
+
+    policyService = new DefaultvNextPolicyService(stateProvider, organizationService);
+  });
+
+  it("upsert", async () => {
+    singleUserState.nextState(
+      arrayToRecord([
+        policyData("1", "test-organization", PolicyType.MaximumVaultTimeout, true, { minutes: 14 }),
+      ]),
+    );
+
+    await policyService.upsert(
+      policyData("99", "test-organization", PolicyType.DisableSend, true),
+      userId,
+    );
+
+    expect(await firstValueFrom(policyService.policies$(userId))).toEqual([
+      {
+        id: "1",
+        organizationId: "test-organization",
+        type: PolicyType.MaximumVaultTimeout,
+        enabled: true,
+        data: { minutes: 14 },
+      },
+      {
+        id: "99",
+        organizationId: "test-organization",
+        type: PolicyType.DisableSend,
+        enabled: true,
+      },
+    ]);
+  });
+
+  it("replace", async () => {
+    singleUserState.nextState(
+      arrayToRecord([
+        policyData("1", "test-organization", PolicyType.MaximumVaultTimeout, true, { minutes: 14 }),
+      ]),
+    );
+
+    await policyService.replace(
+      {
+        "2": policyData("2", "test-organization", PolicyType.DisableSend, true),
+      },
+      userId,
+    );
+
+    expect(await firstValueFrom(policyService.policies$(userId))).toEqual([
+      {
+        id: "2",
+        organizationId: "test-organization",
+        type: PolicyType.DisableSend,
+        enabled: true,
+      },
+    ]);
+  });
+
+  describe("masterPasswordPolicyOptions", () => {
+    it("returns default policy options", async () => {
+      const data: any = {
+        minComplexity: 5,
+        minLength: 20,
+        requireUpper: true,
+      };
+      const model = [
+        new Policy(policyData("1", "test-organization-3", PolicyType.MasterPassword, true, data)),
+      ];
+      jest.spyOn(policyService as any, "policies$").mockReturnValue(of(model));
+
+      const result = await firstValueFrom(policyService.masterPasswordPolicyOptions$(userId));
+
+      expect(result).toEqual({
+        minComplexity: 5,
+        minLength: 20,
+        requireLower: false,
+        requireNumbers: false,
+        requireSpecial: false,
+        requireUpper: true,
+        enforceOnLogin: false,
+      });
+    });
+
+    it("returns undefined", async () => {
+      const data: any = {};
+      const model = [
+        new Policy(
+          policyData("3", "test-organization-3", PolicyType.DisablePersonalVaultExport, true, data),
+        ),
+        new Policy(
+          policyData("4", "test-organization-3", PolicyType.MaximumVaultTimeout, true, data),
+        ),
+      ];
+      jest.spyOn(policyService as any, "policies$").mockReturnValue(of(model));
+
+      const result = await firstValueFrom(policyService.masterPasswordPolicyOptions$(userId));
+
+      expect(result).toBeUndefined();
+    });
+
+    it("returns specified policy options", async () => {
+      const data: any = {
+        minLength: 14,
+      };
+      const model = [
+        new Policy(
+          policyData("3", "test-organization-3", PolicyType.DisablePersonalVaultExport, true, data),
+        ),
+        new Policy(policyData("4", "test-organization-3", PolicyType.MasterPassword, true, data)),
+      ];
+      jest.spyOn(policyService as any, "policies$").mockReturnValue(of(model));
+
+      const result = await firstValueFrom(policyService.masterPasswordPolicyOptions$(userId));
+
+      expect(result).toEqual({
+        minComplexity: 0,
+        minLength: 14,
+        requireLower: false,
+        requireNumbers: false,
+        requireSpecial: false,
+        requireUpper: false,
+        enforceOnLogin: false,
+      });
+    });
+  });
+
+  describe("evaluateMasterPassword", () => {
+    it("false", async () => {
+      const enforcedPolicyOptions = new MasterPasswordPolicyOptions();
+      enforcedPolicyOptions.minLength = 14;
+      const result = policyService.evaluateMasterPassword(10, "password", enforcedPolicyOptions);
+
+      expect(result).toEqual(false);
+    });
+
+    it("true", async () => {
+      const enforcedPolicyOptions = new MasterPasswordPolicyOptions();
+      const result = policyService.evaluateMasterPassword(0, "password", enforcedPolicyOptions);
+
+      expect(result).toEqual(true);
+    });
+  });
+
+  describe("getResetPasswordPolicyOptions", () => {
+    it("default", async () => {
+      const result = policyService.getResetPasswordPolicyOptions([], "");
+
+      expect(result).toEqual([new ResetPasswordPolicyOptions(), false]);
+    });
+
+    it("returns autoEnrollEnabled true", async () => {
+      const data: any = {
+        autoEnrollEnabled: true,
+      };
+      const policies = [
+        new Policy(policyData("5", "test-organization-3", PolicyType.ResetPassword, true, data)),
+      ];
+      const result = policyService.getResetPasswordPolicyOptions(policies, "test-organization-3");
+
+      expect(result).toEqual([{ autoEnrollEnabled: true }, true]);
+    });
+  });
+
+  describe("policiesByType$", () => {
+    it("returns the specified PolicyType", async () => {
+      singleUserState.nextState(
+        arrayToRecord([
+          policyData("policy1", "org1", PolicyType.ActivateAutofill, true),
+          policyData("policy2", "org1", PolicyType.DisablePersonalVaultExport, true),
+        ]),
+      );
+
+      const result = await firstValueFrom(
+        policyService
+          .policiesByType$(PolicyType.DisablePersonalVaultExport, userId)
+          .pipe(getFirstPolicy),
+      );
+
+      expect(result).toEqual({
+        id: "policy2",
+        organizationId: "org1",
+        type: PolicyType.DisablePersonalVaultExport,
+        enabled: true,
+      });
+    });
+
+    it("does not return disabled policies", async () => {
+      singleUserState.nextState(
+        arrayToRecord([
+          policyData("policy1", "org1", PolicyType.ActivateAutofill, true),
+          policyData("policy2", "org1", PolicyType.DisablePersonalVaultExport, false),
+        ]),
+      );
+
+      const result = await firstValueFrom(
+        policyService
+          .policiesByType$(PolicyType.DisablePersonalVaultExport, userId)
+          .pipe(getFirstPolicy),
+      );
+
+      expect(result).toBeUndefined();
+    });
+
+    it("does not return policies that do not apply to the user because the user's role is exempt", async () => {
+      singleUserState.nextState(
+        arrayToRecord([
+          policyData("policy1", "org1", PolicyType.ActivateAutofill, true),
+          policyData("policy2", "org2", PolicyType.DisablePersonalVaultExport, false),
+        ]),
+      );
+
+      const result = await firstValueFrom(
+        policyService
+          .policiesByType$(PolicyType.DisablePersonalVaultExport, userId)
+          .pipe(getFirstPolicy),
+      );
+      expect(result).toBeUndefined();
+    });
+
+    it.each([
+      ["owners", "org2"],
+      ["administrators", "org6"],
+    ])("returns the password generator policy for %s", async (_, organization) => {
+      singleUserState.nextState(
+        arrayToRecord([
+          policyData("policy1", "org1", PolicyType.ActivateAutofill, false),
+          policyData("policy2", organization, PolicyType.PasswordGenerator, true),
+        ]),
+      );
+
+      const result = await firstValueFrom(
+        policyService.policiesByType$(PolicyType.PasswordGenerator, userId).pipe(getFirstPolicy),
+      );
+
+      expect(result).toBeTruthy();
+    });
+
+    it("does not return policies for organizations that do not use policies", async () => {
+      singleUserState.nextState(
+        arrayToRecord([
+          policyData("policy1", "org3", PolicyType.ActivateAutofill, true),
+          policyData("policy2", "org2", PolicyType.DisablePersonalVaultExport, true),
+        ]),
+      );
+
+      const result = await firstValueFrom(
+        policyService.policiesByType$(PolicyType.ActivateAutofill, userId).pipe(getFirstPolicy),
+      );
+
+      expect(result).toBeUndefined();
+    });
+  });
+
+  describe("policies$", () => {
+    it("returns all policies when none are disabled", async () => {
+      singleUserState.nextState(
+        arrayToRecord([
+          policyData("policy1", "org4", PolicyType.DisablePersonalVaultExport, true),
+          policyData("policy2", "org1", PolicyType.ActivateAutofill, true),
+          policyData("policy3", "org5", PolicyType.DisablePersonalVaultExport, true),
+          policyData("policy4", "org1", PolicyType.DisablePersonalVaultExport, true),
+        ]),
+      );
+
+      const result = await firstValueFrom(policyService.policies$(userId));
+
+      expect(result).toEqual([
+        {
+          id: "policy1",
+          organizationId: "org4",
+          type: PolicyType.DisablePersonalVaultExport,
+          enabled: true,
+        },
+        {
+          id: "policy2",
+          organizationId: "org1",
+          type: PolicyType.ActivateAutofill,
+          enabled: true,
+        },
+        {
+          id: "policy3",
+          organizationId: "org5",
+          type: PolicyType.DisablePersonalVaultExport,
+          enabled: true,
+        },
+        {
+          id: "policy4",
+          organizationId: "org1",
+          type: PolicyType.DisablePersonalVaultExport,
+          enabled: true,
+        },
+      ]);
+    });
+
+    it("returns all policies when some are disabled", async () => {
+      singleUserState.nextState(
+        arrayToRecord([
+          policyData("policy1", "org4", PolicyType.DisablePersonalVaultExport, true),
+          policyData("policy2", "org1", PolicyType.ActivateAutofill, true),
+          policyData("policy3", "org5", PolicyType.DisablePersonalVaultExport, false), // disabled
+          policyData("policy4", "org1", PolicyType.DisablePersonalVaultExport, true),
+        ]),
+      );
+
+      const result = await firstValueFrom(policyService.policies$(userId));
+
+      expect(result).toEqual([
+        {
+          id: "policy1",
+          organizationId: "org4",
+          type: PolicyType.DisablePersonalVaultExport,
+          enabled: true,
+        },
+        {
+          id: "policy2",
+          organizationId: "org1",
+          type: PolicyType.ActivateAutofill,
+          enabled: true,
+        },
+        {
+          id: "policy3",
+          organizationId: "org5",
+          type: PolicyType.DisablePersonalVaultExport,
+          enabled: false,
+        },
+        {
+          id: "policy4",
+          organizationId: "org1",
+          type: PolicyType.DisablePersonalVaultExport,
+          enabled: true,
+        },
+      ]);
+    });
+
+    it("returns policies that do not apply to the user because the user's role is exempt", async () => {
+      singleUserState.nextState(
+        arrayToRecord([
+          policyData("policy1", "org4", PolicyType.DisablePersonalVaultExport, true),
+          policyData("policy2", "org1", PolicyType.ActivateAutofill, true),
+          policyData("policy3", "org5", PolicyType.DisablePersonalVaultExport, true),
+          policyData("policy4", "org2", PolicyType.DisablePersonalVaultExport, true), // owner
+        ]),
+      );
+
+      const result = await firstValueFrom(policyService.policies$(userId));
+
+      expect(result).toEqual([
+        {
+          id: "policy1",
+          organizationId: "org4",
+          type: PolicyType.DisablePersonalVaultExport,
+          enabled: true,
+        },
+        {
+          id: "policy2",
+          organizationId: "org1",
+          type: PolicyType.ActivateAutofill,
+          enabled: true,
+        },
+        {
+          id: "policy3",
+          organizationId: "org5",
+          type: PolicyType.DisablePersonalVaultExport,
+          enabled: true,
+        },
+        {
+          id: "policy4",
+          organizationId: "org2",
+          type: PolicyType.DisablePersonalVaultExport,
+          enabled: true,
+        },
+      ]);
+    });
+
+    it("does not return policies for organizations that do not use policies", async () => {
+      singleUserState.nextState(
+        arrayToRecord([
+          policyData("policy1", "org4", PolicyType.DisablePersonalVaultExport, true),
+          policyData("policy2", "org1", PolicyType.ActivateAutofill, true),
+          policyData("policy3", "org3", PolicyType.DisablePersonalVaultExport, true), // does not use policies
+          policyData("policy4", "org1", PolicyType.DisablePersonalVaultExport, true),
+        ]),
+      );
+
+      const result = await firstValueFrom(policyService.policies$(userId));
+
+      expect(result).toEqual([
+        {
+          id: "policy1",
+          organizationId: "org4",
+          type: PolicyType.DisablePersonalVaultExport,
+          enabled: true,
+        },
+        {
+          id: "policy2",
+          organizationId: "org1",
+          type: PolicyType.ActivateAutofill,
+          enabled: true,
+        },
+        {
+          id: "policy3",
+          organizationId: "org3",
+          type: PolicyType.DisablePersonalVaultExport,
+          enabled: true,
+        },
+        {
+          id: "policy4",
+          organizationId: "org1",
+          type: PolicyType.DisablePersonalVaultExport,
+          enabled: true,
+        },
+      ]);
+    });
+  });
+
+  describe("policyAppliesToUser$", () => {
+    it("returns true when the policyType applies to the user", async () => {
+      singleUserState.nextState(
+        arrayToRecord([
+          policyData("policy1", "org4", PolicyType.DisablePersonalVaultExport, true),
+          policyData("policy2", "org1", PolicyType.ActivateAutofill, true),
+          policyData("policy3", "org5", PolicyType.DisablePersonalVaultExport, true),
+          policyData("policy4", "org1", PolicyType.DisablePersonalVaultExport, true),
+        ]),
+      );
+
+      const result = await firstValueFrom(
+        policyService.policyAppliesToUser$(PolicyType.DisablePersonalVaultExport, userId),
+      );
+
+      expect(result).toBe(true);
+    });
+
+    it("returns false when policyType is disabled", async () => {
+      singleUserState.nextState(
+        arrayToRecord([
+          policyData("policy2", "org1", PolicyType.ActivateAutofill, true),
+          policyData("policy3", "org5", PolicyType.DisablePersonalVaultExport, false), // disabled
+        ]),
+      );
+
+      const result = await firstValueFrom(
+        policyService.policyAppliesToUser$(PolicyType.DisablePersonalVaultExport, userId),
+      );
+
+      expect(result).toBe(false);
+    });
+
+    it("returns false when the policyType does not apply to the user because the user's role is exempt", async () => {
+      singleUserState.nextState(
+        arrayToRecord([
+          policyData("policy2", "org1", PolicyType.ActivateAutofill, true),
+          policyData("policy4", "org2", PolicyType.DisablePersonalVaultExport, true), // owner
+        ]),
+      );
+
+      const result = await firstValueFrom(
+        policyService.policyAppliesToUser$(PolicyType.DisablePersonalVaultExport, userId),
+      );
+
+      expect(result).toBe(false);
+    });
+
+    it("returns false for organizations that do not use policies", async () => {
+      singleUserState.nextState(
+        arrayToRecord([
+          policyData("policy2", "org1", PolicyType.ActivateAutofill, true),
+          policyData("policy3", "org3", PolicyType.DisablePersonalVaultExport, true), // does not use policies
+        ]),
+      );
+
+      const result = await firstValueFrom(
+        policyService.policyAppliesToUser$(PolicyType.DisablePersonalVaultExport, userId),
+      );
+
+      expect(result).toBe(false);
+    });
+  });
+
+  function policyData(
+    id: string,
+    organizationId: string,
+    type: PolicyType,
+    enabled: boolean,
+    data?: any,
+  ) {
+    const policyData = new PolicyData({} as any);
+    policyData.id = id as PolicyId;
+    policyData.organizationId = organizationId;
+    policyData.type = type;
+    policyData.enabled = enabled;
+    policyData.data = data;
+
+    return policyData;
+  }
+
+  function organizationData(
+    id: string,
+    enabled: boolean,
+    usePolicies: boolean,
+    status: OrganizationUserStatusType,
+    managePolicies: boolean,
+    type: OrganizationUserType = OrganizationUserType.User,
+  ) {
+    const organizationData = new OrganizationData({} as any, {} as any);
+    organizationData.id = id;
+    organizationData.enabled = enabled;
+    organizationData.usePolicies = usePolicies;
+    organizationData.status = status;
+    organizationData.permissions = new PermissionsApi({ managePolicies: managePolicies } as any);
+    organizationData.type = type;
+    return organizationData;
+  }
+
+  function organization(
+    id: string,
+    enabled: boolean,
+    usePolicies: boolean,
+    status: OrganizationUserStatusType,
+    managePolicies: boolean,
+    type: OrganizationUserType = OrganizationUserType.User,
+  ) {
+    return new Organization(
+      organizationData(id, enabled, usePolicies, status, managePolicies, type),
+    );
+  }
+
+  function arrayToRecord(input: PolicyData[]): Record<PolicyId, PolicyData> {
+    return Object.fromEntries(input.map((i) => [i.id, i]));
+  }
+});
diff --git a/libs/common/src/admin-console/services/policy/default-vnext-policy.service.ts b/libs/common/src/admin-console/services/policy/default-vnext-policy.service.ts
new file mode 100644
index 00000000000..bc56638a987
--- /dev/null
+++ b/libs/common/src/admin-console/services/policy/default-vnext-policy.service.ts
@@ -0,0 +1,240 @@
+import { combineLatest, map, Observable, of } from "rxjs";
+
+import { StateProvider } from "../../../platform/state";
+import { UserId } from "../../../types/guid";
+import { OrganizationService } from "../../abstractions/organization/organization.service.abstraction";
+import { vNextPolicyService } from "../../abstractions/policy/vnext-policy.service";
+import { OrganizationUserStatusType, PolicyType } from "../../enums";
+import { PolicyData } from "../../models/data/policy.data";
+import { MasterPasswordPolicyOptions } from "../../models/domain/master-password-policy-options";
+import { Organization } from "../../models/domain/organization";
+import { Policy } from "../../models/domain/policy";
+import { ResetPasswordPolicyOptions } from "../../models/domain/reset-password-policy-options";
+
+import { POLICIES } from "./vnext-policy-state";
+
+export function policyRecordToArray(policiesMap: { [id: string]: PolicyData }) {
+  return Object.values(policiesMap || {}).map((f) => new Policy(f));
+}
+
+export const getFirstPolicy = map<Policy[], Policy | undefined>((policies) => {
+  return policies.at(0) ?? undefined;
+});
+
+export class DefaultvNextPolicyService implements vNextPolicyService {
+  constructor(
+    private stateProvider: StateProvider,
+    private organizationService: OrganizationService,
+  ) {}
+
+  private policyState(userId: UserId) {
+    return this.stateProvider.getUser(userId, POLICIES);
+  }
+
+  private policyData$(userId: UserId) {
+    return this.policyState(userId).state$.pipe(map((policyData) => policyData ?? {}));
+  }
+
+  policies$(userId: UserId) {
+    return this.policyData$(userId).pipe(map((policyData) => policyRecordToArray(policyData)));
+  }
+
+  policiesByType$(policyType: PolicyType, userId: UserId) {
+    const filteredPolicies$ = this.policies$(userId).pipe(
+      map((policies) => policies.filter((p) => p.type === policyType)),
+    );
+
+    if (!userId) {
+      throw new Error("No userId provided");
+    }
+
+    const organizations$ = this.organizationService.organizations$(userId);
+
+    return combineLatest([filteredPolicies$, organizations$]).pipe(
+      map(([policies, organizations]) => this.enforcedPolicyFilter(policies, organizations)),
+    );
+  }
+
+  policyAppliesToUser$(policyType: PolicyType, userId: UserId) {
+    return this.policiesByType$(policyType, userId).pipe(
+      getFirstPolicy,
+      map((policy) => !!policy),
+    );
+  }
+
+  private enforcedPolicyFilter(policies: Policy[], organizations: Organization[]) {
+    const orgDict = Object.fromEntries(organizations.map((o) => [o.id, o]));
+    return policies.filter((policy) => {
+      const organization = orgDict[policy.organizationId];
+
+      // This shouldn't happen, i.e. the user should only have policies for orgs they are a member of
+      // But if it does, err on the side of enforcing the policy
+      if (!organization) {
+        return true;
+      }
+
+      return (
+        policy.enabled &&
+        organization.status >= OrganizationUserStatusType.Accepted &&
+        organization.usePolicies &&
+        !this.isExemptFromPolicy(policy.type, organization)
+      );
+    });
+  }
+
+  masterPasswordPolicyOptions$(
+    userId: UserId,
+    policies?: Policy[],
+  ): Observable<MasterPasswordPolicyOptions | undefined> {
+    const policies$ = policies ? of(policies) : this.policies$(userId);
+    return policies$.pipe(
+      map((obsPolicies) => {
+        const enforcedOptions: MasterPasswordPolicyOptions = new MasterPasswordPolicyOptions();
+        const filteredPolicies =
+          obsPolicies.filter((p) => p.type === PolicyType.MasterPassword) ?? [];
+
+        if (filteredPolicies.length === 0) {
+          return;
+        }
+
+        filteredPolicies.forEach((currentPolicy) => {
+          if (!currentPolicy.enabled || !currentPolicy.data) {
+            return;
+          }
+
+          if (
+            currentPolicy.data.minComplexity != null &&
+            currentPolicy.data.minComplexity > enforcedOptions.minComplexity
+          ) {
+            enforcedOptions.minComplexity = currentPolicy.data.minComplexity;
+          }
+
+          if (
+            currentPolicy.data.minLength != null &&
+            currentPolicy.data.minLength > enforcedOptions.minLength
+          ) {
+            enforcedOptions.minLength = currentPolicy.data.minLength;
+          }
+
+          if (currentPolicy.data.requireUpper) {
+            enforcedOptions.requireUpper = true;
+          }
+
+          if (currentPolicy.data.requireLower) {
+            enforcedOptions.requireLower = true;
+          }
+
+          if (currentPolicy.data.requireNumbers) {
+            enforcedOptions.requireNumbers = true;
+          }
+
+          if (currentPolicy.data.requireSpecial) {
+            enforcedOptions.requireSpecial = true;
+          }
+
+          if (currentPolicy.data.enforceOnLogin) {
+            enforcedOptions.enforceOnLogin = true;
+          }
+        });
+
+        return enforcedOptions;
+      }),
+    );
+  }
+
+  evaluateMasterPassword(
+    passwordStrength: number,
+    newPassword: string,
+    enforcedPolicyOptions?: MasterPasswordPolicyOptions,
+  ): boolean {
+    if (!enforcedPolicyOptions) {
+      return true;
+    }
+
+    if (
+      enforcedPolicyOptions.minComplexity > 0 &&
+      enforcedPolicyOptions.minComplexity > passwordStrength
+    ) {
+      return false;
+    }
+
+    if (
+      enforcedPolicyOptions.minLength > 0 &&
+      enforcedPolicyOptions.minLength > newPassword.length
+    ) {
+      return false;
+    }
+
+    if (enforcedPolicyOptions.requireUpper && newPassword.toLocaleLowerCase() === newPassword) {
+      return false;
+    }
+
+    if (enforcedPolicyOptions.requireLower && newPassword.toLocaleUpperCase() === newPassword) {
+      return false;
+    }
+
+    if (enforcedPolicyOptions.requireNumbers && !/[0-9]/.test(newPassword)) {
+      return false;
+    }
+
+    // eslint-disable-next-line
+    if (enforcedPolicyOptions.requireSpecial && !/[!@#$%\^&*]/g.test(newPassword)) {
+      return false;
+    }
+
+    return true;
+  }
+
+  getResetPasswordPolicyOptions(
+    policies: Policy[],
+    orgId: string,
+  ): [ResetPasswordPolicyOptions, boolean] {
+    const resetPasswordPolicyOptions = new ResetPasswordPolicyOptions();
+
+    if (!policies || !orgId) {
+      return [resetPasswordPolicyOptions, false];
+    }
+
+    const policy = policies.find(
+      (p) => p.organizationId === orgId && p.type === PolicyType.ResetPassword && p.enabled,
+    );
+    resetPasswordPolicyOptions.autoEnrollEnabled = policy?.data?.autoEnrollEnabled ?? false;
+
+    return [resetPasswordPolicyOptions, policy?.enabled ?? false];
+  }
+
+  async upsert(policy: PolicyData, userId: UserId): Promise<void> {
+    await this.policyState(userId).update((policies) => {
+      policies ??= {};
+      policies[policy.id] = policy;
+      return policies;
+    });
+  }
+
+  async replace(policies: { [id: string]: PolicyData }, userId: UserId): Promise<void> {
+    await this.stateProvider.setUserState(POLICIES, policies, userId);
+  }
+
+  /**
+   * Determines whether an orgUser is exempt from a specific policy because of their role
+   * Generally orgUsers who can manage policies are exempt from them, but some policies are stricter
+   */
+  private isExemptFromPolicy(policyType: PolicyType, organization: Organization) {
+    switch (policyType) {
+      case PolicyType.MaximumVaultTimeout:
+        // Max Vault Timeout applies to everyone except owners
+        return organization.isOwner;
+      case PolicyType.PasswordGenerator:
+        // password generation policy applies to everyone
+        return false;
+      case PolicyType.PersonalOwnership:
+        // individual vault policy applies to everyone except admins and owners
+        return organization.isAdmin;
+      case PolicyType.FreeFamiliesSponsorshipPolicy:
+        // free Bitwarden families policy applies to everyone
+        return false;
+      default:
+        return organization.canManagePolicies;
+    }
+  }
+}
diff --git a/libs/common/src/admin-console/services/policy/vnext-policy-state.ts b/libs/common/src/admin-console/services/policy/vnext-policy-state.ts
new file mode 100644
index 00000000000..7f53e781ad3
--- /dev/null
+++ b/libs/common/src/admin-console/services/policy/vnext-policy-state.ts
@@ -0,0 +1,8 @@
+import { POLICIES_DISK, UserKeyDefinition } from "../../../platform/state";
+import { PolicyId } from "../../../types/guid";
+import { PolicyData } from "../../models/data/policy.data";
+
+export const POLICIES = UserKeyDefinition.record<PolicyData, PolicyId>(POLICIES_DISK, "policies", {
+  deserializer: (policyData) => policyData,
+  clearOn: ["logout"],
+});