@jskit-ai/agent-docs 0.1.61 → 0.1.63
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/guide/agent/app-setup/authentication.md +100 -155
- package/guide/agent/app-setup/database-layer.md +24 -34
- package/guide/agent/app-setup/initial-scaffolding.md +3 -8
- package/guide/agent/app-setup/quickstart.md +4 -9
- package/guide/agent/app-setup/users.md +39 -30
- package/guide/agent/app-setup/working-with-the-jskit-cli.md +13 -11
- package/package.json +1 -1
- package/reference/autogen/README.md +1 -0
- package/reference/autogen/packages/auth-core.md +73 -0
- package/reference/autogen/packages/auth-provider-local-core.md +112 -0
- package/reference/autogen/packages/auth-provider-supabase-core.md +3 -12
- package/reference/autogen/packages/auth-web.md +30 -1
- package/reference/autogen/tooling/jskit-cli.md +5 -0
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
# Authentication
|
|
4
4
|
|
|
5
|
-
At the end of the previous chapter, the app had a real shell, but it still did not know how to sign users in. In this chapter, we
|
|
5
|
+
At the end of the previous chapter, the app had a real shell, but it still did not know how to sign users in. In this chapter, we add JSKIT's local auth provider, the stock login and sign-out routes, and the files that make authentication visible in the shell.
|
|
6
6
|
|
|
7
|
-
This chapter still does **not** add the database-backed users layer. That is intentional.
|
|
7
|
+
This chapter still does **not** add Supabase or the database-backed users layer. That is intentional. Keep authentication basic until the app's main product workflows exist, then add Supabase, OAuth, OTP, provider linking, profile projection, or workspace/account complexity only when those features are actually needed.
|
|
8
8
|
|
|
9
9
|
To get back to the same starting point as the end of the previous chapter, run:
|
|
10
10
|
|
|
@@ -16,56 +16,22 @@ npm install
|
|
|
16
16
|
|
|
17
17
|
If you are already continuing from the previous chapter, you are already in the right place and can skip that setup.
|
|
18
18
|
|
|
19
|
-
**
|
|
19
|
+
**Default: Start With Local Auth**
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
The default auth path does not require a Supabase project or a database. It uses `auth-local`, which stores local credentials and sessions in `.jskit/auth/` by default.
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
2. Open `Project Settings -> API`. Copy:
|
|
25
|
-
- the **Project URL**
|
|
26
|
-
- the **publishable** key
|
|
27
|
-
3. Open `Authentication -> URL Configuration`.
|
|
28
|
-
4. Set **Site URL** to:
|
|
29
|
-
|
|
30
|
-
```text
|
|
31
|
-
http://localhost:5173
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
5. Add this local redirect URL:
|
|
35
|
-
|
|
36
|
-
```text
|
|
37
|
-
http://localhost:5173/auth/login
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
JSKIT uses the project URL as `AUTH_SUPABASE_URL`, the publishable key as `AUTH_SUPABASE_PUBLISHABLE_KEY`, and the browser address as `APP_PUBLIC_URL`.
|
|
41
|
-
|
|
42
|
-
Use the **publishable** key here, not the secret key and not a service-role key. If you later run the app on a different host or port, update both Supabase's URL settings and `APP_PUBLIC_URL` to match the real browser URL exactly.
|
|
43
|
-
|
|
44
|
-
If you are guiding someone interactively, ask plainly for these exact values:
|
|
45
|
-
|
|
46
|
-
- `AUTH_SUPABASE_URL`
|
|
47
|
-
- `AUTH_SUPABASE_PUBLISHABLE_KEY`
|
|
48
|
-
- whether `APP_PUBLIC_URL` should stay `http://localhost:5173`
|
|
49
|
-
|
|
50
|
-
Do not hide behind vague language like "send the auth credentials later." In this local guide flow, those are routine setup values for the Supabase auth install step.
|
|
23
|
+
That is the recommended first step for most apps. After the app's core flow is working, switch to Supabase or add richer auth methods only if the product needs them.
|
|
51
24
|
|
|
52
25
|
## Installing the auth layer
|
|
53
26
|
|
|
54
27
|
From inside `exampleapp`, run:
|
|
55
28
|
|
|
56
29
|
```bash
|
|
57
|
-
npx jskit add
|
|
58
|
-
--auth-supabase-url "https://YOUR-PROJECT.supabase.co" \
|
|
59
|
-
--auth-supabase-publishable-key "sb_publishable_..." \
|
|
60
|
-
--app-public-url "http://localhost:5173"
|
|
61
|
-
|
|
62
|
-
npx jskit add bundle auth-base
|
|
30
|
+
npx jskit add bundle auth-local
|
|
63
31
|
npm install
|
|
64
32
|
```
|
|
65
33
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
The second command adds the web auth layer. `auth-base` is a small bundle, not a black box: in practice it adds `auth-core` and `auth-web`, which together provide the auth routes, the auth surface, and the stock login and sign-out screens.
|
|
34
|
+
`auth-local` installs the provider-neutral auth core, the auth web layer, and the local provider. It gives the app working register, login, logout, session, and password recovery flows without requiring an external auth service.
|
|
69
35
|
|
|
70
36
|
The final `npm install` matters for the same reason it did in the shell chapter: `jskit add` rewrites the scaffold and updates `package.json`, but `npm install` is what actually downloads the newly referenced runtime packages.
|
|
71
37
|
|
|
@@ -80,12 +46,11 @@ npm run server
|
|
|
80
46
|
|
|
81
47
|
Then open `http://localhost:5173/auth/login` in the browser.
|
|
82
48
|
|
|
83
|
-
The login page is real
|
|
49
|
+
The login page is real and renders only the modes supported by the active provider.
|
|
84
50
|
|
|
85
51
|
- **Sign in** is the normal email-and-password flow.
|
|
86
|
-
- **Register** creates a
|
|
87
|
-
- **Forgot password?** requests a password reset
|
|
88
|
-
- **Use one-time code** switches to an email OTP login flow.
|
|
52
|
+
- **Register** creates a local auth user.
|
|
53
|
+
- **Forgot password?** requests a password reset link when recovery is configured.
|
|
89
54
|
- **Remember this account on this device** stores a small local hint in browser storage so the next visit can greet the last-used account and let the user keep that email preselected.
|
|
90
55
|
|
|
91
56
|
If you go back to `http://localhost:5173/home`, the shell also has a small auth widget in the status area. When you are signed out it shows a guest state and a menu entry that leads to `/auth/login`. After you sign in, the same placement changes to a sign-out menu.
|
|
@@ -107,7 +72,7 @@ This is the first screen you see.
|
|
|
107
72
|
The small links under the password field are not decoration.
|
|
108
73
|
|
|
109
74
|
- `Forgot password?` switches the card into password-reset-request mode.
|
|
110
|
-
- `Use one-time code` switches the card into OTP mode.
|
|
75
|
+
- `Use one-time code` switches the card into OTP mode when the active provider supports OTP. The local provider does not show this link.
|
|
111
76
|
|
|
112
77
|
### Register mode
|
|
113
78
|
|
|
@@ -117,16 +82,16 @@ When the user presses `Register`, the form changes in three important ways.
|
|
|
117
82
|
- A `Confirm password` field appears.
|
|
118
83
|
- The main submit button changes from `Sign in` to `Register`.
|
|
119
84
|
|
|
120
|
-
Pressing `Register` sends a sign-up request
|
|
85
|
+
Pressing `Register` sends a sign-up request through `POST /api/register`. With the local provider, JSKIT writes the local auth record and creates a session immediately.
|
|
121
86
|
|
|
122
|
-
What happens next depends on
|
|
87
|
+
What happens next depends on the active provider's capabilities.
|
|
123
88
|
|
|
124
|
-
- If email confirmation is required,
|
|
89
|
+
- If email confirmation is required, the provider creates the user but does not return a live session. The card then switches to a dedicated confirmation state.
|
|
125
90
|
- If email confirmation is not required, the user is registered and signed in immediately.
|
|
126
91
|
|
|
127
92
|
### Email-confirmation state
|
|
128
93
|
|
|
129
|
-
If
|
|
94
|
+
If the active provider requires confirmation, the screen changes again. The local provider does not require email confirmation, so most first apps will not see this mode.
|
|
130
95
|
|
|
131
96
|
- The mode-switch buttons disappear.
|
|
132
97
|
- The card shows a confirmation message instead of the email/password fields.
|
|
@@ -134,11 +99,11 @@ If Supabase requires confirmation, the screen changes again.
|
|
|
134
99
|
- `Resend confirmation email` calls `POST /api/register/confirmation/resend`.
|
|
135
100
|
- `Back to sign in` returns to the normal login mode.
|
|
136
101
|
|
|
137
|
-
This matters because a junior developer might otherwise assume registration failed. In reality, the
|
|
102
|
+
This matters because a junior developer might otherwise assume registration failed. In reality, the provider may already have created the auth identity; the browser just does not have an active session yet.
|
|
138
103
|
|
|
139
104
|
### One-time-code mode
|
|
140
105
|
|
|
141
|
-
`Use one-time code` changes the form again.
|
|
106
|
+
When a provider supports OTP, `Use one-time code` changes the form again.
|
|
142
107
|
|
|
143
108
|
- The password field disappears.
|
|
144
109
|
- A `One-time code` field appears.
|
|
@@ -150,7 +115,7 @@ Those two buttons do different jobs.
|
|
|
150
115
|
- `Send one-time code` requests the email through `POST /api/login/otp/request`.
|
|
151
116
|
- `Verify code` submits the code through `POST /api/login/otp/verify`.
|
|
152
117
|
|
|
153
|
-
|
|
118
|
+
The local provider does not implement OTP. Supabase and future providers can expose this mode through the same capability contract. In other words, OTP is an opt-in sign-in method, not something the first app needs up front.
|
|
154
119
|
|
|
155
120
|
### Password-reset-request mode
|
|
156
121
|
|
|
@@ -160,9 +125,9 @@ JSKIT asks Supabase to send OTP login emails only for existing users. In other w
|
|
|
160
125
|
- The main button changes to `Send reset instructions`.
|
|
161
126
|
- Submitting this screen calls `POST /api/password/forgot`.
|
|
162
127
|
|
|
163
|
-
That endpoint asks
|
|
128
|
+
That endpoint asks the active provider to start password recovery. With the local provider, SMTP is used when configured; otherwise local development can log or return the recovery URL depending on environment settings.
|
|
164
129
|
|
|
165
|
-
The
|
|
130
|
+
The recovery link opens `/auth/reset-password`. That page exchanges the recovery token for a short-lived recovery-scoped session, lets the user choose a new password, and then clears the recovery session.
|
|
166
131
|
|
|
167
132
|
### Remembered account behavior
|
|
168
133
|
|
|
@@ -179,22 +144,22 @@ On the next visit, the card can show a `Welcome back, ...` panel and a `Use anot
|
|
|
179
144
|
|
|
180
145
|
### OAuth buttons
|
|
181
146
|
|
|
182
|
-
The screen is also ready to show OAuth provider buttons, but only if providers are configured.
|
|
147
|
+
The screen is also ready to show OAuth provider buttons, but only if the active provider supports OAuth and providers are configured. Local auth intentionally keeps this off.
|
|
183
148
|
|
|
184
|
-
For
|
|
149
|
+
For a later Supabase setup, `config.server.js` can keep this empty at first:
|
|
185
150
|
|
|
186
151
|
```js
|
|
187
152
|
config.auth ||= {};
|
|
188
|
-
config.auth.profileMode = "
|
|
153
|
+
config.auth.profileMode = "provider";
|
|
189
154
|
config.auth.oauth = {
|
|
190
155
|
providers: [],
|
|
191
156
|
defaultProvider: ""
|
|
192
157
|
};
|
|
193
158
|
```
|
|
194
159
|
|
|
195
|
-
That is why the login card
|
|
160
|
+
That is why the login card does not show buttons like `Continue with Google`.
|
|
196
161
|
|
|
197
|
-
|
|
162
|
+
After switching to a provider that supports OAuth, turning on Google has two separate setup steps.
|
|
198
163
|
|
|
199
164
|
First, configure Google and Supabase:
|
|
200
165
|
|
|
@@ -219,67 +184,53 @@ config.auth.oauth = {
|
|
|
219
184
|
|
|
220
185
|
**Important: What Works Without A Database**
|
|
221
186
|
|
|
222
|
-
Authentication is already real in this chapter because
|
|
187
|
+
Authentication is already real in this chapter because the local provider owns the auth data it needs:
|
|
223
188
|
|
|
224
|
-
-
|
|
225
|
-
-
|
|
226
|
-
-
|
|
227
|
-
-
|
|
189
|
+
- local auth user records
|
|
190
|
+
- password hashes
|
|
191
|
+
- recovery tokens
|
|
192
|
+
- access and refresh sessions
|
|
193
|
+
- the HTTP-only cookies JSKIT writes for the browser
|
|
228
194
|
|
|
229
|
-
What is missing is JSKIT's own database-backed users layer.
|
|
195
|
+
What is missing is JSKIT's own database-backed users layer. That is intentional for the first app. Login should not require MySQL, Postgres, Supabase, or a generated users table before the product itself exists.
|
|
230
196
|
|
|
231
197
|
Concretely, that means:
|
|
232
198
|
|
|
233
|
-
- JSKIT
|
|
234
|
-
-
|
|
235
|
-
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
- `authProviderUserSid`
|
|
239
|
-
- `email`
|
|
240
|
-
- `displayName`
|
|
241
|
-
- JSKIT also keeps a tiny in-memory user-settings record for auth-related flags such as:
|
|
242
|
-
- `passwordSignInEnabled`
|
|
243
|
-
- `passwordSetupRequired`
|
|
199
|
+
- JSKIT can register a user.
|
|
200
|
+
- JSKIT can sign that user in and out.
|
|
201
|
+
- JSKIT can read the current session.
|
|
202
|
+
- JSKIT can request and complete password recovery.
|
|
203
|
+
- JSKIT can run without any JSKIT database runtime package.
|
|
244
204
|
|
|
245
|
-
|
|
205
|
+
By default, the local provider stores its runtime state under `.jskit/auth/`:
|
|
246
206
|
|
|
247
|
-
-
|
|
248
|
-
-
|
|
249
|
-
-
|
|
250
|
-
-
|
|
207
|
+
- `users.passwd`
|
|
208
|
+
- `sessions.passwd`
|
|
209
|
+
- `recovery.passwd`
|
|
210
|
+
- `session.secret`
|
|
251
211
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
That last point is the important difference. A restart does **not** delete the Supabase user. It does **not** erase the real password. It does **not** erase the real auth session in Supabase itself. What it clears is only the app's temporary in-memory mirror. On the next authenticated request, JSKIT rebuilds that mirror from the Supabase user or token claims.
|
|
212
|
+
Those files are local runtime state, not generated application code. They are ignored by git.
|
|
255
213
|
|
|
256
214
|
So without a database, you still get:
|
|
257
215
|
|
|
258
216
|
- real login
|
|
259
217
|
- real logout
|
|
260
218
|
- real registration
|
|
261
|
-
- real password reset requests
|
|
262
|
-
- real OTP and OAuth flows
|
|
219
|
+
- real password reset requests and completions
|
|
263
220
|
- real session cookies
|
|
264
221
|
|
|
265
222
|
But you do **not** get:
|
|
266
223
|
|
|
267
224
|
- persistent JSKIT-side user rows
|
|
268
|
-
- persistent JSKIT-side
|
|
269
|
-
-
|
|
225
|
+
- persistent JSKIT-side account settings
|
|
226
|
+
- OAuth, OTP, or provider linking from the local provider
|
|
270
227
|
- workspace membership, user preferences, or the later users/workspaces data model
|
|
271
228
|
|
|
272
|
-
Later, when the
|
|
273
|
-
|
|
274
|
-
That service exposes three main functions:
|
|
229
|
+
Later, when an app needs the database-backed users layer, `users-core` can provide `auth.profile.projector` through `users.profile.sync.service`.
|
|
275
230
|
|
|
276
|
-
|
|
277
|
-
- `upsertByIdentity(...)` to create or update the JSKIT-side user record directly
|
|
278
|
-
- `syncIdentityProfile(...)` to run the normal auth-driven synchronization flow
|
|
231
|
+
That service projects a provider identity into the app-owned user/profile model. Local auth, Supabase auth, and future auth providers can all use that same projection seam.
|
|
279
232
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
So this chapter gives you real authentication, but only a temporary app-side user mirror. The full persistent JSKIT user model comes later with the database and users layers.
|
|
233
|
+
So this chapter gives you real authentication, but it deliberately keeps the user model small. The full persistent JSKIT user model comes later with the database and users layers.
|
|
283
234
|
|
|
284
235
|
## Using auth in your own app
|
|
285
236
|
|
|
@@ -517,7 +468,7 @@ At this point the guide has shown three distinct layers of client state:
|
|
|
517
468
|
|
|
518
469
|
That progression is intentional. Packages keep their operational runtimes internally, but the app-facing shared state they surface to Vue code is store-based.
|
|
519
470
|
|
|
520
|
-
## What `auth-
|
|
471
|
+
## What `auth-local` adds to the app
|
|
521
472
|
|
|
522
473
|
The interesting part of this chapter is that authentication appears in several different layers at once: environment config, public routing config, shell placements, and app-owned view wrappers.
|
|
523
474
|
|
|
@@ -532,7 +483,7 @@ The first new place to inspect is `package.json`:
|
|
|
532
483
|
},
|
|
533
484
|
"dependencies": {
|
|
534
485
|
"@jskit-ai/auth-core": "0.x",
|
|
535
|
-
"@jskit-ai/auth-provider-
|
|
486
|
+
"@jskit-ai/auth-provider-local-core": "0.x",
|
|
536
487
|
"@jskit-ai/auth-web": "0.x"
|
|
537
488
|
}
|
|
538
489
|
}
|
|
@@ -540,20 +491,25 @@ The first new place to inspect is `package.json`:
|
|
|
540
491
|
|
|
541
492
|
Three things are worth noticing immediately.
|
|
542
493
|
|
|
543
|
-
- `auth-
|
|
494
|
+
- `auth-core` owns the provider-neutral contract, shared actions, policy hooks, and capability normalization.
|
|
495
|
+
- `auth-provider-local-core` is the selected provider-specific runtime.
|
|
544
496
|
- `auth-web` is the part that adds the web routes and the default auth UI.
|
|
545
497
|
- there is an `auth` surface-specific dev/build script family, just as `home` already had.
|
|
546
498
|
|
|
547
|
-
The provider
|
|
499
|
+
The provider package also writes local auth settings into `.env`:
|
|
548
500
|
|
|
549
501
|
```dotenv
|
|
550
|
-
AUTH_PROVIDER=
|
|
551
|
-
|
|
552
|
-
AUTH_SUPABASE_PUBLISHABLE_KEY=sb_publishable_...
|
|
553
|
-
APP_PUBLIC_URL=http://localhost:5173
|
|
502
|
+
AUTH_PROVIDER=local
|
|
503
|
+
AUTH_LOCAL_BACKEND=file
|
|
554
504
|
```
|
|
555
505
|
|
|
556
|
-
|
|
506
|
+
`AUTH_PROVIDER=local` is the selected-provider marker. `AUTH_LOCAL_BACKEND=file` tells the local provider to use its built-in file backend. No database URL, Supabase URL, or provider key is needed for this chapter.
|
|
507
|
+
|
|
508
|
+
The backend selection is intentionally local-provider-specific. `auth.local.backend` is the public server container token for replacing the file store later; Supabase and other external providers do not use it. A custom local backend must expose a single `withTransaction(callback)` method. JSKIT calls that method for register, login, recovery, password reset, profile update, session revocation, and security operations, and the callback receives transactional `users`, `sessions`, and `recovery` repositories.
|
|
509
|
+
|
|
510
|
+
The built-in file backend is for low-friction local development, first deploys with one persistent Node instance, and simple apps that accept that storage model. It writes `.jskit/auth/*.passwd` files behind a store-level lock. Do not treat it as horizontally scalable or serverless-safe storage; when the app needs replicated auth storage, register a custom `auth.local.backend` that uses the app's chosen database runtime.
|
|
511
|
+
|
|
512
|
+
If you later configure local SMTP password recovery, also set `APP_PUBLIC_URL` so reset links point back to the browser URL for this app.
|
|
557
513
|
|
|
558
514
|
Public routing config changes too. `config/public.js` has a second surface:
|
|
559
515
|
|
|
@@ -571,18 +527,9 @@ config.surfaceDefinitions.auth = {
|
|
|
571
527
|
|
|
572
528
|
That `requiresAuth: false` line is important. The auth surface must stay public, otherwise users would need to be logged in before they could reach the login page.
|
|
573
529
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
```js
|
|
577
|
-
config.auth ||= {};
|
|
578
|
-
config.auth.profileMode = "standalone";
|
|
579
|
-
config.auth.oauth = {
|
|
580
|
-
providers: [],
|
|
581
|
-
defaultProvider: ""
|
|
582
|
-
};
|
|
583
|
-
```
|
|
530
|
+
The local provider does not need an app database profile mode or OAuth config. The stock login UI asks `/api/session` for the active provider capabilities and renders only what the selected provider actually supports. With `auth-local`, that means email/password login, registration, sign-out, session refresh, and password recovery when recovery is configured.
|
|
584
531
|
|
|
585
|
-
|
|
532
|
+
Later, if you switch to Supabase, the Supabase provider can append the app-owned OAuth visibility config and profile mode settings it needs.
|
|
586
533
|
|
|
587
534
|
The auth routes themselves are app-owned wrappers around the module-supplied default views. `src/pages/auth/login.vue` looks like this:
|
|
588
535
|
|
|
@@ -658,9 +605,8 @@ This is the shell placement system from the previous chapter doing real work aga
|
|
|
658
605
|
|
|
659
606
|
So the auth story in this chapter is spread across clear responsibilities:
|
|
660
607
|
|
|
661
|
-
- `.env`
|
|
608
|
+
- `.env` selects the local provider and its backend
|
|
662
609
|
- `config/public.js` declares an `auth` surface
|
|
663
|
-
- `config/server.js` exposes app-owned OAuth visibility settings
|
|
664
610
|
- `src/pages/auth/*` gives the app real public auth routes
|
|
665
611
|
- `src/placement.js` makes auth visible in the shell
|
|
666
612
|
|
|
@@ -731,24 +677,23 @@ For ordinary Vue component code there is usually no advantage to writing it this
|
|
|
731
677
|
|
|
732
678
|
### Who actually talks to whom
|
|
733
679
|
|
|
734
|
-
The most important thing to understand is that the browser
|
|
680
|
+
The most important thing to understand is that the browser talks to **your app**, and the app talks to the **selected auth provider** through the provider-neutral `authService` contract.
|
|
735
681
|
|
|
736
|
-
|
|
682
|
+
With the local provider, the selected provider is in the same Node process. It verifies passwords, writes sessions, and stores local auth state through `auth.local.backend`. The default backend writes the `.jskit/auth/*.passwd` files; a custom backend can use a database later without changing the login UI or the shared auth routes.
|
|
737
683
|
|
|
738
|
-
That means there are
|
|
684
|
+
That means there are two actors in the first-app flow:
|
|
739
685
|
|
|
740
686
|
- the browser, which renders the login screen and submits forms
|
|
741
|
-
- the JSKIT app server, which owns `/api/login`, `/api/
|
|
742
|
-
- Supabase, which owns the real authentication backend, password verification, OAuth exchange, and auth user records
|
|
687
|
+
- the JSKIT app server, which owns `/api/login`, `/api/register`, `/api/password/*`, `/api/session`, the selected provider, and the cookie-writing step
|
|
743
688
|
|
|
744
|
-
So the mental model
|
|
689
|
+
So the default local mental model is:
|
|
745
690
|
|
|
746
691
|
```text
|
|
747
|
-
browser -> JSKIT app ->
|
|
748
|
-
browser <- JSKIT app <-
|
|
692
|
+
browser -> JSKIT app -> local auth provider
|
|
693
|
+
browser <- JSKIT app <- local auth provider
|
|
749
694
|
```
|
|
750
695
|
|
|
751
|
-
|
|
696
|
+
If you later switch to Supabase, the same app routes still exist, but the selected provider implementation delegates to Supabase Auth.
|
|
752
697
|
|
|
753
698
|
### Password login: the full round trip
|
|
754
699
|
|
|
@@ -774,20 +719,19 @@ From there, the server-side flow is:
|
|
|
774
719
|
1. `POST /api/login` hits the route registered by `auth-web`.
|
|
775
720
|
2. `AuthController.login()` receives the request.
|
|
776
721
|
3. `AuthWebService.login()` executes the internal action `auth.login.password`.
|
|
777
|
-
4.
|
|
778
|
-
5.
|
|
779
|
-
6. JSKIT
|
|
780
|
-
7.
|
|
781
|
-
8. The API response sent back to the browser is intentionally small.
|
|
722
|
+
4. `auth-core` routes that action to the selected provider's `authService.login(...)`.
|
|
723
|
+
5. The local provider checks the password hash in its backend and creates a session record.
|
|
724
|
+
6. JSKIT writes the access and refresh tokens into HTTP-only cookies.
|
|
725
|
+
7. The API response sent back to the browser is intentionally small.
|
|
782
726
|
|
|
783
|
-
The important detail is step
|
|
727
|
+
The important detail is step 6. The browser does **not** receive raw session tokens as normal application state. The server writes them into HTTP-only cookies instead. With the local provider, those cookies are:
|
|
784
728
|
|
|
785
|
-
- `
|
|
786
|
-
- `
|
|
729
|
+
- `jskit_local_access_token`
|
|
730
|
+
- `jskit_local_refresh_token`
|
|
787
731
|
|
|
788
732
|
Those cookies are HTTP-only and `sameSite: "lax"`, so the browser sends them back automatically on later requests, but normal client-side code cannot read them directly.
|
|
789
733
|
|
|
790
|
-
The JSON response from `/api/login`
|
|
734
|
+
The JSON response from `/api/login` stays small:
|
|
791
735
|
|
|
792
736
|
```json
|
|
793
737
|
{
|
|
@@ -802,17 +746,17 @@ So the real password-login round trip is:
|
|
|
802
746
|
|
|
803
747
|
```text
|
|
804
748
|
1. browser -> POST /api/login -> JSKIT app
|
|
805
|
-
2. JSKIT app ->
|
|
806
|
-
3.
|
|
749
|
+
2. JSKIT app -> selected auth provider login
|
|
750
|
+
3. selected auth provider -> JSKIT app: profile + session
|
|
807
751
|
4. JSKIT app -> browser: set HTTP-only cookies + { ok, username }
|
|
808
752
|
5. browser -> GET /api/session
|
|
809
|
-
6. JSKIT app -> browser: { authenticated, username, csrfToken, ... }
|
|
753
|
+
6. JSKIT app -> browser: { authenticated, username, csrfToken, authCapabilities, ... }
|
|
810
754
|
7. browser redirects to the requested route
|
|
811
755
|
```
|
|
812
756
|
|
|
813
757
|
### OAuth login: the extra browser bounce
|
|
814
758
|
|
|
815
|
-
OAuth is the case where the browser really does leave the app briefly, but the app still owns the edges of the flow.
|
|
759
|
+
Local auth does not expose OAuth buttons. If you later switch to Supabase and configure OAuth providers, OAuth is the case where the browser really does leave the app briefly, but the app still owns the edges of the flow.
|
|
816
760
|
|
|
817
761
|
The first step is still browser -> app. If the login page shows a button such as `Continue with Google`, clicking it does **not** go straight to Supabase. It first goes to:
|
|
818
762
|
|
|
@@ -903,8 +847,8 @@ That is why the login page needs both browser-side logic and server-side routes.
|
|
|
903
847
|
When the browser calls it, the server:
|
|
904
848
|
|
|
905
849
|
- reads the auth cookies
|
|
906
|
-
-
|
|
907
|
-
- refreshes the
|
|
850
|
+
- asks the selected provider to authenticate the request
|
|
851
|
+
- refreshes or reissues provider cookies when the provider returns a refreshed session
|
|
908
852
|
- clears invalid cookies if the session is no longer usable
|
|
909
853
|
- returns the auth state the client actually needs
|
|
910
854
|
|
|
@@ -915,6 +859,7 @@ The response is shaped roughly like this:
|
|
|
915
859
|
"authenticated": true,
|
|
916
860
|
"username": "alice",
|
|
917
861
|
"csrfToken": "...",
|
|
862
|
+
"authCapabilities": { "...": "..." },
|
|
918
863
|
"oauthProviders": [],
|
|
919
864
|
"oauthDefaultProvider": null
|
|
920
865
|
}
|
|
@@ -927,11 +872,11 @@ That explains why the login screen and auth guard runtime both care about `/api/
|
|
|
927
872
|
- which OAuth buttons to render
|
|
928
873
|
- which CSRF token to use for later writes
|
|
929
874
|
|
|
930
|
-
It is also why the shell widget can react cleanly to auth state without storing raw session tokens in client state. The browser just asks the app for the current session view, and the app derives that from its cookies plus
|
|
875
|
+
It is also why the shell widget can react cleanly to auth state without storing raw session tokens in client state. The browser just asks the app for the current session view, and the app derives that from its cookies plus the selected provider.
|
|
931
876
|
|
|
932
877
|
### Authenticated Playwright testing with the dev auth bypass
|
|
933
878
|
|
|
934
|
-
JSKIT ships a development-only auth bootstrap path specifically so authenticated UI can be verified in Playwright without depending on a real live login flow through
|
|
879
|
+
JSKIT ships a development-only auth bootstrap path specifically so authenticated UI can be verified in Playwright without depending on a real live login flow through an external auth provider.
|
|
935
880
|
|
|
936
881
|
This is the standard path the agent should use for authenticated browser tests:
|
|
937
882
|
|
|
@@ -1060,16 +1005,16 @@ That flow is preferable to driving the real sign-in form in feature tests becaus
|
|
|
1060
1005
|
- use the local dev auth bypass or another local session bootstrap path
|
|
1061
1006
|
- exercise the actual changed behavior, not only page load
|
|
1062
1007
|
|
|
1063
|
-
##
|
|
1008
|
+
## When you later switch to Supabase
|
|
1064
1009
|
|
|
1065
|
-
|
|
1010
|
+
This chapter does not create anything in Supabase. If you later switch the selected provider from local auth to Supabase, keep separating **Supabase auth data** from **JSKIT app-owned data**.
|
|
1066
1011
|
|
|
1067
|
-
When a user registers
|
|
1012
|
+
When a user registers through a Supabase-backed app, Supabase creates a real auth user. According to Supabase's user-management docs, you can see users in two places.
|
|
1068
1013
|
|
|
1069
1014
|
- `Authentication -> Users` in the Supabase dashboard
|
|
1070
1015
|
- the `auth` schema in the Table Editor
|
|
1071
1016
|
|
|
1072
|
-
In practice, after someone registers you should expect to see at least these things on the Supabase side.
|
|
1017
|
+
In practice, after someone registers against Supabase you should expect to see at least these things on the Supabase side.
|
|
1073
1018
|
|
|
1074
1019
|
- A user row exists in `auth.users`.
|
|
1075
1020
|
- The email address appears there.
|
|
@@ -1079,14 +1024,14 @@ In practice, after someone registers you should expect to see at least these thi
|
|
|
1079
1024
|
|
|
1080
1025
|
On the Dashboard's `Authentication -> Users` page, that usually means you will see a new user entry with the email address, creation time, last sign-in information once they have signed in, and confirmation state. If you open the user details, you can inspect the auth record more closely.
|
|
1081
1026
|
|
|
1082
|
-
JSKIT's register flow also sends a starter `display_name` into Supabase user metadata. That means the new user can carry an initial display-name value in provider metadata even before the later database-backed users layer is installed.
|
|
1027
|
+
JSKIT's Supabase register flow also sends a starter `display_name` into Supabase user metadata. That means the new user can carry an initial display-name value in provider metadata even before the later database-backed users layer is installed.
|
|
1083
1028
|
|
|
1084
1029
|
This is the key distinction for the chapter.
|
|
1085
1030
|
|
|
1086
|
-
- Supabase
|
|
1087
|
-
- JSKIT
|
|
1031
|
+
- Supabase owns the real auth user record.
|
|
1032
|
+
- JSKIT app-owned profile/settings rows are separate and only exist after you install the users/database layer.
|
|
1088
1033
|
|
|
1089
|
-
So if you restart the local JSKIT server
|
|
1034
|
+
So if you restart the local JSKIT server after switching to Supabase, the actual Supabase auth user is still there because that data lives in Supabase, not in your local Node process.
|
|
1090
1035
|
|
|
1091
1036
|
One more subtle point matters here.
|
|
1092
1037
|
|
|
@@ -1097,6 +1042,6 @@ That is why the confirmation screen in the app should be understood as a **sessi
|
|
|
1097
1042
|
|
|
1098
1043
|
## Summary
|
|
1099
1044
|
|
|
1100
|
-
After this chapter, the app can really authenticate
|
|
1045
|
+
After this chapter, the app can really authenticate without Supabase or a database. It has a public `auth` surface, a stock login page, a sign-out route, and a shell widget that reflects auth state. The selected provider value lives in `.env`, and the web auth layer is wired into the same placement and surface system introduced earlier in the guide.
|
|
1101
1046
|
|
|
1102
1047
|
Just as importantly, the app is still deliberately incomplete. Authentication exists, but the database-backed user model does not. That separation is useful, because the next layer of the guide can explain users and persistence without having to also explain the first auth setup at the same time.
|