miolo 3.0.0-beta.21 → 3.0.0-beta.210

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.
Files changed (246) hide show
  1. package/bin/build/build.mjs +53 -0
  2. package/bin/build/build_bin.mjs +17 -0
  3. package/bin/build/cli/client.mjs +52 -0
  4. package/bin/build/cli/css.mjs +22 -0
  5. package/bin/build/cli/index.mjs +40 -0
  6. package/bin/build/cli/ssr.mjs +21 -0
  7. package/bin/build/server/aliases.mjs +112 -0
  8. package/bin/build/server/babel.config.js +24 -0
  9. package/bin/build/server/banner.mjs +20 -0
  10. package/bin/build/server/bundle.mjs +111 -0
  11. package/bin/build/server/fix.mjs +15 -0
  12. package/bin/build/server/index.mjs +69 -0
  13. package/bin/build/server/options.mjs +83 -0
  14. package/bin/create/auth.mjs +23 -0
  15. package/bin/create/copy.mjs +175 -0
  16. package/bin/create/docker.mjs +25 -0
  17. package/bin/create/index.mjs +137 -0
  18. package/bin/create/pkgjson.mjs +72 -0
  19. package/bin/create/prepare-template.mjs +158 -0
  20. package/bin/create/validation.mjs +27 -0
  21. package/bin/dev/dev.mjs +32 -23
  22. package/bin/dev/dev_start.mjs +6 -5
  23. package/bin/index.mjs +94 -52
  24. package/bin/prod-bin/create-bin.mjs +42 -27
  25. package/bin/prod-bin/run.mjs +13 -9
  26. package/bin/{prod-run → run}/pid.mjs +4 -4
  27. package/bin/run/restart.mjs +13 -0
  28. package/bin/run/start.mjs +18 -0
  29. package/bin/run/stop.mjs +20 -0
  30. package/bin/util.mjs +35 -11
  31. package/package.json +59 -39
  32. package/src/config/.env +34 -12
  33. package/src/config/defaults.mjs +253 -185
  34. package/src/config/env.mjs +40 -22
  35. package/src/config/index.mjs +19 -24
  36. package/src/config/util.mjs +25 -10
  37. package/src/db-conn.mjs +34 -0
  38. package/src/engines/cron/emails.mjs +10 -5
  39. package/src/engines/cron/index.mjs +45 -51
  40. package/src/engines/cron/init.mjs +16 -17
  41. package/src/engines/cron/ipsum.mjs +65 -60
  42. package/src/engines/cron/syscheck.mjs +30 -30
  43. package/src/engines/emailer/index.mjs +1 -2
  44. package/src/engines/emailer/queue.mjs +14 -20
  45. package/src/engines/emailer/transporter.mjs +86 -74
  46. package/src/engines/geoip/index.mjs +23 -28
  47. package/src/engines/http/index.mjs +26 -15
  48. package/src/engines/logger/buildErrorEmailBody.mjs +72 -0
  49. package/src/engines/logger/index.mjs +114 -122
  50. package/src/engines/logger/injectStackTrace.mjs +59 -0
  51. package/src/engines/logger/logger_mail.mjs +47 -61
  52. package/src/engines/logger/reopenTransportOnHupSignal.mjs +12 -13
  53. package/src/engines/parser/Parser.mjs +77 -60
  54. package/src/engines/parser/index.mjs +1 -1
  55. package/src/engines/schema/diffObjs.mjs +41 -0
  56. package/src/engines/schema/index.mjs +4 -0
  57. package/src/engines/schema/input.mjs +54 -0
  58. package/src/engines/schema/output.mjs +66 -0
  59. package/src/engines/socket/index.mjs +44 -46
  60. package/src/index.mjs +15 -10
  61. package/src/middleware/auth/basic.mjs +41 -40
  62. package/src/middleware/auth/custom.mjs +10 -13
  63. package/src/middleware/auth/guest.mjs +27 -27
  64. package/src/middleware/auth/passport/index.mjs +374 -0
  65. package/src/middleware/auth/passport/session/index.mjs +43 -0
  66. package/src/middleware/auth/{credentials → passport}/session/store.mjs +35 -15
  67. package/src/middleware/auth/passport/session/store_koa_redis.mjs +3 -0
  68. package/src/middleware/context/cache/index.mjs +78 -33
  69. package/src/middleware/context/cache/options.mjs +19 -21
  70. package/src/middleware/context/db.mjs +45 -20
  71. package/src/middleware/context/index.mjs +12 -12
  72. package/src/middleware/extra.mjs +4 -5
  73. package/src/middleware/http/body.mjs +25 -25
  74. package/src/middleware/http/catcher.mjs +81 -8
  75. package/src/middleware/http/custom_blacklist.mjs +19 -16
  76. package/src/middleware/http/headers.mjs +37 -34
  77. package/src/middleware/http/ratelimit.mjs +16 -23
  78. package/src/middleware/http/request.mjs +60 -65
  79. package/src/middleware/routes/catch_js_error.mjs +30 -23
  80. package/src/middleware/routes/robots.mjs +4 -7
  81. package/src/middleware/routes/router/crud/attachCrudRoutes.mjs +108 -90
  82. package/src/middleware/routes/router/crud/getCrudConfig.mjs +31 -55
  83. package/src/middleware/routes/router/defaults.mjs +6 -19
  84. package/src/middleware/routes/router/index.mjs +17 -21
  85. package/src/middleware/routes/router/queries/attachQueriesRoutes.mjs +227 -50
  86. package/src/middleware/routes/router/queries/getQueriesConfig.mjs +45 -55
  87. package/src/middleware/routes/router/utils.mjs +41 -26
  88. package/src/middleware/ssr/context.mjs +5 -7
  89. package/src/middleware/ssr/html.mjs +66 -43
  90. package/src/middleware/ssr/loader.mjs +11 -14
  91. package/src/middleware/ssr/ssr_render.mjs +39 -22
  92. package/src/middleware/static/index.mjs +33 -14
  93. package/src/middleware/vite/devserver.mjs +38 -22
  94. package/src/middleware/vite/watcher.mjs +12 -14
  95. package/src/server-cron.mjs +13 -8
  96. package/src/server-dev.mjs +13 -16
  97. package/src/server.mjs +49 -51
  98. package/template/.agent/skills/miolo-app-arch/SKILL.md +218 -0
  99. package/template/.agent/skills/miolo-auth/SKILL.md +450 -0
  100. package/template/.agent/skills/miolo-cli-router/SKILL.md +394 -0
  101. package/template/.agent/skills/miolo-database/SKILL.md +358 -0
  102. package/template/.agent/skills/miolo-react-patterns/SKILL.md +426 -0
  103. package/template/.agent/skills/miolo-routing/SKILL.md +326 -0
  104. package/template/.agent/skills/miolo-schemas/SKILL.md +329 -0
  105. package/template/.agent/skills/miolo-session-context/SKILL.md +397 -0
  106. package/template/.agent/skills/miolo-ssr/SKILL.md +433 -0
  107. package/template/.editorconfig +18 -0
  108. package/template/.env +120 -0
  109. package/template/biome.json +63 -0
  110. package/template/components.json +21 -0
  111. package/template/db/init.sh +89 -0
  112. package/template/db/sql/00_drop.sql +2 -0
  113. package/template/db/sql/01_users.sql +31 -0
  114. package/template/db/sql/02_todos.sql +20 -0
  115. package/template/docker/Dockerfile +13 -0
  116. package/template/docker/docker-compose.yaml +79 -0
  117. package/template/gitignore +42 -0
  118. package/template/jsconfig.json +18 -0
  119. package/template/package.json +88 -0
  120. package/template/postcss.config.js +9 -0
  121. package/template/src/cli/App.jsx +25 -0
  122. package/template/src/cli/components/JsonTreeViewer.jsx +128 -0
  123. package/template/src/cli/components/shadcn-io/spinner/index.jsx +232 -0
  124. package/template/src/cli/components/stepper.jsx +408 -0
  125. package/template/src/cli/components/ui/avatar.jsx +36 -0
  126. package/template/src/cli/components/ui/badge.jsx +31 -0
  127. package/template/src/cli/components/ui/breadcrumb.jsx +97 -0
  128. package/template/src/cli/components/ui/card.jsx +73 -0
  129. package/template/src/cli/components/ui/collapsible.jsx +16 -0
  130. package/template/src/cli/components/ui/dropdown-menu.jsx +179 -0
  131. package/template/src/cli/components/ui/field.jsx +217 -0
  132. package/template/src/cli/components/ui/input.jsx +19 -0
  133. package/template/src/cli/components/ui/label.jsx +17 -0
  134. package/template/src/cli/components/ui/pagination.jsx +99 -0
  135. package/template/src/cli/components/ui/patched/alert.jsx +56 -0
  136. package/template/src/cli/components/ui/patched/button.jsx +45 -0
  137. package/template/src/cli/components/ui/patched/dialog.jsx +114 -0
  138. package/template/src/cli/components/ui/patched/sidebar.jsx +660 -0
  139. package/template/src/cli/components/ui/select.jsx +141 -0
  140. package/template/src/cli/components/ui/separator.jsx +21 -0
  141. package/template/src/cli/components/ui/sheet.jsx +115 -0
  142. package/template/src/cli/components/ui/skeleton.jsx +13 -0
  143. package/template/src/cli/components/ui/sonner.jsx +22 -0
  144. package/template/src/cli/components/ui/switch.jsx +25 -0
  145. package/template/src/cli/components/ui/table.jsx +88 -0
  146. package/template/src/cli/components/ui/textarea.jsx +16 -0
  147. package/template/src/cli/components/ui/tooltip.jsx +45 -0
  148. package/template/src/cli/config/store_keys.mjs +2 -0
  149. package/template/src/cli/context/data/DataContext.jsx +5 -0
  150. package/template/src/cli/context/data/DataProvider.jsx +44 -0
  151. package/template/src/cli/context/data/useBreads.mjs +15 -0
  152. package/template/src/cli/context/data/useDataContext.mjs +4 -0
  153. package/template/src/cli/context/session/SessionContext.mjs +4 -0
  154. package/template/src/cli/context/session/SessionProvider.jsx +31 -0
  155. package/template/src/cli/context/session/makePermissioner.mjs +34 -0
  156. package/template/src/cli/context/session/useSessionContext.mjs +6 -0
  157. package/template/src/cli/context/theme/ThemeContext.mjs +4 -0
  158. package/template/src/cli/context/theme/ThemeProvider.jsx +49 -0
  159. package/template/src/cli/context/theme/useThemeContext.mjs +6 -0
  160. package/template/src/cli/context/ui/UIContext.jsx +5 -0
  161. package/template/src/cli/context/ui/UIProvider.jsx +16 -0
  162. package/template/src/cli/context/ui/useUIContext.mjs +4 -0
  163. package/template/src/cli/context/util.mjs +17 -0
  164. package/template/src/cli/entry-cli.jsx +33 -0
  165. package/template/src/cli/hooks/useIsMobile.mjs +19 -0
  166. package/template/src/cli/hooks/useStoragedState.mjs +63 -0
  167. package/template/src/cli/index.html +29 -0
  168. package/template/src/cli/layout/app-sidebar.jsx +25 -0
  169. package/template/src/cli/layout/main-layout.jsx +63 -0
  170. package/template/src/cli/layout/nav-last-todos.jsx +72 -0
  171. package/template/src/cli/layout/nav-main.jsx +39 -0
  172. package/template/src/cli/layout/nav-user.jsx +105 -0
  173. package/template/src/cli/layout/prop-switcher.jsx +93 -0
  174. package/template/src/cli/lib/utils.mjs +10 -0
  175. package/template/src/cli/pages/Index.jsx +13 -0
  176. package/template/src/cli/pages/IndexOffline.jsx +13 -0
  177. package/template/src/cli/pages/IndexOnline.jsx +18 -0
  178. package/template/src/cli/pages/dash/Dashboard.jsx +29 -0
  179. package/template/src/cli/pages/offline/Login.jsx +43 -0
  180. package/template/src/cli/pages/offline/LoginForm.jsx +115 -0
  181. package/template/src/cli/pages/security/Security.jsx +39 -0
  182. package/template/src/cli/pages/security/SecurityForm.jsx +106 -0
  183. package/template/src/cli/pages/todos/TodoActions.jsx +99 -0
  184. package/template/src/cli/pages/todos/TodoAdd.jsx +43 -0
  185. package/template/src/cli/pages/todos/TodoList.jsx +60 -0
  186. package/template/src/cli/pages/todos/Todos.jsx +23 -0
  187. package/template/src/cli/pages/todos/context/TodosContext.jsx +5 -0
  188. package/template/src/cli/pages/todos/context/TodosProvider.jsx +191 -0
  189. package/template/src/cli/pages/todos/context/useTodosContext.mjs +4 -0
  190. package/template/src/ns/models/Todo.mjs +29 -0
  191. package/template/src/ns/models/TodoList.mjs +8 -0
  192. package/template/src/ns/models/User.mjs +40 -0
  193. package/template/src/server/bot/check_today.mjs +10 -0
  194. package/template/src/server/io/cache/base.mjs +21 -0
  195. package/template/src/server/io/db/filter.mjs +92 -0
  196. package/template/src/server/io/db/todos/delete.mjs +29 -0
  197. package/template/src/server/io/db/todos/find.mjs +13 -0
  198. package/template/src/server/io/db/todos/read.mjs +83 -0
  199. package/template/src/server/io/db/todos/toggle.mjs +37 -0
  200. package/template/src/server/io/db/todos/upsave.mjs +32 -0
  201. package/template/src/server/io/db/triggers/user.mjs +13 -0
  202. package/template/src/server/io/db/users/auth.mjs +132 -0
  203. package/template/src/server/io/db/users/pwd.mjs +38 -0
  204. package/template/src/server/io/db/users/save.mjs +17 -0
  205. package/template/src/server/miolo/auth/basic.mjs +15 -0
  206. package/template/src/server/miolo/auth/guest.mjs +3 -0
  207. package/template/src/server/miolo/auth/passport.mjs +73 -0
  208. package/template/src/server/miolo/cache.mjs +11 -0
  209. package/template/src/server/miolo/cron/foo.mjs +7 -0
  210. package/template/src/server/miolo/cron/index.mjs +28 -0
  211. package/template/src/server/miolo/cron/invalidate.mjs +21 -0
  212. package/template/src/server/miolo/db.mjs +36 -0
  213. package/template/src/server/miolo/http.mjs +14 -0
  214. package/template/src/server/miolo/index.mjs +43 -0
  215. package/template/src/server/miolo/routes/crud.mjs +16 -0
  216. package/template/src/server/miolo/routes/index.mjs +8 -0
  217. package/template/src/server/miolo/ssr/entry-server.jsx +13 -0
  218. package/template/src/server/miolo/ssr/loader.mjs +18 -0
  219. package/template/src/server/routes/index.mjs +66 -0
  220. package/template/src/server/routes/todos/mod.mjs +52 -0
  221. package/template/src/server/routes/todos/read.mjs +45 -0
  222. package/template/src/server/routes/todos/special.mjs +47 -0
  223. package/template/src/server/routes/users/user.mjs +54 -0
  224. package/template/src/server/server.mjs +10 -0
  225. package/template/src/server/utils/crypt.mjs +38 -0
  226. package/template/src/server/utils/io.mjs +15 -0
  227. package/template/src/server/utils/pwdfor.mjs +25 -0
  228. package/template/src/server/utils/schema.mjs +22 -0
  229. package/template/src/static/img/default/profile.png +0 -0
  230. package/template/src/static/img/favicon.ico +0 -0
  231. package/template/src/static/img/miolo_logo.png +0 -0
  232. package/template/src/static/img/miolo_name.png +0 -0
  233. package/template/src/static/public/manifest.json +21 -0
  234. package/template/src/static/public/sw.js +79 -0
  235. package/template/src/static/style/globals.css +156 -0
  236. package/template/src/static/style/json-tree.css +54 -0
  237. package/template/src/static/style/skeleton.css +49 -0
  238. package/bin/prod-build/build-client.mjs +0 -67
  239. package/bin/prod-build/build-server.mjs +0 -58
  240. package/bin/prod-run/restart.mjs +0 -9
  241. package/bin/prod-run/start.mjs +0 -15
  242. package/bin/prod-run/stop.mjs +0 -20
  243. package/src/engines/logger/verify.mjs +0 -22
  244. package/src/middleware/auth/credentials/index.mjs +0 -151
  245. package/src/middleware/auth/credentials/session/index.mjs +0 -24
  246. package/src/middleware/auth/credentials/session/store_koa_redis.mjs +0 -3
