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.
- package/bin/build/build.mjs +53 -0
- package/bin/build/build_bin.mjs +17 -0
- package/bin/build/cli/client.mjs +52 -0
- package/bin/build/cli/css.mjs +22 -0
- package/bin/build/cli/index.mjs +40 -0
- package/bin/build/cli/ssr.mjs +21 -0
- package/bin/build/server/aliases.mjs +112 -0
- package/bin/build/server/babel.config.js +24 -0
- package/bin/build/server/banner.mjs +20 -0
- package/bin/build/server/bundle.mjs +111 -0
- package/bin/build/server/fix.mjs +15 -0
- package/bin/build/server/index.mjs +69 -0
- package/bin/build/server/options.mjs +83 -0
- package/bin/create/auth.mjs +23 -0
- package/bin/create/copy.mjs +175 -0
- package/bin/create/docker.mjs +25 -0
- package/bin/create/index.mjs +137 -0
- package/bin/create/pkgjson.mjs +72 -0
- package/bin/create/prepare-template.mjs +158 -0
- package/bin/create/validation.mjs +27 -0
- package/bin/dev/dev.mjs +32 -23
- package/bin/dev/dev_start.mjs +6 -5
- package/bin/index.mjs +94 -52
- package/bin/prod-bin/create-bin.mjs +42 -27
- package/bin/prod-bin/run.mjs +13 -9
- package/bin/{prod-run → run}/pid.mjs +4 -4
- package/bin/run/restart.mjs +13 -0
- package/bin/run/start.mjs +18 -0
- package/bin/run/stop.mjs +20 -0
- package/bin/util.mjs +35 -11
- package/package.json +59 -39
- package/src/config/.env +34 -12
- package/src/config/defaults.mjs +253 -185
- package/src/config/env.mjs +40 -22
- package/src/config/index.mjs +19 -24
- package/src/config/util.mjs +25 -10
- package/src/db-conn.mjs +34 -0
- package/src/engines/cron/emails.mjs +10 -5
- package/src/engines/cron/index.mjs +45 -51
- package/src/engines/cron/init.mjs +16 -17
- package/src/engines/cron/ipsum.mjs +65 -60
- package/src/engines/cron/syscheck.mjs +30 -30
- package/src/engines/emailer/index.mjs +1 -2
- package/src/engines/emailer/queue.mjs +14 -20
- package/src/engines/emailer/transporter.mjs +86 -74
- package/src/engines/geoip/index.mjs +23 -28
- package/src/engines/http/index.mjs +26 -15
- package/src/engines/logger/buildErrorEmailBody.mjs +72 -0
- package/src/engines/logger/index.mjs +114 -122
- package/src/engines/logger/injectStackTrace.mjs +59 -0
- package/src/engines/logger/logger_mail.mjs +47 -61
- package/src/engines/logger/reopenTransportOnHupSignal.mjs +12 -13
- package/src/engines/parser/Parser.mjs +77 -60
- package/src/engines/parser/index.mjs +1 -1
- package/src/engines/schema/diffObjs.mjs +41 -0
- package/src/engines/schema/index.mjs +4 -0
- package/src/engines/schema/input.mjs +54 -0
- package/src/engines/schema/output.mjs +66 -0
- package/src/engines/socket/index.mjs +44 -46
- package/src/index.mjs +15 -10
- package/src/middleware/auth/basic.mjs +41 -40
- package/src/middleware/auth/custom.mjs +10 -13
- package/src/middleware/auth/guest.mjs +27 -27
- package/src/middleware/auth/passport/index.mjs +374 -0
- package/src/middleware/auth/passport/session/index.mjs +43 -0
- package/src/middleware/auth/{credentials → passport}/session/store.mjs +35 -15
- package/src/middleware/auth/passport/session/store_koa_redis.mjs +3 -0
- package/src/middleware/context/cache/index.mjs +78 -33
- package/src/middleware/context/cache/options.mjs +19 -21
- package/src/middleware/context/db.mjs +45 -20
- package/src/middleware/context/index.mjs +12 -12
- package/src/middleware/extra.mjs +4 -5
- package/src/middleware/http/body.mjs +25 -25
- package/src/middleware/http/catcher.mjs +81 -8
- package/src/middleware/http/custom_blacklist.mjs +19 -16
- package/src/middleware/http/headers.mjs +37 -34
- package/src/middleware/http/ratelimit.mjs +16 -23
- package/src/middleware/http/request.mjs +60 -65
- package/src/middleware/routes/catch_js_error.mjs +30 -23
- package/src/middleware/routes/robots.mjs +4 -7
- package/src/middleware/routes/router/crud/attachCrudRoutes.mjs +108 -90
- package/src/middleware/routes/router/crud/getCrudConfig.mjs +31 -55
- package/src/middleware/routes/router/defaults.mjs +6 -19
- package/src/middleware/routes/router/index.mjs +17 -21
- package/src/middleware/routes/router/queries/attachQueriesRoutes.mjs +227 -50
- package/src/middleware/routes/router/queries/getQueriesConfig.mjs +45 -55
- package/src/middleware/routes/router/utils.mjs +41 -26
- package/src/middleware/ssr/context.mjs +5 -7
- package/src/middleware/ssr/html.mjs +66 -43
- package/src/middleware/ssr/loader.mjs +11 -14
- package/src/middleware/ssr/ssr_render.mjs +39 -22
- package/src/middleware/static/index.mjs +33 -14
- package/src/middleware/vite/devserver.mjs +38 -22
- package/src/middleware/vite/watcher.mjs +12 -14
- package/src/server-cron.mjs +13 -8
- package/src/server-dev.mjs +13 -16
- package/src/server.mjs +49 -51
- package/template/.agent/skills/miolo-app-arch/SKILL.md +218 -0
- package/template/.agent/skills/miolo-auth/SKILL.md +450 -0
- package/template/.agent/skills/miolo-cli-router/SKILL.md +394 -0
- package/template/.agent/skills/miolo-database/SKILL.md +358 -0
- package/template/.agent/skills/miolo-react-patterns/SKILL.md +426 -0
- package/template/.agent/skills/miolo-routing/SKILL.md +326 -0
- package/template/.agent/skills/miolo-schemas/SKILL.md +329 -0
- package/template/.agent/skills/miolo-session-context/SKILL.md +397 -0
- package/template/.agent/skills/miolo-ssr/SKILL.md +433 -0
- package/template/.editorconfig +18 -0
- package/template/.env +120 -0
- package/template/biome.json +63 -0
- package/template/components.json +21 -0
- package/template/db/init.sh +89 -0
- package/template/db/sql/00_drop.sql +2 -0
- package/template/db/sql/01_users.sql +31 -0
- package/template/db/sql/02_todos.sql +20 -0
- package/template/docker/Dockerfile +13 -0
- package/template/docker/docker-compose.yaml +79 -0
- package/template/gitignore +42 -0
- package/template/jsconfig.json +18 -0
- package/template/package.json +88 -0
- package/template/postcss.config.js +9 -0
- package/template/src/cli/App.jsx +25 -0
- package/template/src/cli/components/JsonTreeViewer.jsx +128 -0
- package/template/src/cli/components/shadcn-io/spinner/index.jsx +232 -0
- package/template/src/cli/components/stepper.jsx +408 -0
- package/template/src/cli/components/ui/avatar.jsx +36 -0
- package/template/src/cli/components/ui/badge.jsx +31 -0
- package/template/src/cli/components/ui/breadcrumb.jsx +97 -0
- package/template/src/cli/components/ui/card.jsx +73 -0
- package/template/src/cli/components/ui/collapsible.jsx +16 -0
- package/template/src/cli/components/ui/dropdown-menu.jsx +179 -0
- package/template/src/cli/components/ui/field.jsx +217 -0
- package/template/src/cli/components/ui/input.jsx +19 -0
- package/template/src/cli/components/ui/label.jsx +17 -0
- package/template/src/cli/components/ui/pagination.jsx +99 -0
- package/template/src/cli/components/ui/patched/alert.jsx +56 -0
- package/template/src/cli/components/ui/patched/button.jsx +45 -0
- package/template/src/cli/components/ui/patched/dialog.jsx +114 -0
- package/template/src/cli/components/ui/patched/sidebar.jsx +660 -0
- package/template/src/cli/components/ui/select.jsx +141 -0
- package/template/src/cli/components/ui/separator.jsx +21 -0
- package/template/src/cli/components/ui/sheet.jsx +115 -0
- package/template/src/cli/components/ui/skeleton.jsx +13 -0
- package/template/src/cli/components/ui/sonner.jsx +22 -0
- package/template/src/cli/components/ui/switch.jsx +25 -0
- package/template/src/cli/components/ui/table.jsx +88 -0
- package/template/src/cli/components/ui/textarea.jsx +16 -0
- package/template/src/cli/components/ui/tooltip.jsx +45 -0
- package/template/src/cli/config/store_keys.mjs +2 -0
- package/template/src/cli/context/data/DataContext.jsx +5 -0
- package/template/src/cli/context/data/DataProvider.jsx +44 -0
- package/template/src/cli/context/data/useBreads.mjs +15 -0
- package/template/src/cli/context/data/useDataContext.mjs +4 -0
- package/template/src/cli/context/session/SessionContext.mjs +4 -0
- package/template/src/cli/context/session/SessionProvider.jsx +31 -0
- package/template/src/cli/context/session/makePermissioner.mjs +34 -0
- package/template/src/cli/context/session/useSessionContext.mjs +6 -0
- package/template/src/cli/context/theme/ThemeContext.mjs +4 -0
- package/template/src/cli/context/theme/ThemeProvider.jsx +49 -0
- package/template/src/cli/context/theme/useThemeContext.mjs +6 -0
- package/template/src/cli/context/ui/UIContext.jsx +5 -0
- package/template/src/cli/context/ui/UIProvider.jsx +16 -0
- package/template/src/cli/context/ui/useUIContext.mjs +4 -0
- package/template/src/cli/context/util.mjs +17 -0
- package/template/src/cli/entry-cli.jsx +33 -0
- package/template/src/cli/hooks/useIsMobile.mjs +19 -0
- package/template/src/cli/hooks/useStoragedState.mjs +63 -0
- package/template/src/cli/index.html +29 -0
- package/template/src/cli/layout/app-sidebar.jsx +25 -0
- package/template/src/cli/layout/main-layout.jsx +63 -0
- package/template/src/cli/layout/nav-last-todos.jsx +72 -0
- package/template/src/cli/layout/nav-main.jsx +39 -0
- package/template/src/cli/layout/nav-user.jsx +105 -0
- package/template/src/cli/layout/prop-switcher.jsx +93 -0
- package/template/src/cli/lib/utils.mjs +10 -0
- package/template/src/cli/pages/Index.jsx +13 -0
- package/template/src/cli/pages/IndexOffline.jsx +13 -0
- package/template/src/cli/pages/IndexOnline.jsx +18 -0
- package/template/src/cli/pages/dash/Dashboard.jsx +29 -0
- package/template/src/cli/pages/offline/Login.jsx +43 -0
- package/template/src/cli/pages/offline/LoginForm.jsx +115 -0
- package/template/src/cli/pages/security/Security.jsx +39 -0
- package/template/src/cli/pages/security/SecurityForm.jsx +106 -0
- package/template/src/cli/pages/todos/TodoActions.jsx +99 -0
- package/template/src/cli/pages/todos/TodoAdd.jsx +43 -0
- package/template/src/cli/pages/todos/TodoList.jsx +60 -0
- package/template/src/cli/pages/todos/Todos.jsx +23 -0
- package/template/src/cli/pages/todos/context/TodosContext.jsx +5 -0
- package/template/src/cli/pages/todos/context/TodosProvider.jsx +191 -0
- package/template/src/cli/pages/todos/context/useTodosContext.mjs +4 -0
- package/template/src/ns/models/Todo.mjs +29 -0
- package/template/src/ns/models/TodoList.mjs +8 -0
- package/template/src/ns/models/User.mjs +40 -0
- package/template/src/server/bot/check_today.mjs +10 -0
- package/template/src/server/io/cache/base.mjs +21 -0
- package/template/src/server/io/db/filter.mjs +92 -0
- package/template/src/server/io/db/todos/delete.mjs +29 -0
- package/template/src/server/io/db/todos/find.mjs +13 -0
- package/template/src/server/io/db/todos/read.mjs +83 -0
- package/template/src/server/io/db/todos/toggle.mjs +37 -0
- package/template/src/server/io/db/todos/upsave.mjs +32 -0
- package/template/src/server/io/db/triggers/user.mjs +13 -0
- package/template/src/server/io/db/users/auth.mjs +132 -0
- package/template/src/server/io/db/users/pwd.mjs +38 -0
- package/template/src/server/io/db/users/save.mjs +17 -0
- package/template/src/server/miolo/auth/basic.mjs +15 -0
- package/template/src/server/miolo/auth/guest.mjs +3 -0
- package/template/src/server/miolo/auth/passport.mjs +73 -0
- package/template/src/server/miolo/cache.mjs +11 -0
- package/template/src/server/miolo/cron/foo.mjs +7 -0
- package/template/src/server/miolo/cron/index.mjs +28 -0
- package/template/src/server/miolo/cron/invalidate.mjs +21 -0
- package/template/src/server/miolo/db.mjs +36 -0
- package/template/src/server/miolo/http.mjs +14 -0
- package/template/src/server/miolo/index.mjs +43 -0
- package/template/src/server/miolo/routes/crud.mjs +16 -0
- package/template/src/server/miolo/routes/index.mjs +8 -0
- package/template/src/server/miolo/ssr/entry-server.jsx +13 -0
- package/template/src/server/miolo/ssr/loader.mjs +18 -0
- package/template/src/server/routes/index.mjs +66 -0
- package/template/src/server/routes/todos/mod.mjs +52 -0
- package/template/src/server/routes/todos/read.mjs +45 -0
- package/template/src/server/routes/todos/special.mjs +47 -0
- package/template/src/server/routes/users/user.mjs +54 -0
- package/template/src/server/server.mjs +10 -0
- package/template/src/server/utils/crypt.mjs +38 -0
- package/template/src/server/utils/io.mjs +15 -0
- package/template/src/server/utils/pwdfor.mjs +25 -0
- package/template/src/server/utils/schema.mjs +22 -0
- package/template/src/static/img/default/profile.png +0 -0
- package/template/src/static/img/favicon.ico +0 -0
- package/template/src/static/img/miolo_logo.png +0 -0
- package/template/src/static/img/miolo_name.png +0 -0
- package/template/src/static/public/manifest.json +21 -0
- package/template/src/static/public/sw.js +79 -0
- package/template/src/static/style/globals.css +156 -0
- package/template/src/static/style/json-tree.css +54 -0
- package/template/src/static/style/skeleton.css +49 -0
- package/bin/prod-build/build-client.mjs +0 -67
- package/bin/prod-build/build-server.mjs +0 -58
- package/bin/prod-run/restart.mjs +0 -9
- package/bin/prod-run/start.mjs +0 -15
- package/bin/prod-run/stop.mjs +0 -20
- package/src/engines/logger/verify.mjs +0 -22
- package/src/middleware/auth/credentials/index.mjs +0 -151
- package/src/middleware/auth/credentials/session/index.mjs +0 -24
- 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
|