@simple-auth-kit/cli 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 +88 -0
- package/bin/simple-auth-kit.mjs +18 -0
- package/lib/copy.ts +190 -0
- package/lib/rename-native.ts +173 -0
- package/package.json +31 -0
- package/registry/README.md +312 -0
- package/registry/admin-apps/README.md +71 -0
- package/registry/admin-apps/nextjs/shared/components.json +25 -0
- package/registry/admin-apps/nextjs/shared/next.config.ts +9 -0
- package/registry/admin-apps/nextjs/shared/postcss.config.mjs +7 -0
- package/registry/admin-apps/nextjs/shared/src/app/globals.css +159 -0
- package/registry/admin-apps/nextjs/shared/src/app/signup/page.tsx +72 -0
- package/registry/admin-apps/nextjs/shared/src/components/alert-modal.tsx +32 -0
- package/registry/admin-apps/nextjs/shared/src/components/breadcrumb.tsx +48 -0
- package/registry/admin-apps/nextjs/shared/src/components/form-error-alert.tsx +24 -0
- package/registry/admin-apps/nextjs/shared/src/components/loader/skeleton.tsx +39 -0
- package/registry/admin-apps/nextjs/shared/src/components/loader/table-skeleton-loader.tsx +57 -0
- package/registry/admin-apps/nextjs/shared/src/components/permission-group-select.tsx +101 -0
- package/registry/admin-apps/nextjs/shared/src/components/permission-group-view.tsx +53 -0
- package/registry/admin-apps/nextjs/shared/src/components/role-multi-select.tsx +71 -0
- package/registry/admin-apps/nextjs/shared/src/components/theme-toggle.tsx +26 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/alert.tsx +78 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/avatar.tsx +53 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/badge.tsx +51 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/breadcrumb.tsx +109 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/button.tsx +67 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/card.tsx +103 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/checkbox.tsx +32 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/collapsible.tsx +33 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/data-table.tsx +107 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/dialog.tsx +143 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/dropdown-menu.tsx +234 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/heading.tsx +16 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/input.tsx +19 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/label.tsx +24 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/modal.tsx +29 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/select.tsx +192 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/separator.tsx +28 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/sheet.tsx +135 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/sidebar.tsx +567 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/skeleton.tsx +13 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/sonner.tsx +34 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/table.tsx +116 -0
- package/registry/admin-apps/nextjs/shared/src/components/ui/tooltip.tsx +61 -0
- package/registry/admin-apps/nextjs/shared/src/hooks/use-live-audit-feed.ts +51 -0
- package/registry/admin-apps/nextjs/shared/src/hooks/use-mobile.ts +17 -0
- package/registry/admin-apps/nextjs/shared/src/lib/error.ts +23 -0
- package/registry/admin-apps/nextjs/shared/src/lib/query-client.tsx +10 -0
- package/registry/admin-apps/nextjs/shared/src/lib/token-storage.ts +36 -0
- package/registry/admin-apps/nextjs/shared/src/lib/utils.ts +6 -0
- package/registry/admin-apps/nextjs/shared/tsconfig.json +41 -0
- package/registry/admin-apps/nextjs/variants/base/.env.example +9 -0
- package/registry/admin-apps/nextjs/variants/base/Dockerfile +32 -0
- package/registry/admin-apps/nextjs/variants/base/package.json +58 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/account/change-password/page.tsx +163 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/account/page.tsx +569 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/audit-log/page.tsx +180 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/countries/[id]/edit/page.tsx +357 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/countries/[id]/page.tsx +191 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/countries/country-schema.ts +18 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/countries/new/page.tsx +305 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/countries/page.tsx +367 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/customers/[id]/edit/page.tsx +386 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/customers/[id]/page.tsx +213 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/customers/customer-schema.ts +35 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/customers/new/page.tsx +334 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/customers/page.tsx +400 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/dashboard/page.tsx +263 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/[id]/edit/page.tsx +275 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/[id]/page.tsx +183 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/language-schema.ts +20 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/new/page.tsx +226 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/page.tsx +342 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/layout.tsx +50 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/permissions/[id]/edit/page.tsx +309 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/permissions/[id]/page.tsx +119 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/permissions/new/page.tsx +275 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/permissions/page.tsx +311 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/permissions/permission-schema.ts +44 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/roles/[id]/edit/page.tsx +276 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/roles/[id]/page.tsx +179 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/roles/new/page.tsx +241 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/roles/page.tsx +325 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/roles/role-schema.ts +20 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/users/[id]/edit/page.tsx +411 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/users/[id]/page.tsx +253 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/users/new/page.tsx +444 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/users/page.tsx +410 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/users/user-schema.ts +37 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/api/auth/[...nextauth]/route.ts +3 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/icon.svg +5 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/layout.tsx +31 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/login/page.tsx +162 -0
- package/registry/admin-apps/nextjs/variants/base/src/app/page.tsx +22 -0
- package/registry/admin-apps/nextjs/variants/base/src/auth.ts +105 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/app-sidebar.tsx +182 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/nav-user.tsx +99 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/permission-required.tsx +32 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/photo-upload.tsx +203 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/accordion.tsx +66 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/alert-dialog.tsx +157 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/button-group.tsx +83 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/calendar.tsx +218 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/carousel.tsx +241 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/combobox.tsx +243 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/command.tsx +184 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/context-menu.tsx +252 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/date-picker.tsx +323 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/datetime-picker.tsx +896 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/drawer.tsx +135 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/empty.tsx +104 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/field.tsx +248 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/form.tsx +168 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/hover-card.tsx +44 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/input-group.tsx +170 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/input-otp.tsx +77 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/item.tsx +193 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/kbd.tsx +28 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/menubar.tsx +276 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/multi-select.tsx +381 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/multi-selector.tsx +641 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/native-select.tsx +48 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/navigation-menu.tsx +168 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/pagination.tsx +127 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/popover.tsx +48 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/progress.tsx +31 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/radio-group.tsx +45 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/resizable.tsx +55 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/scroll-area.tsx +58 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/spinner.tsx +27 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/switch.tsx +31 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/tabs.tsx +66 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/textarea.tsx +18 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/toggle-group.tsx +83 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/ui/toggle.tsx +47 -0
- package/registry/admin-apps/nextjs/variants/base/src/components/user-menu.tsx +61 -0
- package/registry/admin-apps/nextjs/variants/base/src/lib/ability.ts +90 -0
- package/registry/admin-apps/nextjs/variants/base/src/lib/auth-client.ts +10 -0
- package/registry/admin-apps/nextjs/variants/base/src/lib/env.ts +10 -0
- package/registry/admin-apps/nextjs/variants/base/src/lib/server-token-storage.ts +33 -0
- package/registry/admin-apps/nextjs/variants/base/src/lib/stores/auth-store.ts +117 -0
- package/registry/admin-apps/nextjs/variants/base/src/lib/stores/store-context.tsx +50 -0
- package/registry/admin-apps/nextjs/variants/base/src/proxy.ts +81 -0
- package/registry/admin-apps/nextjs/variants/base/src/routes.ts +7 -0
- package/registry/admin-apps/nextjs/variants/workspaces/.env.example +7 -0
- package/registry/admin-apps/nextjs/variants/workspaces/Dockerfile +32 -0
- package/registry/admin-apps/nextjs/variants/workspaces/package.json +48 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/account/page.tsx +360 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/audit-log/page.tsx +185 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/countries/[id]/edit/page.tsx +296 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/countries/[id]/page.tsx +194 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/countries/country-schema.ts +27 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/countries/new/page.tsx +242 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/countries/page.tsx +373 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/customers/[id]/edit/page.tsx +306 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/customers/[id]/page.tsx +219 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/customers/customer-schema.ts +36 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/customers/new/page.tsx +251 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/customers/page.tsx +406 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/dashboard/page.tsx +146 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/languages/[id]/edit/page.tsx +240 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/languages/[id]/page.tsx +186 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/languages/language-schema.ts +26 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/languages/new/page.tsx +189 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/languages/page.tsx +348 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/layout.tsx +96 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/members/page.tsx +263 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/permissions/[id]/edit/page.tsx +264 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/permissions/[id]/page.tsx +126 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/permissions/new/page.tsx +238 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/permissions/page.tsx +294 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/permissions/permission-schema.ts +46 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/roles/[id]/edit/page.tsx +248 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/roles/[id]/page.tsx +186 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/roles/new/page.tsx +205 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/roles/page.tsx +485 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/roles/role-schema.ts +23 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/users/[id]/page.tsx +521 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/users/new/page.tsx +342 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/users/page.tsx +414 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/users/user-schema.ts +44 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/workspaces/new/page.tsx +66 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/layout.tsx +31 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/login/page.tsx +110 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/page.tsx +33 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/app-sidebar.tsx +180 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/change-password-dialog.tsx +119 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/nav-user.tsx +105 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/permission-required.tsx +32 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/photo-upload.tsx +226 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/ui/calendar.tsx +218 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/ui/datetime-picker.tsx +896 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/ui/popover.tsx +48 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/user-menu.tsx +67 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/components/workspace-switcher.tsx +57 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/lib/ability.ts +101 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/lib/active-workspace.ts +38 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/lib/auth-client.ts +17 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/lib/env.ts +4 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/lib/stores/auth-store.ts +139 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/lib/stores/store-context.tsx +66 -0
- package/registry/admin-apps/nextjs/variants/workspaces/src/lib/stores/workspace-store.ts +89 -0
- package/registry/admin-apps/react/shared/components.json +25 -0
- package/registry/admin-apps/react/shared/postcss.config.js +5 -0
- package/registry/admin-apps/react/shared/src/components/alert-modal.tsx +30 -0
- package/registry/admin-apps/react/shared/src/components/loader/skeleton.tsx +39 -0
- package/registry/admin-apps/react/shared/src/components/loader/table-skeleton-loader.tsx +57 -0
- package/registry/admin-apps/react/shared/src/components/nav-user.tsx +103 -0
- package/registry/admin-apps/react/shared/src/components/role-multi-select.tsx +69 -0
- package/registry/admin-apps/react/shared/src/components/theme-toggle.tsx +24 -0
- package/registry/admin-apps/react/shared/src/components/ui/accordion.tsx +66 -0
- package/registry/admin-apps/react/shared/src/components/ui/alert-dialog.tsx +157 -0
- package/registry/admin-apps/react/shared/src/components/ui/alert.tsx +78 -0
- package/registry/admin-apps/react/shared/src/components/ui/avatar.tsx +53 -0
- package/registry/admin-apps/react/shared/src/components/ui/badge.tsx +51 -0
- package/registry/admin-apps/react/shared/src/components/ui/breadcrumb.tsx +109 -0
- package/registry/admin-apps/react/shared/src/components/ui/button-group.tsx +83 -0
- package/registry/admin-apps/react/shared/src/components/ui/button.tsx +67 -0
- package/registry/admin-apps/react/shared/src/components/ui/calendar.tsx +218 -0
- package/registry/admin-apps/react/shared/src/components/ui/card.tsx +103 -0
- package/registry/admin-apps/react/shared/src/components/ui/carousel.tsx +241 -0
- package/registry/admin-apps/react/shared/src/components/ui/checkbox.tsx +32 -0
- package/registry/admin-apps/react/shared/src/components/ui/collapsible.tsx +33 -0
- package/registry/admin-apps/react/shared/src/components/ui/context-menu.tsx +252 -0
- package/registry/admin-apps/react/shared/src/components/ui/data-table.tsx +107 -0
- package/registry/admin-apps/react/shared/src/components/ui/datetime-picker.tsx +896 -0
- package/registry/admin-apps/react/shared/src/components/ui/dialog.tsx +143 -0
- package/registry/admin-apps/react/shared/src/components/ui/drawer.tsx +135 -0
- package/registry/admin-apps/react/shared/src/components/ui/dropdown-menu.tsx +234 -0
- package/registry/admin-apps/react/shared/src/components/ui/empty.tsx +104 -0
- package/registry/admin-apps/react/shared/src/components/ui/field.tsx +248 -0
- package/registry/admin-apps/react/shared/src/components/ui/form.tsx +168 -0
- package/registry/admin-apps/react/shared/src/components/ui/heading.tsx +16 -0
- package/registry/admin-apps/react/shared/src/components/ui/hover-card.tsx +44 -0
- package/registry/admin-apps/react/shared/src/components/ui/input-group.tsx +170 -0
- package/registry/admin-apps/react/shared/src/components/ui/input-otp.tsx +77 -0
- package/registry/admin-apps/react/shared/src/components/ui/input.tsx +19 -0
- package/registry/admin-apps/react/shared/src/components/ui/item.tsx +193 -0
- package/registry/admin-apps/react/shared/src/components/ui/kbd.tsx +28 -0
- package/registry/admin-apps/react/shared/src/components/ui/label.tsx +24 -0
- package/registry/admin-apps/react/shared/src/components/ui/menubar.tsx +276 -0
- package/registry/admin-apps/react/shared/src/components/ui/modal.tsx +29 -0
- package/registry/admin-apps/react/shared/src/components/ui/multi-select.tsx +381 -0
- package/registry/admin-apps/react/shared/src/components/ui/multi-selector.tsx +641 -0
- package/registry/admin-apps/react/shared/src/components/ui/native-select.tsx +48 -0
- package/registry/admin-apps/react/shared/src/components/ui/navigation-menu.tsx +168 -0
- package/registry/admin-apps/react/shared/src/components/ui/pagination.tsx +127 -0
- package/registry/admin-apps/react/shared/src/components/ui/popover.tsx +48 -0
- package/registry/admin-apps/react/shared/src/components/ui/progress.tsx +31 -0
- package/registry/admin-apps/react/shared/src/components/ui/radio-group.tsx +45 -0
- package/registry/admin-apps/react/shared/src/components/ui/resizable.tsx +55 -0
- package/registry/admin-apps/react/shared/src/components/ui/scroll-area.tsx +58 -0
- package/registry/admin-apps/react/shared/src/components/ui/select.tsx +192 -0
- package/registry/admin-apps/react/shared/src/components/ui/separator.tsx +28 -0
- package/registry/admin-apps/react/shared/src/components/ui/sheet.tsx +135 -0
- package/registry/admin-apps/react/shared/src/components/ui/sidebar.tsx +567 -0
- package/registry/admin-apps/react/shared/src/components/ui/skeleton.tsx +13 -0
- package/registry/admin-apps/react/shared/src/components/ui/spinner.tsx +27 -0
- package/registry/admin-apps/react/shared/src/components/ui/switch.tsx +31 -0
- package/registry/admin-apps/react/shared/src/components/ui/table.tsx +116 -0
- package/registry/admin-apps/react/shared/src/components/ui/tabs.tsx +66 -0
- package/registry/admin-apps/react/shared/src/components/ui/textarea.tsx +18 -0
- package/registry/admin-apps/react/shared/src/components/ui/toggle-group.tsx +83 -0
- package/registry/admin-apps/react/shared/src/components/ui/toggle.tsx +47 -0
- package/registry/admin-apps/react/shared/src/components/ui/tooltip.tsx +61 -0
- package/registry/admin-apps/react/shared/src/hooks/use-mobile.ts +17 -0
- package/registry/admin-apps/react/shared/src/lib/cn.ts +6 -0
- package/registry/admin-apps/react/shared/src/lib/error.ts +23 -0
- package/registry/admin-apps/react/shared/src/lib/query-client.tsx +8 -0
- package/registry/admin-apps/react/shared/src/lib/utils.ts +1 -0
- package/registry/admin-apps/react/shared/src/vite-env.d.ts +9 -0
- package/registry/admin-apps/react/shared/tsconfig.json +24 -0
- package/registry/admin-apps/react/variants/base/.env.example +3 -0
- package/registry/admin-apps/react/variants/base/Dockerfile +32 -0
- package/registry/admin-apps/react/variants/base/index.html +12 -0
- package/registry/admin-apps/react/variants/base/package.json +61 -0
- package/registry/admin-apps/react/variants/base/src/App.tsx +103 -0
- package/registry/admin-apps/react/variants/base/src/components/app-sidebar.tsx +181 -0
- package/registry/admin-apps/react/variants/base/src/components/breadcrumb.tsx +47 -0
- package/registry/admin-apps/react/variants/base/src/components/change-password-dialog.tsx +102 -0
- package/registry/admin-apps/react/variants/base/src/components/form-error-alert.tsx +23 -0
- package/registry/admin-apps/react/variants/base/src/components/layout/AppShell.tsx +36 -0
- package/registry/admin-apps/react/variants/base/src/components/layout/RequireAuth.tsx +66 -0
- package/registry/admin-apps/react/variants/base/src/components/permission-group-select.tsx +99 -0
- package/registry/admin-apps/react/variants/base/src/components/permission-group-view.tsx +53 -0
- package/registry/admin-apps/react/variants/base/src/components/photo-upload.tsx +221 -0
- package/registry/admin-apps/react/variants/base/src/components/ui/combobox.tsx +243 -0
- package/registry/admin-apps/react/variants/base/src/components/ui/command.tsx +177 -0
- package/registry/admin-apps/react/variants/base/src/components/ui/date-picker.tsx +322 -0
- package/registry/admin-apps/react/variants/base/src/components/ui/sonner.tsx +34 -0
- package/registry/admin-apps/react/variants/base/src/components/user-menu.tsx +65 -0
- package/registry/admin-apps/react/variants/base/src/hooks/use-live-audit-feed.ts +50 -0
- package/registry/admin-apps/react/variants/base/src/index.css +161 -0
- package/registry/admin-apps/react/variants/base/src/lib/ability.ts +72 -0
- package/registry/admin-apps/react/variants/base/src/lib/auth-client.ts +9 -0
- package/registry/admin-apps/react/variants/base/src/lib/theme.tsx +64 -0
- package/registry/admin-apps/react/variants/base/src/lib/token-storage.ts +34 -0
- package/registry/admin-apps/react/variants/base/src/main.tsx +24 -0
- package/registry/admin-apps/react/variants/base/src/pages/AccountPage.tsx +368 -0
- package/registry/admin-apps/react/variants/base/src/pages/AddCountryPage.tsx +227 -0
- package/registry/admin-apps/react/variants/base/src/pages/AddCustomerPage.tsx +220 -0
- package/registry/admin-apps/react/variants/base/src/pages/AddLanguagePage.tsx +167 -0
- package/registry/admin-apps/react/variants/base/src/pages/AddRolePage.tsx +187 -0
- package/registry/admin-apps/react/variants/base/src/pages/AddUserPage.tsx +328 -0
- package/registry/admin-apps/react/variants/base/src/pages/AuditLogPage.tsx +180 -0
- package/registry/admin-apps/react/variants/base/src/pages/CountriesPage.tsx +371 -0
- package/registry/admin-apps/react/variants/base/src/pages/CountryDetailPage.tsx +209 -0
- package/registry/admin-apps/react/variants/base/src/pages/CustomerDetailPage.tsx +231 -0
- package/registry/admin-apps/react/variants/base/src/pages/CustomersPage.tsx +404 -0
- package/registry/admin-apps/react/variants/base/src/pages/DashboardPage.tsx +190 -0
- package/registry/admin-apps/react/variants/base/src/pages/EditCountryPage.tsx +279 -0
- package/registry/admin-apps/react/variants/base/src/pages/EditCustomerPage.tsx +272 -0
- package/registry/admin-apps/react/variants/base/src/pages/EditLanguagePage.tsx +216 -0
- package/registry/admin-apps/react/variants/base/src/pages/EditRolePage.tsx +232 -0
- package/registry/admin-apps/react/variants/base/src/pages/EditUserPage.tsx +308 -0
- package/registry/admin-apps/react/variants/base/src/pages/LanguageDetailPage.tsx +201 -0
- package/registry/admin-apps/react/variants/base/src/pages/LanguagesPage.tsx +346 -0
- package/registry/admin-apps/react/variants/base/src/pages/LoginPage.tsx +149 -0
- package/registry/admin-apps/react/variants/base/src/pages/PermissionDetailPage.tsx +135 -0
- package/registry/admin-apps/react/variants/base/src/pages/PermissionsPage.tsx +331 -0
- package/registry/admin-apps/react/variants/base/src/pages/RoleDetailPage.tsx +167 -0
- package/registry/admin-apps/react/variants/base/src/pages/RolesPage.tsx +613 -0
- package/registry/admin-apps/react/variants/base/src/pages/UserDetailPage.tsx +268 -0
- package/registry/admin-apps/react/variants/base/src/pages/UsersPage.tsx +423 -0
- package/registry/admin-apps/react/variants/base/src/pages/country-schema.ts +29 -0
- package/registry/admin-apps/react/variants/base/src/pages/customer-schema.ts +37 -0
- package/registry/admin-apps/react/variants/base/src/pages/language-schema.ts +28 -0
- package/registry/admin-apps/react/variants/base/src/pages/user-schema.ts +36 -0
- package/registry/admin-apps/react/variants/base/src/stores/auth-store.ts +144 -0
- package/registry/admin-apps/react/variants/base/src/stores/store-context.tsx +15 -0
- package/registry/admin-apps/react/variants/base/vite.config.ts +15 -0
- package/registry/admin-apps/react/variants/workspaces/.env.example +5 -0
- package/registry/admin-apps/react/variants/workspaces/Dockerfile +32 -0
- package/registry/admin-apps/react/variants/workspaces/index.html +12 -0
- package/registry/admin-apps/react/variants/workspaces/package.json +61 -0
- package/registry/admin-apps/react/variants/workspaces/src/App.tsx +118 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/app-sidebar.tsx +186 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/breadcrumb.tsx +39 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/change-password-dialog.tsx +111 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/form-error-alert.tsx +23 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/layout/AppShell.tsx +37 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/layout/CreateWorkspaceDialog.tsx +66 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/layout/RequireAuth.tsx +107 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/layout/WorkspaceSwitcher.tsx +52 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/permission-group-select.tsx +99 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/permission-group-view.tsx +53 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/photo-upload.tsx +223 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/ui/combobox.tsx +243 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/ui/command.tsx +184 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/ui/date-picker.tsx +323 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/ui/sonner.tsx +34 -0
- package/registry/admin-apps/react/variants/workspaces/src/components/user-menu.tsx +66 -0
- package/registry/admin-apps/react/variants/workspaces/src/hooks/use-live-audit-feed.ts +50 -0
- package/registry/admin-apps/react/variants/workspaces/src/index.css +162 -0
- package/registry/admin-apps/react/variants/workspaces/src/lib/ability.ts +78 -0
- package/registry/admin-apps/react/variants/workspaces/src/lib/auth-client.ts +27 -0
- package/registry/admin-apps/react/variants/workspaces/src/lib/permission-groups.ts +22 -0
- package/registry/admin-apps/react/variants/workspaces/src/lib/theme.tsx +64 -0
- package/registry/admin-apps/react/variants/workspaces/src/lib/token-storage.ts +37 -0
- package/registry/admin-apps/react/variants/workspaces/src/lib/user-fields.ts +13 -0
- package/registry/admin-apps/react/variants/workspaces/src/main.tsx +24 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/AccountPage.tsx +351 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/AddCountryPage.tsx +230 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/AddCustomerPage.tsx +224 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/AddLanguagePage.tsx +170 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/AddUserPage.tsx +297 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/AuditLogPage.tsx +195 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/CountriesPage.tsx +378 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/CountryDetailPage.tsx +215 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/CustomerDetailPage.tsx +237 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/CustomersPage.tsx +411 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/DashboardPage.tsx +191 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/EditCountryPage.tsx +285 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/EditCustomerPage.tsx +279 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/EditLanguagePage.tsx +222 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/LanguageDetailPage.tsx +207 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/LanguagesPage.tsx +353 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/LoginPage.tsx +149 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/MembersPage.tsx +349 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/PermissionDetailPage.tsx +123 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/PermissionsPage.tsx +317 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/RoleDetailPage.tsx +207 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/RolesPage.tsx +617 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/UserDetailPage.tsx +405 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/UsersPage.tsx +425 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/country-schema.ts +29 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/customer-schema.ts +34 -0
- package/registry/admin-apps/react/variants/workspaces/src/pages/language-schema.ts +27 -0
- package/registry/admin-apps/react/variants/workspaces/src/stores/auth-store.ts +169 -0
- package/registry/admin-apps/react/variants/workspaces/src/stores/store-context.tsx +43 -0
- package/registry/admin-apps/react/variants/workspaces/src/stores/workspace-store.ts +120 -0
- package/registry/admin-apps/react/variants/workspaces/vite.config.ts +15 -0
- package/registry/combos/express-drizzle/package.json +32 -0
- package/registry/combos/express-drizzle/scripts/materialize.mjs +104 -0
- package/registry/combos/express-drizzle/scripts/migrate.mjs +41 -0
- package/registry/combos/express-drizzle/scripts/prove-cycle.mjs +29 -0
- package/registry/combos/express-drizzle/scripts/seed.mjs +25 -0
- package/registry/combos/express-drizzle/shared/drizzle.config.ts +67 -0
- package/registry/combos/express-drizzle/shared/src/ability.ts +44 -0
- package/registry/combos/express-drizzle/shared/src/auth-core-error.middleware.ts +31 -0
- package/registry/combos/express-drizzle/shared/src/auth.config.ts +63 -0
- package/registry/combos/express-drizzle/shared/src/auth.middleware.ts +38 -0
- package/registry/combos/express-drizzle/shared/src/auth.router.ts +266 -0
- package/registry/combos/express-drizzle/shared/src/db.ts +12 -0
- package/registry/combos/express-drizzle/shared/src/http-error.ts +18 -0
- package/registry/combos/express-drizzle/shared/src/id.helper.ts +22 -0
- package/registry/combos/express-drizzle/shared/src/key-provider.ts +52 -0
- package/registry/combos/express-drizzle/shared/src/oauth.repository.ts +47 -0
- package/registry/combos/express-drizzle/shared/src/openapi-fragment.ts +49 -0
- package/registry/combos/express-drizzle/shared/src/openapi-spec.ts +493 -0
- package/registry/combos/express-drizzle/shared/src/pagination.ts +30 -0
- package/registry/combos/express-drizzle/shared/src/password-reset.repository.ts +35 -0
- package/registry/combos/express-drizzle/shared/src/permission-cache.ts +191 -0
- package/registry/combos/express-drizzle/shared/src/rate-limit.store.ts +24 -0
- package/registry/combos/express-drizzle/shared/src/request-context.ts +35 -0
- package/registry/combos/express-drizzle/shared/src/response-envelope.middleware.ts +46 -0
- package/registry/combos/express-drizzle/shared/src/route-tiers.ts +148 -0
- package/registry/combos/express-drizzle/shared/src/session.repository.ts +130 -0
- package/registry/combos/express-drizzle/shared/src/two-factor.repository.ts +38 -0
- package/registry/combos/express-drizzle/test/bootstrap.ts +40 -0
- package/registry/combos/express-drizzle/test/harness.ts +187 -0
- package/registry/combos/express-drizzle/test/prove-cycle.ts +789 -0
- package/registry/combos/express-drizzle/tsconfig.json +20 -0
- package/registry/combos/express-drizzle/variants/base/.env.example +25 -0
- package/registry/combos/express-drizzle/variants/base/drizzle/0000_init.sql +219 -0
- package/registry/combos/express-drizzle/variants/base/drizzle/0001_add_is_active_field.sql +1 -0
- package/registry/combos/express-drizzle/variants/base/drizzle/meta/0000_snapshot.json +1861 -0
- package/registry/combos/express-drizzle/variants/base/drizzle/meta/0001_snapshot.json +1868 -0
- package/registry/combos/express-drizzle/variants/base/drizzle/meta/_journal.json +20 -0
- package/registry/combos/express-drizzle/variants/base/src/admin.router.ts +307 -0
- package/registry/combos/express-drizzle/variants/base/src/audit-log.repository.ts +93 -0
- package/registry/combos/express-drizzle/variants/base/src/auth.service.ts +528 -0
- package/registry/combos/express-drizzle/variants/base/src/authz.middleware.ts +58 -0
- package/registry/combos/express-drizzle/variants/base/src/create-auth-app.ts +78 -0
- package/registry/combos/express-drizzle/variants/base/src/openapi-admin.ts +565 -0
- package/registry/combos/express-drizzle/variants/base/src/rbac.defaults.ts +218 -0
- package/registry/combos/express-drizzle/variants/base/src/rbac.repository.ts +606 -0
- package/registry/combos/express-drizzle/variants/base/src/schema.ts +425 -0
- package/registry/combos/express-drizzle/variants/base/src/seed.ts +138 -0
- package/registry/combos/express-drizzle/variants/base/test/variant-hooks.ts +179 -0
- package/registry/combos/express-drizzle/variants/workspaces/.env.example +30 -0
- package/registry/combos/express-drizzle/variants/workspaces/drizzle/0000_init.sql +251 -0
- package/registry/combos/express-drizzle/variants/workspaces/drizzle/0001_add_is_active_field.sql +1 -0
- package/registry/combos/express-drizzle/variants/workspaces/drizzle/meta/0000_snapshot.json +2155 -0
- package/registry/combos/express-drizzle/variants/workspaces/drizzle/meta/0001_snapshot.json +2162 -0
- package/registry/combos/express-drizzle/variants/workspaces/drizzle/meta/_journal.json +20 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/admin.router.ts +323 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/audit-log.repository.ts +105 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/auth.service.ts +545 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/authz.middleware.ts +94 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/create-auth-app.ts +87 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/openapi-admin.ts +596 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/openapi-workspace.ts +183 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/rbac.defaults.ts +248 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/rbac.repository.ts +698 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/schema.ts +487 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/seed.ts +173 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/workspace.repository.ts +254 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/workspace.router.ts +123 -0
- package/registry/combos/express-drizzle/variants/workspaces/test/variant-hooks.ts +370 -0
- package/registry/combos/express-prisma/package.json +32 -0
- package/registry/combos/express-prisma/scripts/materialize.mjs +84 -0
- package/registry/combos/express-prisma/scripts/migrate.mjs +32 -0
- package/registry/combos/express-prisma/scripts/prove-cycle.mjs +30 -0
- package/registry/combos/express-prisma/scripts/seed.mjs +24 -0
- package/registry/combos/express-prisma/shared/prisma.config.ts +38 -0
- package/registry/combos/express-prisma/shared/src/ability.ts +44 -0
- package/registry/combos/express-prisma/shared/src/auth-core-error.middleware.ts +31 -0
- package/registry/combos/express-prisma/shared/src/auth.config.ts +63 -0
- package/registry/combos/express-prisma/shared/src/auth.middleware.ts +44 -0
- package/registry/combos/express-prisma/shared/src/auth.router.ts +269 -0
- package/registry/combos/express-prisma/shared/src/http-error.ts +18 -0
- package/registry/combos/express-prisma/shared/src/id.helper.ts +22 -0
- package/registry/combos/express-prisma/shared/src/key-provider.ts +52 -0
- package/registry/combos/express-prisma/shared/src/oauth.repository.ts +33 -0
- package/registry/combos/express-prisma/shared/src/openapi-fragment.ts +49 -0
- package/registry/combos/express-prisma/shared/src/openapi-spec.ts +487 -0
- package/registry/combos/express-prisma/shared/src/pagination.ts +30 -0
- package/registry/combos/express-prisma/shared/src/password-reset.repository.ts +29 -0
- package/registry/combos/express-prisma/shared/src/permission-cache.ts +191 -0
- package/registry/combos/express-prisma/shared/src/rate-limit.store.ts +21 -0
- package/registry/combos/express-prisma/shared/src/request-context.ts +35 -0
- package/registry/combos/express-prisma/shared/src/response-envelope.middleware.ts +46 -0
- package/registry/combos/express-prisma/shared/src/route-tiers.ts +148 -0
- package/registry/combos/express-prisma/shared/src/session.repository.ts +140 -0
- package/registry/combos/express-prisma/shared/src/two-factor.repository.ts +30 -0
- package/registry/combos/express-prisma/test/bootstrap.ts +45 -0
- package/registry/combos/express-prisma/test/harness.ts +187 -0
- package/registry/combos/express-prisma/test/prove-cycle.ts +793 -0
- package/registry/combos/express-prisma/tsconfig.json +21 -0
- package/registry/combos/express-prisma/variants/base/.env.example +25 -0
- package/registry/combos/express-prisma/variants/base/prisma/migrations/20260810165456_init/migration.sql +370 -0
- package/registry/combos/express-prisma/variants/base/prisma/migrations/20260811151048_add_is_active_field/migration.sql +2 -0
- package/registry/combos/express-prisma/variants/base/prisma/migrations/migration_lock.toml +3 -0
- package/registry/combos/express-prisma/variants/base/prisma/schema/simple-auth-kit.prisma +332 -0
- package/registry/combos/express-prisma/variants/base/src/admin.router.ts +314 -0
- package/registry/combos/express-prisma/variants/base/src/audit-log.repository.ts +91 -0
- package/registry/combos/express-prisma/variants/base/src/auth.service.ts +521 -0
- package/registry/combos/express-prisma/variants/base/src/authz.middleware.ts +58 -0
- package/registry/combos/express-prisma/variants/base/src/create-auth-app.ts +93 -0
- package/registry/combos/express-prisma/variants/base/src/openapi-admin.ts +569 -0
- package/registry/combos/express-prisma/variants/base/src/rbac.defaults.ts +195 -0
- package/registry/combos/express-prisma/variants/base/src/rbac.repository.ts +484 -0
- package/registry/combos/express-prisma/variants/base/src/seed.ts +115 -0
- package/registry/combos/express-prisma/variants/base/test/variant-hooks.ts +159 -0
- package/registry/combos/express-prisma/variants/workspaces/.env.example +30 -0
- package/registry/combos/express-prisma/variants/workspaces/prisma/migrations/20260810165502_init/migration.sql +443 -0
- package/registry/combos/express-prisma/variants/workspaces/prisma/migrations/20260811151051_add_is_active_field/migration.sql +2 -0
- package/registry/combos/express-prisma/variants/workspaces/prisma/migrations/migration_lock.toml +3 -0
- package/registry/combos/express-prisma/variants/workspaces/prisma/schema/simple-auth-kit.prisma +394 -0
- package/registry/combos/express-prisma/variants/workspaces/src/admin.router.ts +330 -0
- package/registry/combos/express-prisma/variants/workspaces/src/audit-log.repository.ts +102 -0
- package/registry/combos/express-prisma/variants/workspaces/src/auth.service.ts +542 -0
- package/registry/combos/express-prisma/variants/workspaces/src/authz.middleware.ts +94 -0
- package/registry/combos/express-prisma/variants/workspaces/src/create-auth-app.ts +99 -0
- package/registry/combos/express-prisma/variants/workspaces/src/openapi-admin.ts +601 -0
- package/registry/combos/express-prisma/variants/workspaces/src/openapi-workspace.ts +183 -0
- package/registry/combos/express-prisma/variants/workspaces/src/rbac.defaults.ts +222 -0
- package/registry/combos/express-prisma/variants/workspaces/src/rbac.repository.ts +527 -0
- package/registry/combos/express-prisma/variants/workspaces/src/seed.ts +147 -0
- package/registry/combos/express-prisma/variants/workspaces/src/workspace.repository.ts +188 -0
- package/registry/combos/express-prisma/variants/workspaces/src/workspace.router.ts +123 -0
- package/registry/combos/express-prisma/variants/workspaces/test/variant-hooks.ts +358 -0
- package/registry/combos/nestjs-drizzle/package.json +36 -0
- package/registry/combos/nestjs-drizzle/scripts/materialize.mjs +104 -0
- package/registry/combos/nestjs-drizzle/scripts/migrate.mjs +34 -0
- package/registry/combos/nestjs-drizzle/scripts/prove-cycle.mjs +29 -0
- package/registry/combos/nestjs-drizzle/scripts/seed.mjs +24 -0
- package/registry/combos/nestjs-drizzle/shared/drizzle.config.ts +67 -0
- package/registry/combos/nestjs-drizzle/shared/src/ability.guard.ts +46 -0
- package/registry/combos/nestjs-drizzle/shared/src/ability.ts +44 -0
- package/registry/combos/nestjs-drizzle/shared/src/auth-core-error.filter.ts +32 -0
- package/registry/combos/nestjs-drizzle/shared/src/auth.config.ts +65 -0
- package/registry/combos/nestjs-drizzle/shared/src/auth.controller.ts +299 -0
- package/registry/combos/nestjs-drizzle/shared/src/auth.guard.ts +34 -0
- package/registry/combos/nestjs-drizzle/shared/src/db.ts +12 -0
- package/registry/combos/nestjs-drizzle/shared/src/dto/auth.dto.ts +429 -0
- package/registry/combos/nestjs-drizzle/shared/src/id.helper.ts +22 -0
- package/registry/combos/nestjs-drizzle/shared/src/key-provider.ts +51 -0
- package/registry/combos/nestjs-drizzle/shared/src/oauth.repository.ts +36 -0
- package/registry/combos/nestjs-drizzle/shared/src/pagination.ts +30 -0
- package/registry/combos/nestjs-drizzle/shared/src/password-reset.repository.ts +29 -0
- package/registry/combos/nestjs-drizzle/shared/src/permission-cache.ts +196 -0
- package/registry/combos/nestjs-drizzle/shared/src/rate-limit.store.ts +26 -0
- package/registry/combos/nestjs-drizzle/shared/src/request-context.ts +25 -0
- package/registry/combos/nestjs-drizzle/shared/src/response.interceptor.ts +48 -0
- package/registry/combos/nestjs-drizzle/shared/src/route-tiers.ts +144 -0
- package/registry/combos/nestjs-drizzle/shared/src/session.repository.ts +132 -0
- package/registry/combos/nestjs-drizzle/shared/src/two-factor.repository.ts +35 -0
- package/registry/combos/nestjs-drizzle/test/bootstrap.ts +49 -0
- package/registry/combos/nestjs-drizzle/test/harness.ts +187 -0
- package/registry/combos/nestjs-drizzle/test/prove-cycle.ts +795 -0
- package/registry/combos/nestjs-drizzle/tsconfig.json +20 -0
- package/registry/combos/nestjs-drizzle/variants/base/.env.example +25 -0
- package/registry/combos/nestjs-drizzle/variants/base/drizzle/0000_init.sql +219 -0
- package/registry/combos/nestjs-drizzle/variants/base/drizzle/0001_add_is_active_field.sql +1 -0
- package/registry/combos/nestjs-drizzle/variants/base/drizzle/0002_colossal_true_believers.sql +3 -0
- package/registry/combos/nestjs-drizzle/variants/base/drizzle/meta/0000_snapshot.json +1861 -0
- package/registry/combos/nestjs-drizzle/variants/base/drizzle/meta/0001_snapshot.json +1868 -0
- package/registry/combos/nestjs-drizzle/variants/base/drizzle/meta/0002_snapshot.json +1887 -0
- package/registry/combos/nestjs-drizzle/variants/base/drizzle/meta/_journal.json +27 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/admin.controller.ts +361 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/audit-log.repository.ts +90 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/auth.module.ts +83 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/auth.service.ts +544 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/authz.guard.ts +53 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/dto/admin.dto.ts +130 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/rbac.defaults.ts +218 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/rbac.repository.ts +633 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/schema.ts +427 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/seed.ts +138 -0
- package/registry/combos/nestjs-drizzle/variants/base/test/variant-hooks.ts +179 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/.env.example +30 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/0000_init.sql +251 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/0001_add_is_active_field.sql +1 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/0002_calm_tyger_tiger.sql +3 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/meta/0000_snapshot.json +2155 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/meta/0001_snapshot.json +2162 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/meta/0002_snapshot.json +2181 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/meta/_journal.json +27 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/admin.controller.ts +383 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/audit-log.repository.ts +102 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.module.ts +87 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.service.ts +553 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/authz.guard.ts +87 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/dto/admin.dto.ts +137 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/dto/workspace.dto.ts +60 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/rbac.defaults.ts +248 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/rbac.repository.ts +719 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/schema.ts +489 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/seed.ts +174 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/workspace.controller.ts +123 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/workspace.repository.ts +255 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/test/variant-hooks.ts +370 -0
- package/registry/combos/nestjs-prisma/package.json +41 -0
- package/registry/combos/nestjs-prisma/scripts/materialize.mjs +84 -0
- package/registry/combos/nestjs-prisma/scripts/migrate.mjs +32 -0
- package/registry/combos/nestjs-prisma/scripts/prove-cycle.mjs +30 -0
- package/registry/combos/nestjs-prisma/scripts/seed.mjs +24 -0
- package/registry/combos/nestjs-prisma/shared/prisma.config.ts +38 -0
- package/registry/combos/nestjs-prisma/shared/src/ability.guard.ts +29 -0
- package/registry/combos/nestjs-prisma/shared/src/ability.ts +23 -0
- package/registry/combos/nestjs-prisma/shared/src/auth-core-error.filter.ts +32 -0
- package/registry/combos/nestjs-prisma/shared/src/auth.config.ts +71 -0
- package/registry/combos/nestjs-prisma/shared/src/auth.controller.ts +303 -0
- package/registry/combos/nestjs-prisma/shared/src/auth.guard.ts +31 -0
- package/registry/combos/nestjs-prisma/shared/src/dto/auth.dto.ts +438 -0
- package/registry/combos/nestjs-prisma/shared/src/id.helper.ts +22 -0
- package/registry/combos/nestjs-prisma/shared/src/key-provider.ts +42 -0
- package/registry/combos/nestjs-prisma/shared/src/oauth.repository.ts +32 -0
- package/registry/combos/nestjs-prisma/shared/src/pagination.ts +30 -0
- package/registry/combos/nestjs-prisma/shared/src/password-reset.repository.ts +29 -0
- package/registry/combos/nestjs-prisma/shared/src/permission-cache.ts +140 -0
- package/registry/combos/nestjs-prisma/shared/src/rate-limit.store.ts +26 -0
- package/registry/combos/nestjs-prisma/shared/src/request-context.ts +19 -0
- package/registry/combos/nestjs-prisma/shared/src/response.interceptor.ts +48 -0
- package/registry/combos/nestjs-prisma/shared/src/route-tiers.ts +102 -0
- package/registry/combos/nestjs-prisma/shared/src/session.repository.ts +129 -0
- package/registry/combos/nestjs-prisma/shared/src/two-factor.repository.ts +29 -0
- package/registry/combos/nestjs-prisma/test/bootstrap.ts +54 -0
- package/registry/combos/nestjs-prisma/test/harness.ts +186 -0
- package/registry/combos/nestjs-prisma/test/prove-cycle.ts +795 -0
- package/registry/combos/nestjs-prisma/tsconfig.json +21 -0
- package/registry/combos/nestjs-prisma/variants/base/.env.example +31 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260810084937_init/migration.sql +269 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260810122546_add_profile_audit_soft_delete/migration.sql +96 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260810123620_add_display_name/migration.sql +3 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260810131421_drop_signing_keys/migration.sql +3 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260810161515_bigint_ids/migration.sql +305 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260810162120_drop_name_unique/migration.sql +6 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260811145755_add_is_active_field/migration.sql +2 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260811183529_add_countries_languages_customers/migration.sql +146 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260812121128_add_user_profile_details/migration.sql +4 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/migration_lock.toml +3 -0
- package/registry/combos/nestjs-prisma/variants/base/prisma/schema/simple-auth-kit.prisma +437 -0
- package/registry/combos/nestjs-prisma/variants/base/src/admin.controller.ts +684 -0
- package/registry/combos/nestjs-prisma/variants/base/src/audit-log.gateway.ts +51 -0
- package/registry/combos/nestjs-prisma/variants/base/src/audit-log.repository.ts +111 -0
- package/registry/combos/nestjs-prisma/variants/base/src/auth.module.ts +87 -0
- package/registry/combos/nestjs-prisma/variants/base/src/auth.service.ts +618 -0
- package/registry/combos/nestjs-prisma/variants/base/src/authz.guard.ts +34 -0
- package/registry/combos/nestjs-prisma/variants/base/src/country.repository.ts +149 -0
- package/registry/combos/nestjs-prisma/variants/base/src/customer.repository.ts +169 -0
- package/registry/combos/nestjs-prisma/variants/base/src/docs.ts +60 -0
- package/registry/combos/nestjs-prisma/variants/base/src/dto/admin.dto.ts +130 -0
- package/registry/combos/nestjs-prisma/variants/base/src/dto/country.dto.ts +120 -0
- package/registry/combos/nestjs-prisma/variants/base/src/dto/customer.dto.ts +147 -0
- package/registry/combos/nestjs-prisma/variants/base/src/dto/language.dto.ts +93 -0
- package/registry/combos/nestjs-prisma/variants/base/src/language.repository.ts +135 -0
- package/registry/combos/nestjs-prisma/variants/base/src/rbac.defaults.ts +193 -0
- package/registry/combos/nestjs-prisma/variants/base/src/rbac.repository.ts +514 -0
- package/registry/combos/nestjs-prisma/variants/base/src/seed.ts +89 -0
- package/registry/combos/nestjs-prisma/variants/base/test/variant-hooks.ts +135 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/.env.example +30 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810084938_init/migration.sql +308 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810123450_add_profile_audit_soft_delete/migration.sql +96 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810123632_add_display_name/migration.sql +3 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810124243_workspace_audit_soft_delete/migration.sql +25 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810131432_drop_signing_keys/migration.sql +3 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810161551_bigint_ids/migration.sql +393 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810162131_drop_name_unique/migration.sql +3 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260811145805_add_is_active_field/migration.sql +2 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260813120000_add_countries_languages_customers/migration.sql +158 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/migration_lock.toml +3 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/prisma/schema/simple-auth-kit.prisma +521 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/admin.controller.ts +733 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/audit-log.repository.ts +98 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.module.ts +83 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.service.ts +509 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/authz.guard.ts +69 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/country.repository.ts +158 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/customer.repository.ts +179 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/dto/admin.dto.ts +128 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/dto/country.dto.ts +120 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/dto/customer.dto.ts +147 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/dto/language.dto.ts +93 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/dto/workspace.dto.ts +60 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/language.repository.ts +143 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/rbac.defaults.ts +211 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/rbac.repository.ts +505 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/seed.ts +122 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/workspace.controller.ts +111 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/workspace.repository.ts +177 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/test/variant-hooks.ts +333 -0
- package/registry/core/crypto.ts +43 -0
- package/registry/core/oauth.ts +159 -0
- package/registry/core/package.json +20 -0
- package/registry/core/password-reset.ts +42 -0
- package/registry/core/rate-limit.ts +33 -0
- package/registry/core/rbac.ts +4 -0
- package/registry/core/session-policy.ts +96 -0
- package/registry/core/test/crypto.test.ts +25 -0
- package/registry/core/test/oauth.test.ts +213 -0
- package/registry/core/test/password-reset.test.ts +77 -0
- package/registry/core/test/rate-limit.test.ts +41 -0
- package/registry/core/test/rbac.test.ts +20 -0
- package/registry/core/test/session-policy.test.ts +209 -0
- package/registry/core/test/test-keys.ts +6 -0
- package/registry/core/test/token-service.test.ts +81 -0
- package/registry/core/test/two-factor.test.ts +72 -0
- package/registry/core/token-service.ts +143 -0
- package/registry/core/tsconfig.json +5 -0
- package/registry/core/two-factor.ts +108 -0
- package/registry/core/types.ts +127 -0
- package/registry/mobile-apps/README.md +65 -0
- package/registry/mobile-apps/bare-rn/shared/.bundle/config +2 -0
- package/registry/mobile-apps/bare-rn/shared/.eslintrc.js +4 -0
- package/registry/mobile-apps/bare-rn/shared/.prettierrc.js +5 -0
- package/registry/mobile-apps/bare-rn/shared/.watchmanconfig +1 -0
- package/registry/mobile-apps/bare-rn/shared/App.tsx +28 -0
- package/registry/mobile-apps/bare-rn/shared/Gemfile +17 -0
- package/registry/mobile-apps/bare-rn/shared/README.md +97 -0
- package/registry/mobile-apps/bare-rn/shared/__mocks__/reactNativeConfigMock.js +3 -0
- package/registry/mobile-apps/bare-rn/shared/__mocks__/styleMock.js +1 -0
- package/registry/mobile-apps/bare-rn/shared/__tests__/App.test.tsx +13 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/build.gradle +121 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/debug.keystore +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/proguard-rules.pro +10 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/AndroidManifest.xml +27 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/java/com/mobilebarern/MainActivity.kt +22 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/java/com/mobilebarern/MainApplication.kt +27 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/values/strings.xml +3 -0
- package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/values/styles.xml +9 -0
- package/registry/mobile-apps/bare-rn/shared/android/build.gradle +21 -0
- package/registry/mobile-apps/bare-rn/shared/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/registry/mobile-apps/bare-rn/shared/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/registry/mobile-apps/bare-rn/shared/android/gradle.properties +44 -0
- package/registry/mobile-apps/bare-rn/shared/android/gradlew +248 -0
- package/registry/mobile-apps/bare-rn/shared/android/gradlew.bat +98 -0
- package/registry/mobile-apps/bare-rn/shared/android/settings.gradle +6 -0
- package/registry/mobile-apps/bare-rn/shared/app.json +4 -0
- package/registry/mobile-apps/bare-rn/shared/babel.config.js +6 -0
- package/registry/mobile-apps/bare-rn/shared/global.css +2 -0
- package/registry/mobile-apps/bare-rn/shared/index.js +10 -0
- package/registry/mobile-apps/bare-rn/shared/ios/.xcode.env +11 -0
- package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn/AppDelegate.swift +48 -0
- package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
- package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn/Images.xcassets/Contents.json +6 -0
- package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn/Info.plist +59 -0
- package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn/LaunchScreen.storyboard +47 -0
- package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn/PrivacyInfo.xcprivacy +37 -0
- package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn.xcodeproj/project.pbxproj +475 -0
- package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn.xcodeproj/xcshareddata/xcschemes/MobileBareRn.xcscheme +88 -0
- package/registry/mobile-apps/bare-rn/shared/ios/Podfile +34 -0
- package/registry/mobile-apps/bare-rn/shared/jest.config.js +17 -0
- package/registry/mobile-apps/bare-rn/shared/metro.config.js +55 -0
- package/registry/mobile-apps/bare-rn/shared/nativewind-env.d.ts +1 -0
- package/registry/mobile-apps/bare-rn/shared/src/api/tokenStorage.ts +29 -0
- package/registry/mobile-apps/bare-rn/shared/src/components/FormInput.tsx +21 -0
- package/registry/mobile-apps/bare-rn/shared/src/components/PrimaryButton.tsx +47 -0
- package/registry/mobile-apps/bare-rn/shared/src/screens/LoginScreen.tsx +58 -0
- package/registry/mobile-apps/bare-rn/shared/src/screens/SessionsScreen.tsx +77 -0
- package/registry/mobile-apps/bare-rn/shared/src/screens/SignupScreen.tsx +54 -0
- package/registry/mobile-apps/bare-rn/shared/src/screens/TwoFactorScreen.tsx +59 -0
- package/registry/mobile-apps/bare-rn/shared/tailwind.config.js +9 -0
- package/registry/mobile-apps/bare-rn/shared/tsconfig.json +17 -0
- package/registry/mobile-apps/bare-rn/variants/base/package.json +58 -0
- package/registry/mobile-apps/bare-rn/variants/base/src/api/authClient.ts +14 -0
- package/registry/mobile-apps/bare-rn/variants/base/src/navigation/RootNavigator.tsx +58 -0
- package/registry/mobile-apps/bare-rn/variants/base/src/navigation/types.ts +15 -0
- package/registry/mobile-apps/bare-rn/variants/base/src/screens/HomeScreen.tsx +64 -0
- package/registry/mobile-apps/bare-rn/variants/base/src/store/authStore.ts +143 -0
- package/registry/mobile-apps/bare-rn/variants/workspaces/package.json +58 -0
- package/registry/mobile-apps/bare-rn/variants/workspaces/src/api/authClient.ts +22 -0
- package/registry/mobile-apps/bare-rn/variants/workspaces/src/navigation/RootNavigator.tsx +72 -0
- package/registry/mobile-apps/bare-rn/variants/workspaces/src/navigation/types.ts +18 -0
- package/registry/mobile-apps/bare-rn/variants/workspaces/src/screens/HomeScreen.tsx +85 -0
- package/registry/mobile-apps/bare-rn/variants/workspaces/src/screens/WorkspaceSelectScreen.tsx +165 -0
- package/registry/mobile-apps/bare-rn/variants/workspaces/src/store/authStore.ts +177 -0
- package/registry/mobile-apps/bare-rn/variants/workspaces/src/store/workspaceStore.ts +90 -0
- package/registry/mobile-apps/bare-rn/variants/workspaces/src/workspace/activeWorkspace.ts +55 -0
- package/registry/mobile-apps/expo/shared/App.tsx +15 -0
- package/registry/mobile-apps/expo/shared/assets/android-icon-background.png +0 -0
- package/registry/mobile-apps/expo/shared/assets/android-icon-foreground.png +0 -0
- package/registry/mobile-apps/expo/shared/assets/android-icon-monochrome.png +0 -0
- package/registry/mobile-apps/expo/shared/assets/favicon.png +0 -0
- package/registry/mobile-apps/expo/shared/assets/icon.png +0 -0
- package/registry/mobile-apps/expo/shared/assets/splash-icon.png +0 -0
- package/registry/mobile-apps/expo/shared/babel.config.js +6 -0
- package/registry/mobile-apps/expo/shared/global.css +2 -0
- package/registry/mobile-apps/expo/shared/index.ts +8 -0
- package/registry/mobile-apps/expo/shared/metro.config.js +6 -0
- package/registry/mobile-apps/expo/shared/nativewind-env.d.ts +3 -0
- package/registry/mobile-apps/expo/shared/postcss.config.mjs +5 -0
- package/registry/mobile-apps/expo/shared/src/screens/LoginScreen.tsx +81 -0
- package/registry/mobile-apps/expo/shared/src/screens/SessionsScreen.tsx +77 -0
- package/registry/mobile-apps/expo/shared/src/screens/SignupScreen.tsx +77 -0
- package/registry/mobile-apps/expo/shared/src/screens/TwoFactorScreen.tsx +59 -0
- package/registry/mobile-apps/expo/shared/src/storage/asyncStorageTokenStorage.ts +32 -0
- package/registry/mobile-apps/expo/shared/tsconfig.json +6 -0
- package/registry/mobile-apps/expo/variants/base/app.json +25 -0
- package/registry/mobile-apps/expo/variants/base/package.json +35 -0
- package/registry/mobile-apps/expo/variants/base/src/api/authClient.ts +17 -0
- package/registry/mobile-apps/expo/variants/base/src/navigation/RootNavigator.tsx +58 -0
- package/registry/mobile-apps/expo/variants/base/src/navigation/types.ts +10 -0
- package/registry/mobile-apps/expo/variants/base/src/screens/HomeScreen.tsx +75 -0
- package/registry/mobile-apps/expo/variants/base/src/store/authStore.ts +146 -0
- package/registry/mobile-apps/expo/variants/workspaces/app.json +25 -0
- package/registry/mobile-apps/expo/variants/workspaces/package.json +35 -0
- package/registry/mobile-apps/expo/variants/workspaces/src/api/authClient.ts +21 -0
- package/registry/mobile-apps/expo/variants/workspaces/src/navigation/RootNavigator.tsx +82 -0
- package/registry/mobile-apps/expo/variants/workspaces/src/navigation/types.ts +20 -0
- package/registry/mobile-apps/expo/variants/workspaces/src/screens/HomeScreen.tsx +94 -0
- package/registry/mobile-apps/expo/variants/workspaces/src/screens/WorkspaceSelectScreen.tsx +175 -0
- package/registry/mobile-apps/expo/variants/workspaces/src/store/authStore.ts +179 -0
- package/registry/mobile-apps/expo/variants/workspaces/src/store/workspaceStore.ts +90 -0
- package/registry/mobile-apps/expo/variants/workspaces/src/workspace/activeWorkspace.ts +55 -0
- package/registry.json +263 -0
- package/simple-auth-kit.ts +715 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { hashToken } from "../crypto.js";
|
|
3
|
+
import { requestPasswordReset, resetPassword, PasswordResetStoreDeps } from "../password-reset.js";
|
|
4
|
+
import { AuditEvent, PasswordResetTokenInvalidError } from "../types.js";
|
|
5
|
+
|
|
6
|
+
interface FakeTokenRow {
|
|
7
|
+
userId: string;
|
|
8
|
+
tokenHash: string;
|
|
9
|
+
expiresAt: string;
|
|
10
|
+
consumedAt: string | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function fakeStore() {
|
|
14
|
+
const tokens = new Map<string, FakeTokenRow>();
|
|
15
|
+
const passwordHashes = new Map<string, string>();
|
|
16
|
+
const auditLog: AuditEvent[] = [];
|
|
17
|
+
|
|
18
|
+
const deps: PasswordResetStoreDeps = {
|
|
19
|
+
saveResetToken: async ({ userId, tokenHash, expiresAt }) => {
|
|
20
|
+
tokens.set(tokenHash, { userId, tokenHash, expiresAt, consumedAt: null });
|
|
21
|
+
},
|
|
22
|
+
findValidResetToken: async (tokenHash) => tokens.get(tokenHash) ?? null,
|
|
23
|
+
consumeResetToken: async (tokenHash) => {
|
|
24
|
+
const row = tokens.get(tokenHash);
|
|
25
|
+
if (row) row.consumedAt = new Date().toISOString();
|
|
26
|
+
},
|
|
27
|
+
setPasswordHash: async (userId, passwordHash) => {
|
|
28
|
+
passwordHashes.set(userId, passwordHash);
|
|
29
|
+
},
|
|
30
|
+
appendAuditEvent: async (event) => {
|
|
31
|
+
auditLog.push(event);
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return { deps, tokens, passwordHashes, auditLog };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe("password-reset", () => {
|
|
39
|
+
let store: ReturnType<typeof fakeStore>;
|
|
40
|
+
|
|
41
|
+
beforeEach(() => {
|
|
42
|
+
store = fakeStore();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("issues a reset token and records an audit event", async () => {
|
|
46
|
+
const { token } = await requestPasswordReset(store.deps, "user-1");
|
|
47
|
+
expect(token).toHaveLength(64); // 32 bytes hex
|
|
48
|
+
expect(store.tokens.get(hashToken(token))?.userId).toBe("user-1");
|
|
49
|
+
expect(store.auditLog).toContainEqual({ type: "password_reset_requested", userId: "user-1" });
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("resets the password on a valid token and consumes it", async () => {
|
|
53
|
+
const { token } = await requestPasswordReset(store.deps, "user-1");
|
|
54
|
+
const { userId } = await resetPassword(store.deps, token, "new-correct-horse-battery-staple");
|
|
55
|
+
|
|
56
|
+
expect(userId).toBe("user-1");
|
|
57
|
+
expect(store.passwordHashes.get("user-1")).toBeDefined();
|
|
58
|
+
expect(store.tokens.get(hashToken(token))?.consumedAt).not.toBeNull();
|
|
59
|
+
expect(store.auditLog).toContainEqual({ type: "password_reset_completed", userId: "user-1" });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("rejects an unknown token", async () => {
|
|
63
|
+
await expect(resetPassword(store.deps, "not-a-real-token", "new-pass")).rejects.toBeInstanceOf(PasswordResetTokenInvalidError);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("rejects a token that has already been consumed", async () => {
|
|
67
|
+
const { token } = await requestPasswordReset(store.deps, "user-1");
|
|
68
|
+
await resetPassword(store.deps, token, "first-reset-password");
|
|
69
|
+
|
|
70
|
+
await expect(resetPassword(store.deps, token, "second-reset-password")).rejects.toBeInstanceOf(PasswordResetTokenInvalidError);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("rejects an expired token", async () => {
|
|
74
|
+
const { token } = await requestPasswordReset(store.deps, "user-1", { ttlSeconds: -1 });
|
|
75
|
+
await expect(resetPassword(store.deps, token, "new-pass")).rejects.toBeInstanceOf(PasswordResetTokenInvalidError);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { checkRateLimit, RateLimitDeps } from "../rate-limit.js";
|
|
3
|
+
|
|
4
|
+
function fakeCounter(): RateLimitDeps & { counts: Map<string, number> } {
|
|
5
|
+
const counts = new Map<string, number>();
|
|
6
|
+
return {
|
|
7
|
+
counts,
|
|
8
|
+
increment: async (key) => {
|
|
9
|
+
const next = (counts.get(key) ?? 0) + 1;
|
|
10
|
+
counts.set(key, next);
|
|
11
|
+
return next;
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe("rate-limit", () => {
|
|
17
|
+
it("allows requests under the max", async () => {
|
|
18
|
+
const deps = fakeCounter();
|
|
19
|
+
for (let i = 0; i < 5; i++) {
|
|
20
|
+
const result = await checkRateLimit(deps, "login", "user-1");
|
|
21
|
+
expect(result.allowed).toBe(true);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("blocks requests over the max", async () => {
|
|
26
|
+
const deps = fakeCounter();
|
|
27
|
+
for (let i = 0; i < 5; i++) await checkRateLimit(deps, "login", "user-1");
|
|
28
|
+
const sixth = await checkRateLimit(deps, "login", "user-1");
|
|
29
|
+
expect(sixth.allowed).toBe(false);
|
|
30
|
+
expect(sixth.remaining).toBe(0);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("scopes counters independently per identifier and scope", async () => {
|
|
34
|
+
const deps = fakeCounter();
|
|
35
|
+
for (let i = 0; i < 5; i++) await checkRateLimit(deps, "login", "user-1");
|
|
36
|
+
const otherUser = await checkRateLimit(deps, "login", "user-2");
|
|
37
|
+
const otherScope = await checkRateLimit(deps, "signup", "user-1");
|
|
38
|
+
expect(otherUser.allowed).toBe(true);
|
|
39
|
+
expect(otherScope.allowed).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { resolvePermissions } from "../rbac.js";
|
|
3
|
+
|
|
4
|
+
describe("rbac: resolvePermissions", () => {
|
|
5
|
+
it("unions role-derived and direct permissions", () => {
|
|
6
|
+
expect(resolvePermissions(["users:read", "users:block"], ["billing:manage"])).toEqual([
|
|
7
|
+
"billing:manage",
|
|
8
|
+
"users:block",
|
|
9
|
+
"users:read",
|
|
10
|
+
]);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("dedupes a permission granted both by role and directly", () => {
|
|
14
|
+
expect(resolvePermissions(["users:read"], ["users:read"])).toEqual(["users:read"]);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("returns an empty array when the user has no permissions at all", () => {
|
|
18
|
+
expect(resolvePermissions([], [])).toEqual([]);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { beforeEach, describe, expect, it } from "vitest";
|
|
3
|
+
import {
|
|
4
|
+
blockUser,
|
|
5
|
+
createSession,
|
|
6
|
+
revokeAllSessionsForUser,
|
|
7
|
+
revokeOtherSessionsForUser,
|
|
8
|
+
revokeSession,
|
|
9
|
+
rotateRefreshToken,
|
|
10
|
+
SessionStoreDeps,
|
|
11
|
+
} from "../session-policy.js";
|
|
12
|
+
import { AuditEvent, RefreshInvalidError, RefreshReuseDetectedError, SessionRecord } from "../types.js";
|
|
13
|
+
|
|
14
|
+
/** What a store would read from config. Long enough that no test trips over it by accident. */
|
|
15
|
+
const SESSION_TTL_MS = 60 * 60 * 1000;
|
|
16
|
+
|
|
17
|
+
function fakeStore() {
|
|
18
|
+
const sessions = new Map<string, SessionRecord>();
|
|
19
|
+
const denylist = new Set<string>();
|
|
20
|
+
const auditLog: AuditEvent[] = [];
|
|
21
|
+
|
|
22
|
+
const deps: SessionStoreDeps = {
|
|
23
|
+
findSession: async (id) => sessions.get(id) ?? null,
|
|
24
|
+
saveSession: async (session) => {
|
|
25
|
+
sessions.set(session.id, session);
|
|
26
|
+
},
|
|
27
|
+
createSession: async ({ userId, userAgent, ip, provider }) => {
|
|
28
|
+
const session: SessionRecord = {
|
|
29
|
+
id: randomUUID(),
|
|
30
|
+
userId,
|
|
31
|
+
sessionVersion: 0,
|
|
32
|
+
currentRefreshJti: randomUUID(),
|
|
33
|
+
provider,
|
|
34
|
+
expiresAt: new Date(Date.now() + SESSION_TTL_MS).toISOString(),
|
|
35
|
+
revokedAt: null,
|
|
36
|
+
createdAt: new Date().toISOString(),
|
|
37
|
+
userAgent,
|
|
38
|
+
ip,
|
|
39
|
+
};
|
|
40
|
+
sessions.set(session.id, session);
|
|
41
|
+
return session;
|
|
42
|
+
},
|
|
43
|
+
revokeAllByUser: async (userId, revoker) => {
|
|
44
|
+
for (const session of sessions.values()) {
|
|
45
|
+
if (session.userId === userId) {
|
|
46
|
+
session.sessionVersion += 1;
|
|
47
|
+
session.revokedAt = new Date().toISOString();
|
|
48
|
+
session.revokedBy = revoker?.userId;
|
|
49
|
+
session.revokedByIp = revoker?.ip;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
revokeAllByUserExcept: async (userId, keepSessionId, revoker) => {
|
|
54
|
+
for (const session of sessions.values()) {
|
|
55
|
+
if (session.userId === userId && session.id !== keepSessionId) {
|
|
56
|
+
session.sessionVersion += 1;
|
|
57
|
+
session.revokedAt = new Date().toISOString();
|
|
58
|
+
session.revokedBy = revoker?.userId;
|
|
59
|
+
session.revokedByIp = revoker?.ip;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
denylistJti: async (jti) => {
|
|
64
|
+
denylist.add(jti);
|
|
65
|
+
},
|
|
66
|
+
isDenylisted: async (jti) => denylist.has(jti),
|
|
67
|
+
appendAuditEvent: async (event) => {
|
|
68
|
+
auditLog.push(event);
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return { deps, sessions, denylist, auditLog };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
describe("session-policy", () => {
|
|
76
|
+
let store: ReturnType<typeof fakeStore>;
|
|
77
|
+
|
|
78
|
+
beforeEach(() => {
|
|
79
|
+
store = fakeStore();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("creates a session and records an audit event", async () => {
|
|
83
|
+
const session = await createSession(store.deps, { userId: "user-1" });
|
|
84
|
+
expect(session.sessionVersion).toBe(0);
|
|
85
|
+
expect(session.revokedAt).toBeNull();
|
|
86
|
+
expect(store.auditLog).toContainEqual({ type: "session_created", sessionId: session.id, userId: "user-1" });
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("rotates a refresh token on legitimate use, issuing a new jti", async () => {
|
|
90
|
+
const session = await createSession(store.deps, { userId: "user-1" });
|
|
91
|
+
const { session: rotated, nextJti } = await rotateRefreshToken(store.deps, {
|
|
92
|
+
sub: "user-1",
|
|
93
|
+
sessionId: session.id,
|
|
94
|
+
sv: session.sessionVersion,
|
|
95
|
+
jti: session.currentRefreshJti,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
expect(nextJti).not.toBe(session.currentRefreshJti);
|
|
99
|
+
expect(rotated.currentRefreshJti).toBe(nextJti);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("detects reuse of an already-rotated refresh token and kills the whole session family", async () => {
|
|
103
|
+
const session = await createSession(store.deps, { userId: "user-1" });
|
|
104
|
+
const staleJti = session.currentRefreshJti;
|
|
105
|
+
|
|
106
|
+
await rotateRefreshToken(store.deps, { sub: "user-1", sessionId: session.id, sv: session.sessionVersion, jti: staleJti });
|
|
107
|
+
|
|
108
|
+
await expect(
|
|
109
|
+
rotateRefreshToken(store.deps, { sub: "user-1", sessionId: session.id, sv: session.sessionVersion, jti: staleJti }),
|
|
110
|
+
).rejects.toBeInstanceOf(RefreshReuseDetectedError);
|
|
111
|
+
|
|
112
|
+
const updated = await store.deps.findSession(session.id);
|
|
113
|
+
expect(updated?.revokedAt).not.toBeNull();
|
|
114
|
+
expect(store.auditLog.some((e) => e.type === "refresh_reuse_detected")).toBe(true);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("rejects a refresh token whose sv is stale (session was revoked)", async () => {
|
|
118
|
+
const session = await createSession(store.deps, { userId: "user-1" });
|
|
119
|
+
await revokeSession(store.deps, session.id);
|
|
120
|
+
|
|
121
|
+
await expect(
|
|
122
|
+
rotateRefreshToken(store.deps, {
|
|
123
|
+
sub: "user-1",
|
|
124
|
+
sessionId: session.id,
|
|
125
|
+
sv: 0, // the sv the client had before revoke bumped it
|
|
126
|
+
jti: session.currentRefreshJti,
|
|
127
|
+
}),
|
|
128
|
+
).rejects.toBeInstanceOf(RefreshInvalidError);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("revokeAllSessionsForUser kills every session for that user only", async () => {
|
|
132
|
+
const a = await createSession(store.deps, { userId: "user-1" });
|
|
133
|
+
const b = await createSession(store.deps, { userId: "user-1" });
|
|
134
|
+
const other = await createSession(store.deps, { userId: "user-2" });
|
|
135
|
+
|
|
136
|
+
await revokeAllSessionsForUser(store.deps, "user-1");
|
|
137
|
+
|
|
138
|
+
expect((await store.deps.findSession(a.id))?.revokedAt).not.toBeNull();
|
|
139
|
+
expect((await store.deps.findSession(b.id))?.revokedAt).not.toBeNull();
|
|
140
|
+
expect((await store.deps.findSession(other.id))?.revokedAt).toBeNull();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("revokeOtherSessionsForUser keeps the current session alive and kills the rest", async () => {
|
|
144
|
+
const current = await createSession(store.deps, { userId: "user-1" });
|
|
145
|
+
const other = await createSession(store.deps, { userId: "user-1" });
|
|
146
|
+
|
|
147
|
+
await revokeOtherSessionsForUser(store.deps, "user-1", current.id);
|
|
148
|
+
|
|
149
|
+
expect((await store.deps.findSession(current.id))?.revokedAt).toBeNull();
|
|
150
|
+
expect((await store.deps.findSession(other.id))?.revokedAt).not.toBeNull();
|
|
151
|
+
expect(store.auditLog.some((e) => e.type === "other_sessions_revoked")).toBe(true);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("blockUser revokes every session immediately, not just future logins", async () => {
|
|
155
|
+
const session = await createSession(store.deps, { userId: "user-1" });
|
|
156
|
+
await blockUser(store.deps, "user-1");
|
|
157
|
+
|
|
158
|
+
expect((await store.deps.findSession(session.id))?.revokedAt).not.toBeNull();
|
|
159
|
+
expect(store.auditLog.some((e) => e.type === "user_blocked")).toBe(true);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("records who revoked a session, and from where", async () => {
|
|
163
|
+
const session = await createSession(store.deps, { userId: "user-1" });
|
|
164
|
+
await revokeSession(store.deps, session.id, { userId: "user-1", ip: "10.0.0.9" });
|
|
165
|
+
|
|
166
|
+
const revoked = await store.deps.findSession(session.id);
|
|
167
|
+
expect(revoked?.revokedBy).toBe("user-1");
|
|
168
|
+
expect(revoked?.revokedByIp).toBe("10.0.0.9");
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("blockUser records the administrator as the revoker, not the blocked user", async () => {
|
|
172
|
+
const session = await createSession(store.deps, { userId: "user-1" });
|
|
173
|
+
await blockUser(store.deps, "user-1", { userId: "admin-1", ip: "10.0.0.1" });
|
|
174
|
+
|
|
175
|
+
// The distinction the column exists for: afterwards the row says an administrator ended this,
|
|
176
|
+
// not merely that it ended.
|
|
177
|
+
const revoked = await store.deps.findSession(session.id);
|
|
178
|
+
expect(revoked?.userId).toBe("user-1");
|
|
179
|
+
expect(revoked?.revokedBy).toBe("admin-1");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("a refresh-reuse kill records no revoker — nobody did it", async () => {
|
|
183
|
+
const session = await createSession(store.deps, { userId: "user-1" });
|
|
184
|
+
const staleJti = session.currentRefreshJti;
|
|
185
|
+
await rotateRefreshToken(store.deps, { sub: "user-1", sessionId: session.id, sv: session.sessionVersion, jti: staleJti });
|
|
186
|
+
|
|
187
|
+
await expect(
|
|
188
|
+
rotateRefreshToken(store.deps, { sub: "user-1", sessionId: session.id, sv: session.sessionVersion, jti: staleJti }),
|
|
189
|
+
).rejects.toBeInstanceOf(RefreshReuseDetectedError);
|
|
190
|
+
|
|
191
|
+
expect((await store.deps.findSession(session.id))?.revokedBy).toBeUndefined();
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("refuses to rotate a session past its own expiry, however valid the token is", async () => {
|
|
195
|
+
const session = await createSession(store.deps, { userId: "user-1" });
|
|
196
|
+
// Everything about the presented token stays correct — only the wall clock has moved on.
|
|
197
|
+
store.sessions.set(session.id, { ...session, expiresAt: new Date(Date.now() - 1000).toISOString() });
|
|
198
|
+
|
|
199
|
+
await expect(
|
|
200
|
+
rotateRefreshToken(store.deps, { sub: "user-1", sessionId: session.id, sv: session.sessionVersion, jti: session.currentRefreshJti }),
|
|
201
|
+
).rejects.toBeInstanceOf(RefreshInvalidError);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("carries the login provider onto the session", async () => {
|
|
205
|
+
const session = await createSession(store.deps, { userId: "user-1", provider: "google" });
|
|
206
|
+
expect(session.provider).toBe("google");
|
|
207
|
+
expect((await createSession(store.deps, { userId: "user-1" })).provider).toBeUndefined();
|
|
208
|
+
});
|
|
209
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
signAccessToken,
|
|
4
|
+
signRefreshToken,
|
|
5
|
+
signTwoFactorChallengeToken,
|
|
6
|
+
verifyAccessToken,
|
|
7
|
+
verifyRefreshToken,
|
|
8
|
+
verifyTwoFactorChallengeToken,
|
|
9
|
+
} from "../token-service.js";
|
|
10
|
+
import { AccessTokenInvalidError, RefreshInvalidError, TwoFactorChallengeInvalidError } from "../types.js";
|
|
11
|
+
import { generateTestSigningKey } from "./test-keys.js";
|
|
12
|
+
|
|
13
|
+
describe("token-service", () => {
|
|
14
|
+
it("signs and verifies an access token round-trip", async () => {
|
|
15
|
+
const key = generateTestSigningKey();
|
|
16
|
+
const { token, jti } = await signAccessToken(
|
|
17
|
+
{ activeKey: key },
|
|
18
|
+
{ sub: "user-1", sessionId: "session-1", roles: ["member"], permissions: ["users:read"] },
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const claims = await verifyAccessToken({ secret: key.secret, isDenylisted: async () => false }, token);
|
|
22
|
+
|
|
23
|
+
expect(claims).toEqual({
|
|
24
|
+
sub: "user-1",
|
|
25
|
+
sessionId: "session-1",
|
|
26
|
+
roles: ["member"],
|
|
27
|
+
permissions: ["users:read"],
|
|
28
|
+
jti,
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("rejects a denylisted access token even if signature is valid", async () => {
|
|
33
|
+
const key = generateTestSigningKey();
|
|
34
|
+
const { token, jti } = await signAccessToken({ activeKey: key }, { sub: "u", sessionId: "s", roles: [], permissions: [] });
|
|
35
|
+
|
|
36
|
+
await expect(
|
|
37
|
+
verifyAccessToken({ secret: key.secret, isDenylisted: async (candidate) => candidate === jti }, token),
|
|
38
|
+
).rejects.toBeInstanceOf(AccessTokenInvalidError);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("rejects an access token verified against a different secret", async () => {
|
|
42
|
+
const key = generateTestSigningKey();
|
|
43
|
+
const otherKey = generateTestSigningKey("other-kid");
|
|
44
|
+
const { token } = await signAccessToken({ activeKey: key }, { sub: "u", sessionId: "s", roles: [], permissions: [] });
|
|
45
|
+
|
|
46
|
+
await expect(
|
|
47
|
+
verifyAccessToken({ secret: otherKey.secret, isDenylisted: async () => false }, token),
|
|
48
|
+
).rejects.toBeInstanceOf(AccessTokenInvalidError);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("signs and verifies a refresh token round-trip", async () => {
|
|
52
|
+
const key = generateTestSigningKey();
|
|
53
|
+
const { token, jti } = await signRefreshToken({ activeKey: key }, { sub: "user-1", sessionId: "session-1", sv: 3 });
|
|
54
|
+
|
|
55
|
+
const claims = await verifyRefreshToken({ secret: key.secret }, token);
|
|
56
|
+
expect(claims).toEqual({ sub: "user-1", sessionId: "session-1", sv: 3, jti });
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("rejects a refresh token verified against a different secret", async () => {
|
|
60
|
+
const key = generateTestSigningKey();
|
|
61
|
+
const otherKey = generateTestSigningKey("other-kid");
|
|
62
|
+
const { token } = await signRefreshToken({ activeKey: key }, { sub: "u", sessionId: "s", sv: 1 });
|
|
63
|
+
|
|
64
|
+
await expect(verifyRefreshToken({ secret: otherKey.secret }, token)).rejects.toBeInstanceOf(RefreshInvalidError);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("signs and verifies a two-factor challenge token round-trip", async () => {
|
|
68
|
+
const key = generateTestSigningKey();
|
|
69
|
+
const { token } = await signTwoFactorChallengeToken({ activeKey: key }, "user-1");
|
|
70
|
+
|
|
71
|
+
const claims = await verifyTwoFactorChallengeToken({ secret: key.secret }, token);
|
|
72
|
+
expect(claims).toEqual({ sub: "user-1" });
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("rejects an access token presented in place of a two-factor challenge token", async () => {
|
|
76
|
+
const key = generateTestSigningKey();
|
|
77
|
+
const { token } = await signAccessToken({ activeKey: key }, { sub: "u", sessionId: "s", roles: [], permissions: [] });
|
|
78
|
+
|
|
79
|
+
await expect(verifyTwoFactorChallengeToken({ secret: key.secret }, token)).rejects.toBeInstanceOf(TwoFactorChallengeInvalidError);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { buildTotpProvisioningUri, generateBackupCodes, generateTotpCode, generateTotpSecret, verifyBackupCode, verifyTotpCode } from "../two-factor.js";
|
|
3
|
+
|
|
4
|
+
// Shared seed from RFC 6238 Appendix B / RFC 4226 Appendix D ("12345678901234567890",
|
|
5
|
+
// base32-encoded) — RFC 4226's own T=0 vector for this seed is the well-known "755224".
|
|
6
|
+
const RFC_SECRET = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ";
|
|
7
|
+
|
|
8
|
+
describe("two-factor: TOTP", () => {
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
vi.useRealTimers();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("matches the RFC 4226 test vector at T=0 (counter 0, 6 digits)", () => {
|
|
14
|
+
vi.useFakeTimers();
|
|
15
|
+
vi.setSystemTime(new Date(0));
|
|
16
|
+
expect(verifyTotpCode(RFC_SECRET, "755224")).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("matches the 6-digit truncation of the RFC 6238 T=59 vector (counter 1)", () => {
|
|
20
|
+
vi.useFakeTimers();
|
|
21
|
+
vi.setSystemTime(new Date(59_000));
|
|
22
|
+
expect(verifyTotpCode(RFC_SECRET, "287082")).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("accepts the adjacent step's code within the default window, rejects it with window 0", () => {
|
|
26
|
+
vi.useFakeTimers();
|
|
27
|
+
vi.setSystemTime(new Date(59_000)); // counter 1; "755224" is counter 0's code
|
|
28
|
+
expect(verifyTotpCode(RFC_SECRET, "755224")).toBe(true); // default window is 1
|
|
29
|
+
expect(verifyTotpCode(RFC_SECRET, "755224", { window: 0 })).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("rejects a malformed code without throwing", () => {
|
|
33
|
+
expect(verifyTotpCode(RFC_SECRET, "not-a-code")).toBe(false);
|
|
34
|
+
expect(verifyTotpCode(RFC_SECRET, "12345")).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("generateTotpCode round-trips with verifyTotpCode", () => {
|
|
38
|
+
const secret = generateTotpSecret();
|
|
39
|
+
expect(verifyTotpCode(secret, generateTotpCode(secret))).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("generateTotpSecret produces a fresh base32 secret each call", () => {
|
|
43
|
+
const a = generateTotpSecret();
|
|
44
|
+
const b = generateTotpSecret();
|
|
45
|
+
expect(a).not.toBe(b);
|
|
46
|
+
expect(a).toMatch(/^[A-Z2-7]+$/);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("buildTotpProvisioningUri embeds the issuer, account name, and secret", () => {
|
|
50
|
+
const uri = buildTotpProvisioningUri({ secret: RFC_SECRET, accountName: "alice@example.com", issuer: "simple-auth-kit" });
|
|
51
|
+
expect(uri).toContain("otpauth://totp/");
|
|
52
|
+
expect(uri).toContain(`secret=${RFC_SECRET}`);
|
|
53
|
+
expect(decodeURIComponent(uri)).toContain("simple-auth-kit:alice@example.com");
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe("two-factor: backup codes", () => {
|
|
58
|
+
it("generates unique codes with matching hashes that verify correctly", () => {
|
|
59
|
+
const codes = generateBackupCodes(10);
|
|
60
|
+
expect(codes).toHaveLength(10);
|
|
61
|
+
expect(new Set(codes.map((c) => c.code)).size).toBe(10);
|
|
62
|
+
|
|
63
|
+
for (const { code, hash } of codes) {
|
|
64
|
+
expect(verifyBackupCode(code, hash)).toBe(true);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("rejects an incorrect backup code", () => {
|
|
69
|
+
const [{ hash }] = generateBackupCodes(1);
|
|
70
|
+
expect(verifyBackupCode("0000000000", hash)).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { SignJWT, jwtVerify } from "jose";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import {
|
|
4
|
+
AccessTokenClaims,
|
|
5
|
+
AccessTokenInput,
|
|
6
|
+
AccessTokenInvalidError,
|
|
7
|
+
JwtSigningKey,
|
|
8
|
+
RefreshInvalidError,
|
|
9
|
+
RefreshTokenClaims,
|
|
10
|
+
TwoFactorChallengeInvalidError,
|
|
11
|
+
} from "./types.js";
|
|
12
|
+
|
|
13
|
+
const ACCESS_TOKEN_TTL_SECONDS_DEFAULT = 900; // 15 min
|
|
14
|
+
const REFRESH_TOKEN_TTL_SECONDS_DEFAULT = 60 * 60 * 24 * 30; // 30 days
|
|
15
|
+
const TWO_FACTOR_CHALLENGE_TTL_SECONDS_DEFAULT = 5 * 60; // 5 min
|
|
16
|
+
const TWO_FACTOR_CHALLENGE_PURPOSE = "2fa-challenge";
|
|
17
|
+
|
|
18
|
+
export interface SignDeps {
|
|
19
|
+
activeKey: JwtSigningKey;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface VerifyAccessTokenDeps {
|
|
23
|
+
secret: Uint8Array;
|
|
24
|
+
isDenylisted: (jti: string) => Promise<boolean>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface VerifyRefreshTokenDeps {
|
|
28
|
+
secret: Uint8Array;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function signAccessToken(
|
|
32
|
+
deps: SignDeps,
|
|
33
|
+
claims: AccessTokenInput,
|
|
34
|
+
opts: { ttlSeconds?: number } = {},
|
|
35
|
+
): Promise<{ token: string; jti: string; expiresAt: Date }> {
|
|
36
|
+
const jti = randomUUID();
|
|
37
|
+
const ttl = opts.ttlSeconds ?? ACCESS_TOKEN_TTL_SECONDS_DEFAULT;
|
|
38
|
+
const now = Math.floor(Date.now() / 1000);
|
|
39
|
+
const token = await new SignJWT({
|
|
40
|
+
sessionId: claims.sessionId,
|
|
41
|
+
// Omitted, not emptied, when the caller passes neither: a token that resolves authorization
|
|
42
|
+
// server-side must carry no authorization claim at all, so there is nothing to read off it.
|
|
43
|
+
...(claims.roles !== undefined ? { roles: claims.roles } : {}),
|
|
44
|
+
...(claims.permissions !== undefined ? { permissions: claims.permissions } : {}),
|
|
45
|
+
})
|
|
46
|
+
.setProtectedHeader({ alg: "HS256", kid: deps.activeKey.kid })
|
|
47
|
+
.setSubject(claims.sub)
|
|
48
|
+
.setJti(jti)
|
|
49
|
+
.setIssuedAt(now)
|
|
50
|
+
.setExpirationTime(now + ttl)
|
|
51
|
+
.sign(deps.activeKey.secret);
|
|
52
|
+
return { token, jti, expiresAt: new Date((now + ttl) * 1000) };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function verifyAccessToken(deps: VerifyAccessTokenDeps, token: string): Promise<AccessTokenClaims> {
|
|
56
|
+
let payload;
|
|
57
|
+
try {
|
|
58
|
+
({ payload } = await jwtVerify(token, deps.secret, { algorithms: ["HS256"] }));
|
|
59
|
+
} catch {
|
|
60
|
+
throw new AccessTokenInvalidError();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const jti = payload.jti;
|
|
64
|
+
if (!jti) throw new AccessTokenInvalidError("missing jti");
|
|
65
|
+
if (await deps.isDenylisted(jti)) throw new AccessTokenInvalidError("token revoked");
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
sub: payload.sub as string,
|
|
69
|
+
sessionId: payload.sessionId as string,
|
|
70
|
+
roles: (payload.roles as string[]) ?? [],
|
|
71
|
+
permissions: (payload.permissions as string[]) ?? [],
|
|
72
|
+
jti,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function signRefreshToken(
|
|
77
|
+
deps: SignDeps,
|
|
78
|
+
claims: Omit<RefreshTokenClaims, "jti">,
|
|
79
|
+
opts: { ttlSeconds?: number; jti?: string } = {},
|
|
80
|
+
): Promise<{ token: string; jti: string }> {
|
|
81
|
+
const jti = opts.jti ?? randomUUID();
|
|
82
|
+
const ttl = opts.ttlSeconds ?? REFRESH_TOKEN_TTL_SECONDS_DEFAULT;
|
|
83
|
+
const now = Math.floor(Date.now() / 1000);
|
|
84
|
+
const token = await new SignJWT({ sessionId: claims.sessionId, sv: claims.sv })
|
|
85
|
+
.setProtectedHeader({ alg: "HS256", kid: deps.activeKey.kid })
|
|
86
|
+
.setSubject(claims.sub)
|
|
87
|
+
.setJti(jti)
|
|
88
|
+
.setIssuedAt(now)
|
|
89
|
+
.setExpirationTime(now + ttl)
|
|
90
|
+
.sign(deps.activeKey.secret);
|
|
91
|
+
return { token, jti };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Short-TTL, purpose-scoped token proving "password already checked" without granting API
|
|
95
|
+
// access — carries no session/roles.
|
|
96
|
+
export async function signTwoFactorChallengeToken(
|
|
97
|
+
deps: SignDeps,
|
|
98
|
+
sub: string,
|
|
99
|
+
opts: { ttlSeconds?: number } = {},
|
|
100
|
+
): Promise<{ token: string; jti: string }> {
|
|
101
|
+
const jti = randomUUID();
|
|
102
|
+
const ttl = opts.ttlSeconds ?? TWO_FACTOR_CHALLENGE_TTL_SECONDS_DEFAULT;
|
|
103
|
+
const now = Math.floor(Date.now() / 1000);
|
|
104
|
+
const token = await new SignJWT({ purpose: TWO_FACTOR_CHALLENGE_PURPOSE })
|
|
105
|
+
.setProtectedHeader({ alg: "HS256", kid: deps.activeKey.kid })
|
|
106
|
+
.setSubject(sub)
|
|
107
|
+
.setJti(jti)
|
|
108
|
+
.setIssuedAt(now)
|
|
109
|
+
.setExpirationTime(now + ttl)
|
|
110
|
+
.sign(deps.activeKey.secret);
|
|
111
|
+
return { token, jti };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export async function verifyTwoFactorChallengeToken(deps: VerifyRefreshTokenDeps, token: string): Promise<{ sub: string }> {
|
|
115
|
+
let payload;
|
|
116
|
+
try {
|
|
117
|
+
({ payload } = await jwtVerify(token, deps.secret, { algorithms: ["HS256"] }));
|
|
118
|
+
} catch {
|
|
119
|
+
throw new TwoFactorChallengeInvalidError();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (payload.purpose !== TWO_FACTOR_CHALLENGE_PURPOSE || !payload.sub) throw new TwoFactorChallengeInvalidError("malformed claims");
|
|
123
|
+
return { sub: payload.sub };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export async function verifyRefreshToken(deps: VerifyRefreshTokenDeps, token: string): Promise<RefreshTokenClaims> {
|
|
127
|
+
let payload;
|
|
128
|
+
try {
|
|
129
|
+
({ payload } = await jwtVerify(token, deps.secret, { algorithms: ["HS256"] }));
|
|
130
|
+
} catch {
|
|
131
|
+
throw new RefreshInvalidError();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const jti = payload.jti;
|
|
135
|
+
if (!jti || payload.sv === undefined || !payload.sessionId) throw new RefreshInvalidError("malformed claims");
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
sub: payload.sub as string,
|
|
139
|
+
sessionId: payload.sessionId as string,
|
|
140
|
+
sv: payload.sv as number,
|
|
141
|
+
jti,
|
|
142
|
+
};
|
|
143
|
+
}
|