@@ -0,0 +1,450 @@
1
+ ---
2
+ name: miolo-auth
3
+ description: Authentication configuration and strategies for miolo applications. Use when implementing, modifying, or troubleshooting authentication, configuring auth strategies (passport, basic, guest), or setting up user sessions.
4
+ ---
5
+
6
+ # Miolo Authentication
7
+
8
+ Authentication configuration and strategies for miolo applications.
9
+
10
+ ## Authentication Strategies
11
+
12
+ Miolo supports multiple built-in authentication strategies:
13
+
14
+ ```
15
+ src/server/miolo/auth/
16
+ ├── passport.mjs # Passport-based auth (local + OAuth2)
17
+ ├── basic.mjs # HTTP Basic authentication
18
+ └── guest.mjs # Guest/anonymous access
19
+ ```
20
+
21
+ ## Configuring Authentication
22
+
23
+ Authentication is configured in `src/server/miolo/index.mjs`:
24
+
25
+ ```javascript
26
+ import passport from './auth/passport.mjs'
27
+ // or: import auth from './auth/basic.mjs'
28
+ // or: import auth from './auth/guest.mjs'
29
+
30
+ export default {
31
+ auth: {
32
+ passport // Enables Passport.js with local + Google OAuth2
33
+ },
34
+ // ... other config
35
+ }
36
+ ```
37
+
38
+ ## Passport Strategy (Recommended)
39
+
40
+ Unified authentication using Passport.js supporting both **local (username/password)** and **Google OAuth2**.
41
+
42
+ **File:** `src/server/miolo/auth/passport.mjs`
43
+
44
+ ```javascript
45
+ import { db_find_user_by_id, db_auth_user,
46
+ db_user_find_or_create_from_google } from '#server/io/db/users/auth.mjs'
47
+
48
+ const get_user_id = (user, done, ctx) => {
49
+ const uid = user?.id
50
+ if (uid != undefined) {
51
+ return done(null, uid)
52
+ } else {
53
+ const err = new Error('User id is undefined')
54
+ return done(err, null)
55
+ }
56
+ }
57
+
58
+ const find_user_by_id = (id, done, ctx) => {
59
+ ctx.miolo.logger.debug(`[auth] find_user_by_id() - Searching user id ${id}`)
60
+ db_find_user_by_id(ctx.miolo, id).then(user => {
61
+ if (user == undefined) {
62
+ const err = new Error('User not found')
63
+ return done(err, null)
64
+ } else {
65
+ return done(null, user)
66
+ }
67
+ })
68
+ }
69
+
70
+ const local_auth_user = (username, password, done, ctx) => {
71
+ ctx.miolo.logger.debug(`[auth][local] Checking credentials for ${username}`)
72
+
73
+ db_auth_user(ctx.miolo, username, password).then(([user, msg]) => {
74
+ if (user == undefined) {
75
+ const err = new Error(msg)
76
+ return done(err, null)
77
+ } else {
78
+ return done(null, user)
79
+ }
80
+ })
81
+ }
82
+
83
+ const google_auth_user = async (accessToken, refreshToken, profile, done, ctx) => {
84
+ try {
85
+ const google_id = profile.id
86
+ const email = profile.emails?.[0]?.value
87
+ const name = profile.displayName
88
+ const google_picture = profile.photos?.[0]?.value
89
+
90
+ ctx.miolo.logger.info(`[auth][google] Authenticated user: ${email}`)
91
+
92
+ return db_user_find_or_create_from_google(
93
+ ctx.miolo, email, name, google_id, google_picture
94
+ ).then(([user, msg]) => {
95
+ if (user == undefined) {
96
+ const err = new Error(msg)
97
+ return done(err, null)
98
+ } else {
99
+ return done(null, user)
100
+ }
101
+ })
102
+ } catch (error) {
103
+ ctx.miolo.logger.error(`[auth][google] Error: ${error}`)
104
+ return done(error, null)
105
+ }
106
+ }
107
+
108
+ export default {
109
+ get_user_id,
110
+ find_user_by_id,
111
+ local_auth_user,
112
+ local_url_login: '/login',
113
+ local_url_logout: '/logout',
114
+ local_url_login_redirect: undefined,
115
+ local_url_logout_redirect: '/',
116
+ google_auth_user,
117
+ google_client_id: process.env.MIOLO_AUTH_GOOGLE_CLIENT_ID,
118
+ google_client_secret: process.env.MIOLO_AUTH_GOOGLE_CLIENT_SECRET,
119
+ google_url_login: '/auth/google',
120
+ google_url_callback: process.env.MIOLO_AUTH_GOOGLE_CALLBACK_URL || '/auth/google/callback',
121
+ google_url_logout: '/logout',
122
+ google_url_logout_redirect: '/'
123
+ }
124
+ ```
125
+
126
+ ### Passport Configuration Elements
127
+
128
+ #### Common Functions
129
+ - `get_user_id(user, done, ctx)` - Extracts user ID for session storage
130
+ - `find_user_by_id(id, done, ctx)` - Retrieves user by ID (session deserialization)
131
+
132
+ #### Local Authentication (Username/Password)
133
+ - `local_auth_user(username, password, done, ctx)` - Validates credentials
134
+ - `local_url_login` - Login endpoint (default: `/login`)
135
+ - `local_url_logout` - Logout endpoint (default: `/logout`)
136
+ - `local_url_login_redirect` - Redirect after login (optional)
137
+ - `local_url_logout_redirect` - Redirect after logout (default: `/`)
138
+
139
+ #### Google OAuth2 Authentication
140
+ - `google_auth_user(accessToken, refreshToken, profile, done, ctx)` - Handles Google profile
141
+ - `google_client_id` - Google OAuth2 client ID (from env)
142
+ - `google_client_secret` - Google OAuth2 client secret (from env)
143
+ - `google_url_login` - OAuth initiation endpoint (default: `/auth/google`)
144
+ - `google_url_callback` - OAuth callback endpoint (from env or `/auth/google/callback`)
145
+ - `google_url_logout` - Logout endpoint (default: `/logout`)
146
+ - `google_url_logout_redirect` - Redirect after logout (default: `/`)
147
+
148
+ ### Environment Variables
149
+
150
+ **Required for Google OAuth2 (.env):**
151
+ ```
152
+ MIOLO_AUTH_GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
153
+ MIOLO_AUTH_GOOGLE_CLIENT_SECRET=your-client-secret
154
+ MIOLO_AUTH_GOOGLE_CALLBACK_URL=http://localhost:8001/auth/google/callback
155
+ ```
156
+
157
+ **Session configuration:**
158
+ ```
159
+ MIOLO_SESSION_SALT=your-random-salt-here
160
+ MIOLO_SESSION_SECRET=your-session-secret
161
+ MIOLO_SESSION_SAME_SITE=lax # Required for Google OAuth2
162
+ ```
163
+
164
+ ### Google Cloud Console Setup
165
+
166
+ 1. Go to [Google Cloud Console](https://console.cloud.google.com/)
167
+ 2. Create a project or select existing one
168
+ 3. Enable Google+ API
169
+ 4. Create OAuth 2.0 credentials:
170
+ - Application type: Web application
171
+ - Authorized JavaScript origins: `http://localhost:8001`
172
+ - Authorized redirect URIs: `http://localhost:8001/auth/google/callback`
173
+ 5. Copy Client ID and Client Secret to your `.env`
174
+
175
+ ### OAuth2 Flow
176
+
177
+ 1. User clicks "Login with Google" → `window.location.href = '/auth/google'`
178
+ 2. Server redirects to Google authentication page
179
+ 3. User authenticates with Google
180
+ 4. Google redirects back to `/auth/google/callback`
181
+ 5. `google_auth_user` processes Google profile
182
+ 6. User found/created in database
183
+ 7. User logged in and redirected to home
184
+
185
+ ### Frontend Login Button
186
+
187
+ ```jsx
188
+ // For local login (POST form)
189
+ <form onSubmit={handleLocalLogin}>
190
+ <input name="username" />
191
+ <input name="password" type="password" />
192
+ <button type="submit">Login</button>
193
+ </form>
194
+
195
+ // For Google OAuth2 (Navigate to endpoint)
196
+ <button onClick={() => window.location.href = '/auth/google'}>
197
+ Login with Google
198
+ </button>
199
+ ```
200
+
201
+ **IMPORTANT:** Google OAuth2 requires **navigation**, not fetch/AJAX:
202
+ ```javascript
203
+ // ✅ CORRECT
204
+ window.location.href = '/auth/google'
205
+
206
+ // ❌ WRONG - Causes CORS errors
207
+ fetch('/auth/google')
208
+ ```
209
+
210
+ ## Basic Authentication
211
+
212
+ HTTP Basic Auth for simple API access.
213
+
214
+ **File:** `src/server/miolo/auth/basic.mjs`
215
+
216
+ ```javascript
217
+ export default {
218
+ async login(ctx, credentials) {
219
+ const { username, password } = credentials
220
+
221
+ if (username === process.env.API_USER &&
222
+ password === process.env.API_PASSWORD) {
223
+ return {
224
+ ok: true,
225
+ user: { id: 1, username, role: 'api' }
226
+ }
227
+ }
228
+
229
+ return { ok: false, error: 'Invalid credentials' }
230
+ }
231
+ }
232
+ ```
233
+
234
+ ## Guest Authentication
235
+
236
+ Anonymous access without credentials.
237
+
238
+ **File:** `src/server/miolo/auth/guest.mjs`
239
+
240
+ ```javascript
241
+ export default {
242
+ make_guest_token: (session) => {
243
+ return `guest-${Date.now()}-${Math.random()}`
244
+ }
245
+ }
246
+ ```
247
+
248
+ ## User Session
249
+
250
+ After successful login, user is available in routes:
251
+
252
+ ```javascript
253
+ export async function r_protected_route(ctx, params) {
254
+ const user = ctx.state.user
255
+ const isAuthenticated = ctx.session.authenticated
256
+
257
+ // User object contains:
258
+ // - id
259
+ // - username (for local auth)
260
+ // - email
261
+ // - name
262
+ // - google_id (for Google auth)
263
+ // - google_picture (for Google auth)
264
+
265
+ ctx.miolo.logger.info(`User ${user.id} accessing route`)
266
+
267
+ return { ok: true, data: { userId: user.id } }
268
+ }
269
+ ```
270
+
271
+ ## Protecting Routes
272
+
273
+ Routes are protected by adding `auth` configuration:
274
+
275
+ ```javascript
276
+ const auth = {
277
+ require: true,
278
+ action: 'redirect',
279
+ redirect_url: '/page/login'
280
+ }
281
+
282
+ export default [{
283
+ prefix: 'api',
284
+ routes: [
285
+ // Public route
286
+ { method: 'GET', url: '/public/data', callback: r_public },
287
+
288
+ // Protected route
289
+ { method: 'GET', url: '/user/profile', auth, callback: r_profile },
290
+ ]
291
+ }]
292
+ ```
293
+
294
+ ## Database Functions
295
+
296
+ ### User Authentication (Local)
297
+
298
+ **File:** `src/server/io/db/users/auth.mjs`
299
+
300
+ ```javascript
301
+ import { sha512 } from '#server/utils/crypt.mjs'
302
+
303
+ export async function db_auth_user(miolo, username, password) {
304
+ const conn = miolo.db
305
+ const options = { transaction: undefined }
306
+
307
+ const salt = process.env.MIOLO_SESSION_SALT
308
+ const hashedPassword = sha512(password, salt)
309
+
310
+ const query = `
311
+ SELECT id, username, name, email, active
312
+ FROM account
313
+ WHERE username = $1 AND password = $2 AND active = 1`
314
+
315
+ const ruser = await conn.selectOne(query, [username, hashedPassword], options)
316
+
317
+ if (ruser?.id == undefined) {
318
+ return [undefined, 'Invalid credentials']
319
+ } else {
320
+ return [ruser, undefined]
321
+ }
322
+ }
323
+ ```
324
+
325
+ ### User by ID
326
+
327
+ ```javascript
328
+ export async function db_find_user_by_id(miolo, id) {
329
+ const conn = miolo.db
330
+ const options = { transaction: undefined }
331
+
332
+ const query = `
333
+ SELECT id, username, name, email, active,
334
+ google_id, google_picture
335
+ FROM account
336
+ WHERE id = $1`
337
+
338
+ const ruser = await conn.selectOne(query, [id], options)
339
+ return ruser
340
+ }
341
+ ```
342
+
343
+ ### Google OAuth2 User
344
+
345
+ ```javascript
346
+ export async function db_user_find_or_create_from_google(
347
+ miolo, email, name, google_id, google_picture
348
+ ) {
349
+ const conn = miolo.db
350
+ const options = { transaction: undefined }
351
+
352
+ // Try to find existing user by google_id
353
+ const query = `
354
+ SELECT id, username, name, email, active,
355
+ google_id, google_picture
356
+ FROM account
357
+ WHERE google_id = $1`
358
+
359
+ const ruser = await conn.selectOne(query, [google_id], options)
360
+
361
+ if (ruser?.id == undefined) {
362
+ // Create new user
363
+ const UUser = await conn.get_model('account')
364
+ const data = {
365
+ name,
366
+ email,
367
+ google_id,
368
+ google_picture,
369
+ active: 1
370
+ }
371
+ const uid = await UUser.insert(data, options)
372
+ data.id = uid
373
+ return [data, undefined]
374
+ } else {
375
+ return [ruser, undefined]
376
+ }
377
+ }
378
+ ```
379
+
380
+ ## Password Hashing
381
+
382
+ Use the provided crypto utilities:
383
+
384
+ ```javascript
385
+ import { sha512 } from '#server/utils/crypt.mjs'
386
+
387
+ const salt = process.env.MIOLO_SESSION_SALT
388
+ const hashedPassword = sha512(plainPassword, salt)
389
+ ```
390
+
391
+ **Session salt** is configured in `.env`:
392
+ ```
393
+ MIOLO_SESSION_SALT=your-random-salt-here
394
+ ```
395
+
396
+ ## Database Trigger for Password Hashing
397
+
398
+ **File:** `src/server/io/db/triggers/user.mjs`
399
+
400
+ ```javascript
401
+ import { sha512 } from '#server/utils/crypt.mjs'
402
+
403
+ async function beforeInsertUser(conn, params, options) {
404
+ const raw_pwd = params?.password
405
+ if (raw_pwd) {
406
+ const cry_pwd = sha512(raw_pwd, process.env.MIOLO_SESSION_SALT)
407
+ params.password = cry_pwd
408
+ }
409
+ return [params, options, true]
410
+ }
411
+
412
+ export default {
413
+ before_insert: beforeInsertUser
414
+ }
415
+ ```
416
+
417
+ ## Session Configuration
418
+
419
+ **Important for OAuth2:** The session must use `sameSite: 'lax'` to work with Google redirects.
420
+
421
+ **In** `packages/miolo/src/config/defaults.mjs`:
422
+ ```javascript
423
+ session: {
424
+ options: {
425
+ sameSite: 'lax', // Required for OAuth2
426
+ secure: false, // Set true in production with HTTPS
427
+ maxAge: 86400000, // 24 hours
428
+ }
429
+ }
430
+ ```
431
+
432
+ ## Best Practices
433
+
434
+ 1. **Never store plain passwords** - Always hash with salt
435
+ 2. **Use strong session salt** - Set `MIOLO_SESSION_SALT` in `.env`
436
+ 3. **Don't return passwords** - User object should never include password field
437
+ 4. **Validate on server** - Never trust client-side authentication
438
+ 5. **Use HTTPS in production** - Protect credentials in transit
439
+ 6. **Set secure cookies in production** - `MIOLO_SESSION_SECURE=true`
440
+ 7. **Use sameSite=lax for OAuth2** - Required for external OAuth redirects
441
+ 8. **Rate limit login attempts** - Prevent brute force attacks
442
+ 9. **Log authentication events** - Track successful and failed logins
443
+
444
+ ## Examples from miolo-sample
445
+
446
+ See actual implementations:
447
+ - `src/server/miolo/auth/passport.mjs` - Passport authentication config
448
+ - `src/server/io/db/users/auth.mjs` - User authentication queries
449
+ - `src/server/io/db/triggers/user.mjs` - Password hashing trigger
450
+ - `src/server/utils/crypt.mjs` - Hashing utilities