@owlmeans/web-client 0.1.18-rc.23 → 0.1.18-rc.25

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 CHANGED
@@ -15,7 +15,7 @@ React web application bootstrap — context factory, entrypoint/routing utilitie
15
15
  ## Installation
16
16
 
17
17
  ```bash
18
- bun add @owlmeans/web-client
18
+ bun add @owlmeans/web-client@^0.1.18-rc.23
19
19
  ```
20
20
 
21
21
  ## Usage
@@ -88,7 +88,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
88
88
  your project's skill store (`.agents/skills/`):
89
89
 
90
90
  ```sh
91
- npx @owlmeans/agent-skills
91
+ npx @owlmeans/agent-skills@^0.1.18-rc.13
92
92
  ```
93
93
 
94
94
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/web-client",
4
- "version": "0.1.18-rc.23",
5
- "generatedAt": "2026-09-01T17:39:40.763Z",
4
+ "version": "0.1.18-rc.25",
5
+ "generatedAt": "2026-09-10T18:54:42.497Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: web-client
3
- description: How to use @owlmeans/web-client — browser entry point with renderApp(), elevate() to attach React components to entrypoint declarations, context.registerEntrypoints() and context.serviceRoute() for routing. Auto-invoked when working with the web app entry point or attaching React components to entrypoints.
3
+ description: How to use @owlmeans/web-client — the browser entry point — makeContext(), renderApp(), elevate() to attach React components to entrypoint declarations, context.registerEntrypoints(), context.serviceRoute(), the dispatcher and surrogate login screens, and useAuthenticated(). Auto-invoked when working with a web app entry point, attaching React components to entrypoints, or wiring browser login.
4
4
  user-invocable: false
5
5
  ---
6
6
  <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
@@ -8,22 +8,45 @@ user-invocable: false
8
8
  # @owlmeans/web-client
9
9
 
10
10
  **Layer:** Web (React)
11
- **Install:** `"@owlmeans/web-client": "^0.1.18-rc.23"` in `dependencies`
11
+ **Install:** `"@owlmeans/web-client": "^0.1.18-rc.25"` in `dependencies`
12
+
13
+ The browser layer over `@owlmeans/client`. It builds the context, mounts the app, and brings
14
+ working authentication with nothing registered by hand.
15
+
16
+ **What is here and what is not.** `@owlmeans/client` owns the React primitives and does not
17
+ re-export from here; this package owns the browser wiring and re-exports only the few authoring
18
+ helpers below. In particular `useNavigate`, `useEntrypoint`, `useStoreModel`, `useStoreList`,
19
+ `useValue` and `useToggle` are imported from `@owlmeans/client` even in a browser-only app.
12
20
 
13
21
  ## Key Exports
14
22
 
15
23
  | Export | Description |
16
24
  |--------|-------------|
17
- | `renderApp<C, T>(context)` | Mount the React app (routing via the active router plugin — OwlMeans by default) |
18
- | `elevate(entrypoints, alias, handler?)` | Attach a React component (`handler(Component)`) to an entrypoint, or opt a backend alias into client-side calling |
19
- | `entrypoint(route, handler?)` | Build a client entrypoint from a route declaration |
20
- | `handler(Component)` | Wrap a React component as an entrypoint handler |
21
- | `context.registerEntrypoints(entrypoints)` | Register the full entrypoint list on the context |
22
- | `context.serviceRoute(alias, isDefault?)` | Mark a service's routing root |
23
- | `makeContext(cfg)` | Build the browser context — auth, web db, client resource, router and login are all appended here |
24
- | `appendWebLogin(context)` | Register the login host plus the redirect and surrogate-window plugins |
25
- | `router`, `components`, `service`, `i18n`, `helpers`, `errors` | Web-specific helpers |
26
- | Constants | Default aliases (BASE, HOME), `REDIRECT_LOGIN`, `SURROGATE_LOGIN` |
25
+ | `makeContext(cfg)` | Build the browser context: the client context plus the primary host, the auth service, web db, the auth client resource, the router and login |
26
+ | `AppConfig` / `AppContext` | The config and context types — `ClientContext` plus `auth()` and `login()` |
27
+ | `renderApp(context, opts?, children?)` | Mount the app. `children` render inside the provider and before the router — the global overlay slot |
28
+ | `render(node, opts?)` / `RenderOptions` | The bare mount: `{ domId, onReady, hydrate, debug }`, default root id `root` |
29
+ | `WebApp` | The mounted tree — the i18n provider around `App` from `@owlmeans/client` |
30
+ | `useContext()` | The context, typed as `AppContext` |
31
+ | `useAuthenticated()` | Whether this browsing context holds a session — it reports, it does not guard |
32
+ | `entrypoints` | The auth entrypoint list, with the dispatcher and the login-surrogate screens already elevated. Spread it into the app's own list |
33
+ | `Dispatcher` / `parametriseDispatcher(defaults, Com?)` | The post-login landing screen, and a wrapper that pre-fills its props |
34
+ | `Uploader` / `ImageUploader` / `UploaderProps` / `UploaderRootProps` | Dropzone-based file input; props are `react-dropzone`'s `DropzoneOptions` plus `Root`/`rootProps`. The image variant only DEFAULTS `maxSize` to 5 MB — pass `maxSize` to override it |
35
+ | `UploaderError` / `FileUploadingError` | Upload failures, on top of `ComponentError` |
36
+ | `appendWebLogin(ctx)` | Register the login host plus the redirect and surrogate-window plugins |
37
+ | `makeRedirectLoginPlugin()` / `makeSurrogateLoginPlugin()` / `awaitSurrogate(win, type, onMessage)` | The two browser login plugins and the window pump they share |
38
+ | `SurrogateScreen` / `LoginSurrogateView` / `SurrogateStage` / `SurrogateViewProps` | What the surrogate login window renders |
39
+ | `makeAuthWebService(alias?)` / `appendWebAuthService(ctx, alias?)` | The auth service whose `update(undefined)` leaves for the dispatcher. The append form also registers `authMiddleware` and the logout middleware |
40
+ | `extractPrimaryHost(ctx)` | Fill `cfg.primaryHost` / `primaryPort` from `window.location` |
41
+ | `DEFAULT_ROOT` (`root`), `REDIRECT_LOGIN`, `SURROGATE_LOGIN`, `SURROGATE_LOGIN_PRIORITY` (100) | Constants |
42
+
43
+ ### Re-exported for authoring
44
+
45
+ `handler` (from `@owlmeans/client`); `entrypoint`, `elevate`, `provideRequest`, `stab` and the
46
+ `ClientEntrypoint` type (from `@owlmeans/client-entrypoint`); `route`, `frontend` (from
47
+ `@owlmeans/route`); `config` (from `@owlmeans/client-context`); `service` (from `@owlmeans/config`);
48
+ `AppType`, `HOME`, `ROOT`, `BASE`, `GUEST` (from `@owlmeans/context`). Import them from here so an
49
+ app's entrypoint file needs one import.
27
50
 
28
51
  ## Usage
29
52
 
@@ -43,6 +66,12 @@ context.serviceRoute(MANAGER_API, true)
43
66
  renderApp<Config, Context>(context)
44
67
  ```
45
68
 
69
+ `serviceRoute(alias, makeDefault?)` marks a service's routing root; an alias `cfg.services` does not
70
+ carry throws a `SyntaxError` naming everything that is registered. Anything passed as `renderApp`'s
71
+ third argument mounts once, above routing, and survives navigation — which is where a consent
72
+ dialog or a toast surface belongs, since the same component inside a route is torn down and rebuilt
73
+ on every navigation and a dialog would close itself.
74
+
46
75
  ### Entrypoint elevation with React components
47
76
  ```typescript
48
77
  import { elevate, route, frontend, handler, entrypoint } from '@owlmeans/web-client'
@@ -75,6 +104,12 @@ const href = await context.entrypoint<ClientEntrypoint<Project>>(alias)
75
104
  The auth header is attached automatically for guarded entrypoints. An entrypoint that renders a
76
105
  screen answers `url()` and throws from `call()`/`invoke()` — a screen is navigated to, not called.
77
106
 
107
+ A guarded call whose session the server rejects logs the user out rather than surfacing the error
108
+ raw. `appendWebAuthService` registers a logout middleware for this — internal to the package, not
109
+ an import — which wraps `invoke` on every guarded backend entrypoint (that covers `call`, since
110
+ `call` reads `invoke` at the moment it runs), and an `AuthUnknown` ending in `:invalid` clears the
111
+ token, which through the web auth service leaves for the dispatcher.
112
+
78
113
  ### Login comes with the context
79
114
 
80
115
  `makeContext` calls `appendWebLogin`, which registers the login host (`@owlmeans/client-auth/login`)
@@ -89,9 +124,33 @@ import { useLogin, useLogout } from '@owlmeans/client-auth/login'
89
124
  const [, onLogIn] = useLogin() // one control, one handler, no environment checks
90
125
  ```
91
126
 
127
+ The dispatcher and the surrogate screen are elevated on the auth entrypoint list this package
128
+ exports, so an app that spreads `entrypoints` into its own list gets both without an edit. The
129
+ surrogate screen renders `LoginSurrogateView` with plain elements and inline styles on purpose: it
130
+ runs in a popup opened by apps with entirely different stylesheets, none of which is loaded there.
131
+
92
132
  An app that needs a different mechanic registers its own plugin at a higher priority after building
93
133
  the base context (`context.login().registerPlugin(...)`); it never replaces these. The contract, the
94
- cascade and the invariants are the `login-plugins` skill.
134
+ cascade and the invariants are the [[login-plugins]] skill.
135
+
136
+ ### Reading auth state
137
+
138
+ ```ts
139
+ import { useAuthenticated } from '@owlmeans/web-client'
140
+
141
+ const authenticated = useAuthenticated() // false until the check settles
142
+ ```
143
+
144
+ `useAuthenticated()` asks `context.auth().authenticated()` — the one channel the dispatcher and the
145
+ login screen already read, answering from the auth service's token or rehydrating it from the auth
146
+ resource. It is what a header uses to choose between the "Log in" and "Log out" control; nothing
147
+ about it navigates, and it does not re-check, because both ways out of its answer replace the
148
+ document.
149
+
150
+ **It reports; it does not guard.** The guard is `useSelfAuth` from `@owlmeans/client-auth`, which
151
+ sends an anonymous visitor to the dispatcher and defaults to doing so — the wrong behaviour for a
152
+ component that only wants to know which control to draw. An application declares neither hook
153
+ itself.
95
154
 
96
155
  ### Navigation
97
156
  ```typescript
@@ -110,9 +169,21 @@ which does it for you.
110
169
 
111
170
  ## Depends On
112
171
 
113
- - `@owlmeans/client`, `@owlmeans/web-router` (default OwlMeans browser routing), `@owlmeans/web-panel` (typically), `@owlmeans/client-i18n`
114
- - `@owlmeans/client-auth` — `AUTH_RESOURCE` and the `./login` host the browser plugins register on
115
- - `@owlmeans/entrypoint`, `@owlmeans/route`
172
+ - `@owlmeans/client`, `@owlmeans/web-router` (default OwlMeans browser routing), `@owlmeans/web-db`
173
+ (the IndexedDB store behind the client resources), `@owlmeans/client-i18n`
174
+ - `@owlmeans/client-auth` — `AUTH_RESOURCE`, the dispatcher HOC, and the `./login` host the browser
175
+ plugins register on; `@owlmeans/auth`, `@owlmeans/auth-common`
176
+ - `@owlmeans/client-context`, `@owlmeans/client-entrypoint`, `@owlmeans/client-resource`,
177
+ `@owlmeans/client-route`, `@owlmeans/route`, `@owlmeans/config`, `@owlmeans/context`,
178
+ `@owlmeans/error`, `@owlmeans/i18n`, `react-dropzone`
179
+ - `@owlmeans/web-panel` and `@owlmeans/mui-panel` build **on** this package, not the other way
180
+ round: a UI-family app imports the panel package and gets this one with it
116
181
  - `react`, `react-dom` (peer). It does not depend on `react-router` — opt into it with
117
182
  `@owlmeans/web-router-react-router` (`appendReactRouter`). Routing needs no explicit provider:
118
183
  `PanelApp`/`App` compile through `context.router()`, so nothing passes a `provide` prop.
184
+
185
+ ## Related
186
+
187
+ - [[client]] — the React primitives and the hooks this does not re-export
188
+ - [[login-plugins]] — the plugin cascade the two browser plugins take part in
189
+ - [[web-router]] — the routing this context registers
@@ -1 +1 @@
1
- {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../../src/components/dispatcher/component.tsx"],"names":[],"mappings":"AAUA,eAAO,MAAM,UAAU,6FA+FrB,CAAA"}
1
+ {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../../src/components/dispatcher/component.tsx"],"names":[],"mappings":"AAUA,eAAO,MAAM,UAAU,6FAsGrB,CAAA"}
@@ -3,7 +3,7 @@ import { DispatcherHOC } from '@owlmeans/client-auth';
3
3
  import { useEffect, useState } from 'react';
4
4
  import { useI18nLib } from '@owlmeans/client-i18n';
5
5
  import { AUTH_QUERY } from '@owlmeans/auth';
6
- import { FallbackLoginScreen, LoginIntent, LoginOutcome, ResumeAction, resumeAction, } from '@owlmeans/client-auth/login';
6
+ import { FallbackLoginScreen, LoginIntent, LoginOutcome, ResumeAction, resumeAction, LOGIN_FRESH_QUERY, } from '@owlmeans/client-auth/login';
7
7
  import { LoginSurrogateView, SurrogateStage } from '../../login/view.js';
8
8
  import { useContext } from '../../context.js';
9
9
  export const Dispatcher = DispatcherHOC(({ provideToken, navigate }) => {
@@ -33,7 +33,13 @@ export const Dispatcher = DispatcherHOC(({ provideToken, navigate }) => {
33
33
  provideToken({ token }, params);
34
34
  return;
35
35
  }
36
- void context.auth().authenticated().then(async (authzToken) => {
36
+ // `fresh` says this window was opened to PRODUCE a session, so one it merely finds on this
37
+ // origin is not the answer — see LOGIN_FRESH_QUERY. Kept identical to the relying party's own
38
+ // dispatcher: the last time these two held their own copy of a decision they drifted.
39
+ const found = query.has(LOGIN_FRESH_QUERY)
40
+ ? Promise.resolve(null)
41
+ : context.auth().authenticated();
42
+ void found.then(async (authzToken) => {
37
43
  if (authzToken == null || authzToken === '') {
38
44
  setChoose(true);
39
45
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"component.js","sourceRoot":"","sources":["../../../src/components/dispatcher/component.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"component.js","sourceRoot":"","sources":["../../../src/components/dispatcher/component.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"parametrise.d.ts","sourceRoot":"","sources":["../../../src/components/dispatcher/parametrise.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAI5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAE9C,eAAO,MAAM,qBAAqB,GAAI,KAAK,OAAO,CAAC,iBAAiB,CAAC,EAAE,MAAM,eAAe,CAAC,eAAe,CAAC,MAEnG,OAAO,iBAAiB,gCAiBjC,CAAA"}
1
+ {"version":3,"file":"parametrise.d.ts","sourceRoot":"","sources":["../../../src/components/dispatcher/parametrise.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAI5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAE9C,eAAO,MAAM,qBAAqB,QAAS,OAAO,CAAC,iBAAiB,CAAC,QAAQ,eAAe,CAAC,eAAe,CAAC,aAE5F,iBAAiB,gCAiBjC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAUlD,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,SAAS,GAAG,SAAS,EAAE,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,EAClG,KAAK,CAAC,KACL,CAWF,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,SAAS,GAAG,SAAS,EAAC,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,QACnF,CAAA"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAUlD,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,SAAS,GAAG,SAAS,EAAE,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,OAC7F,CAAC,KACL,CAWF,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,SAAS,GAAG,SAAS,EAAC,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,QACnF,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAE/C,qBAAa,aAAc,SAAQ,cAAc;IAC/C,OAAuB,QAAQ,EAAE,MAAM,CAAuC;gBAElE,OAAO,GAAE,MAAgB;CAItC;AAED,qBAAa,kBAAmB,SAAQ,aAAa;IACnD,OAAuB,QAAQ,EAAE,MAAM,CAA2C;IAE3E,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAA;gBAEtB,OAAO,GAAE,MAAgB;CAItC"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAE/C,qBAAa,aAAc,SAAQ,cAAc;IAC/C,OAAuB,QAAQ,EAAE,MAAM,CAAuC;IAE9E,YAAY,OAAO,GAAE,MAAgB,EAGpC;CACF;AAED,qBAAa,kBAAmB,SAAQ,aAAa;IACnD,OAAuB,QAAQ,EAAE,MAAM,CAA2C;IAE3E,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAA;IAElC,YAAY,OAAO,GAAE,MAAgB,EAGpC;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAE/C,MAAM,OAAO,aAAc,SAAQ,cAAc;IACxC,MAAM,CAAU,QAAQ,GAAW,GAAG,cAAc,CAAC,QAAQ,UAAU,CAAA;IAE9E,YAAY,UAAkB,OAAO;QACnC,KAAK,CAAC,YAAY,OAAO,EAAE,CAAC,CAAA;QAC5B,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC,QAAQ,CAAA;IACpC,CAAC;;AAGH,MAAM,OAAO,kBAAmB,SAAQ,aAAa;IAC5C,MAAM,CAAU,QAAQ,GAAW,GAAG,aAAa,CAAC,QAAQ,eAAe,CAAA;IAE3E,QAAQ,CAAmB;IAElC,YAAY,UAAkB,OAAO;QACnC,KAAK,CAAC,kBAAkB,OAAO,EAAE,CAAC,CAAA;QAClC,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC,QAAQ,CAAA;IACzC,CAAC;;AAGH,cAAc,CAAC,kBAAkB,CAAC,aAAa,CAAC,CAAA;AAChD,cAAc,CAAC,kBAAkB,CAAC,kBAAkB,CAAC,CAAA"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAE/C,MAAM,OAAO,aAAc,SAAQ,cAAc;IACxC,MAAM,CAAU,QAAQ,GAAW,GAAG,cAAc,CAAC,QAAQ,UAAU,CAAA;IAE9E,YAAY,OAAO,GAAW,OAAO;QACnC,KAAK,CAAC,YAAY,OAAO,EAAE,CAAC,CAAA;QAC5B,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC,QAAQ,CAAA;IACpC,CAAC;CACF;AAED,MAAM,OAAO,kBAAmB,SAAQ,aAAa;IAC5C,MAAM,CAAU,QAAQ,GAAW,GAAG,aAAa,CAAC,QAAQ,eAAe,CAAA;IAE3E,QAAQ,CAAmB;IAElC,YAAY,OAAO,GAAW,OAAO;QACnC,KAAK,CAAC,kBAAkB,OAAO,EAAE,CAAC,CAAA;QAClC,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC,QAAQ,CAAA;IACzC,CAAC;CACF;AAED,cAAc,CAAC,kBAAkB,CAAC,aAAa,CAAC,CAAA;AAChD,cAAc,CAAC,kBAAkB,CAAC,kBAAkB,CAAC,CAAA"}
package/build/helper.d.ts CHANGED
@@ -1,3 +1,22 @@
1
1
  import type { AppConfig, AppContext } from './types.js';
2
2
  export declare const extractPrimaryHost: <C extends AppConfig = AppConfig, T extends AppContext<C> = AppContext<C>>(context: T) => void;
3
+ /**
4
+ * Whether this browsing context currently holds a session — the flag a screen renders against
5
+ * when it offers "Log in" or "Log out", a profile menu, or an authenticated-only panel.
6
+ *
7
+ * It reads the one channel the rest of the package reads, `context.auth().authenticated()`, which
8
+ * answers from the auth service's token or, in a cold document, rehydrates it from the auth
9
+ * resource. Nothing here touches storage or decodes a token: a second way to ask the same question
10
+ * is a second thing to keep in step with logout.
11
+ *
12
+ * The answer is `false` until that promise settles and is not re-checked afterwards, because both
13
+ * transitions out of the current answer replace the document — signing in navigates through the
14
+ * dispatcher, and `auth().update(undefined)` assigns `document.location.href`. There is no in-page
15
+ * change to subscribe to.
16
+ *
17
+ * This reports; it does not guard. `useSelfAuth` from `@owlmeans/client-auth` is the guard — it
18
+ * sends an anonymous visitor to the dispatcher, which is the wrong behaviour for a component that
19
+ * only wants to know which control to draw.
20
+ */
21
+ export declare const useAuthenticated: () => boolean;
3
22
  //# sourceMappingURL=helper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEvD,eAAO,MAAM,kBAAkB,GAAI,CAAC,SAAS,SAAS,GAAG,SAAS,EAAE,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,SAKtH,CAAA"}
1
+ {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEvD,eAAO,MAAM,kBAAkB,GAAI,CAAC,SAAS,SAAS,GAAG,SAAS,EAAE,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,SAKtH,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,gBAAgB,QAAO,OAgBnC,CAAA"}
package/build/helper.js CHANGED
@@ -1,7 +1,41 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { useContext } from '@owlmeans/client';
1
3
  export const extractPrimaryHost = (context) => {
2
4
  if (typeof window !== 'undefined') {
3
5
  context.cfg.primaryHost = window.location.hostname;
4
6
  context.cfg.primaryPort = window.location.port != null && window.location.port != '' ? parseInt(window.location.port) : undefined;
5
7
  }
6
8
  };
9
+ /**
10
+ * Whether this browsing context currently holds a session — the flag a screen renders against
11
+ * when it offers "Log in" or "Log out", a profile menu, or an authenticated-only panel.
12
+ *
13
+ * It reads the one channel the rest of the package reads, `context.auth().authenticated()`, which
14
+ * answers from the auth service's token or, in a cold document, rehydrates it from the auth
15
+ * resource. Nothing here touches storage or decodes a token: a second way to ask the same question
16
+ * is a second thing to keep in step with logout.
17
+ *
18
+ * The answer is `false` until that promise settles and is not re-checked afterwards, because both
19
+ * transitions out of the current answer replace the document — signing in navigates through the
20
+ * dispatcher, and `auth().update(undefined)` assigns `document.location.href`. There is no in-page
21
+ * change to subscribe to.
22
+ *
23
+ * This reports; it does not guard. `useSelfAuth` from `@owlmeans/client-auth` is the guard — it
24
+ * sends an anonymous visitor to the dispatcher, which is the wrong behaviour for a component that
25
+ * only wants to know which control to draw.
26
+ */
27
+ export const useAuthenticated = () => {
28
+ const context = useContext();
29
+ const [authenticated, setAuthenticated] = useState(false);
30
+ useEffect(() => {
31
+ let mounted = true;
32
+ void context.auth().authenticated().then(token => {
33
+ if (mounted) {
34
+ setAuthenticated(token != null && token !== '');
35
+ }
36
+ });
37
+ return () => { mounted = false; };
38
+ }, []);
39
+ return authenticated;
40
+ };
7
41
  //# sourceMappingURL=helper.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAA2E,OAAU,EAAE,EAAE;IACzH,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAA;QAClD,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,IAAI,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACnI,CAAC;AACH,CAAC,CAAA"}
1
+ {"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAG7C,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAA2E,OAAU,EAAE,EAAE;IACzH,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAA;QAClD,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,IAAI,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACnI,CAAC;AACH,CAAC,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAY,EAAE;IAC5C,MAAM,OAAO,GAAG,UAAU,EAAyB,CAAA;IACnD,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAEzD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,OAAO,GAAG,IAAI,CAAA;QAClB,KAAK,OAAO,CAAC,IAAI,EAAE,CAAC,aAAa,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;YAC/C,IAAI,OAAO,EAAE,CAAC;gBACZ,gBAAgB,CAAC,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC,CAAA;YACjD,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,OAAO,GAAG,EAAE,GAAG,OAAO,GAAG,KAAK,CAAA,CAAC,CAAC,CAAA;IAClC,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,OAAO,aAAa,CAAA;AACtB,CAAC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"pump.d.ts","sourceRoot":"","sources":["../../src/login/pump.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAEhF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,GACzB,WAAW,MAAM,EACjB,aAAa,MAAM,EACnB,WAAW,CAAC,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAE,KAAK,OAAO,CAAC,YAAY,CAAC,KAC1F,OAAO,CAAC,YAAY,CAiCrB,CAAA"}
1
+ {"version":3,"file":"pump.d.ts","sourceRoot":"","sources":["../../src/login/pump.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAEhF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,cACd,MAAM,eACJ,MAAM,aACR,CAAC,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAE,KAAK,OAAO,CAAC,YAAY,CAAC,KAC1F,OAAO,CAAC,YAAY,CAiCrB,CAAA"}
@@ -7,9 +7,22 @@ import type { FC } from 'react';
7
7
  * application, with its navigation, inside itself. This screen has no continuation at all — it
8
8
  * either hands something back and closes, or it says what it is waiting for.
9
9
  *
10
- * It also never runs the authorization machine. When there is already a session here, that is the
11
- * answer, and it goes back to the opener immediately — no provider round trip, no PKCE exchange.
12
- * When there is not, it forwards to the dispatcher (`next`), which owns that flow.
10
+ * It does not run the authorization machine itself either: it forwards to the dispatcher (`next`),
11
+ * which owns that flow.
12
+ *
13
+ * It used to short-circuit that when a session was already present on this origin — hand it back,
14
+ * no provider round trip. That is wrong, and it was the whole of a reported defect. This window is
15
+ * first-party on the application's origin, so what it finds there is whatever the person's own
16
+ * tabs left behind, and a token being present says nothing about the record behind it still
17
+ * existing: `authenticated()` reads storage and decodes an envelope, and no client asks the server.
18
+ * A session that had been revoked, replaced or expired was therefore handed back to a window that
19
+ * had asked for a LOGIN, adopted, and used to fail every call — and the next press of the same
20
+ * button found the same dead token and did it again. Nothing recovered but clearing site data.
21
+ *
22
+ * So an explicit login PRODUCES a session. `fresh` says so to the dispatcher, which skips its own
23
+ * resume for the same reason. The cost is one provider round trip that the provider's own session
24
+ * normally answers without asking the person anything; the gain is that what comes back has just
25
+ * been vouched for, and that signing in as somebody else is possible at all.
13
26
  */
14
27
  export declare const SurrogateScreen: FC;
15
28
  //# sourceMappingURL=screen.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../../src/login/screen.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAU/B;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,EAAE,EA+F7B,CAAA"}
1
+ {"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../../src/login/screen.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAW/B;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,eAAe,EAAE,EAoF7B,CAAA"}
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useState } from 'react';
3
3
  import { useContext } from '@owlmeans/client';
4
4
  import { useI18nLib } from '@owlmeans/client-i18n';
5
- import { LoginIntent, LoginOutcome, LOGIN_INTENT_QUERY, LOGIN_METHOD_QUERY, LOGIN_NEXT_QUERY, } from '@owlmeans/client-auth/login';
5
+ import { LoginIntent, LoginOutcome, LOGIN_FRESH_QUERY, LOGIN_INTENT_QUERY, LOGIN_METHOD_QUERY, LOGIN_NEXT_QUERY, } from '@owlmeans/client-auth/login';
6
6
  import { LoginSurrogateView, SurrogateStage } from './view.js';
7
7
  /**
8
8
  * The login window an embedded application opens one level up.
@@ -12,9 +12,22 @@ import { LoginSurrogateView, SurrogateStage } from './view.js';
12
12
  * application, with its navigation, inside itself. This screen has no continuation at all — it
13
13
  * either hands something back and closes, or it says what it is waiting for.
14
14
  *
15
- * It also never runs the authorization machine. When there is already a session here, that is the
16
- * answer, and it goes back to the opener immediately — no provider round trip, no PKCE exchange.
17
- * When there is not, it forwards to the dispatcher (`next`), which owns that flow.
15
+ * It does not run the authorization machine itself either: it forwards to the dispatcher (`next`),
16
+ * which owns that flow.
17
+ *
18
+ * It used to short-circuit that when a session was already present on this origin — hand it back,
19
+ * no provider round trip. That is wrong, and it was the whole of a reported defect. This window is
20
+ * first-party on the application's origin, so what it finds there is whatever the person's own
21
+ * tabs left behind, and a token being present says nothing about the record behind it still
22
+ * existing: `authenticated()` reads storage and decodes an envelope, and no client asks the server.
23
+ * A session that had been revoked, replaced or expired was therefore handed back to a window that
24
+ * had asked for a LOGIN, adopted, and used to fail every call — and the next press of the same
25
+ * button found the same dead token and did it again. Nothing recovered but clearing site data.
26
+ *
27
+ * So an explicit login PRODUCES a session. `fresh` says so to the dispatcher, which skips its own
28
+ * resume for the same reason. The cost is one provider round trip that the provider's own session
29
+ * normally answers without asking the person anything; the gain is that what comes back has just
30
+ * been vouched for, and that signing in as somebody else is possible at all.
18
31
  */
19
32
  export const SurrogateScreen = () => {
20
33
  const context = useContext();
@@ -51,7 +64,6 @@ export const SurrogateScreen = () => {
51
64
  return;
52
65
  }
53
66
  const run = async () => {
54
- const token = await context.auth().authenticated();
55
67
  if (intent === LoginIntent.Logout) {
56
68
  // Revoked FIRST, before anything can be awaited on a channel that may never answer: a
57
69
  // window the user closes half a second later must still have ended the session it opened
@@ -63,28 +75,22 @@ export const SurrogateScreen = () => {
63
75
  : SurrogateStage.Failed);
64
76
  return;
65
77
  }
66
- if (token != null && token !== '') {
67
- // Already signed in here. The framed application is the one that asked, so the session
68
- // goes to it rather than being displayed to this window.
69
- const outcome = await login.resume(token);
70
- setStage(outcome === LoginOutcome.Handled ? SurrogateStage.Handing
71
- : outcome === LoginOutcome.Orphaned ? SurrogateStage.Orphaned
72
- : SurrogateStage.Failed);
73
- return;
74
- }
75
- // Nothing here yet — the dispatcher owns the authorization round trip, and the provider's
76
- // callback lands there rather than on this route. The method the user already chose travels
77
- // with it, so the dispatcher does not ask a second time in a window with no one to ask.
78
+ // The dispatcher owns the authorization round trip, and the provider's callback lands there
79
+ // rather than on this route. Two things travel with it: the method the user already chose,
80
+ // so the dispatcher does not ask a second time in a window with no one to ask, and `fresh`,
81
+ // which tells it that a session it happens to find here is not the answer to this request.
78
82
  const next = query.get(LOGIN_NEXT_QUERY);
79
83
  if (next == null || next === '') {
80
84
  setStage(SurrogateStage.Gesture);
81
85
  return;
82
86
  }
83
87
  const method = query.get(LOGIN_METHOD_QUERY);
84
- const target = method != null && method !== ''
85
- ? `${next}${next.includes('?') ? '&' : '?'}${LOGIN_METHOD_QUERY}=${encodeURIComponent(method)}`
86
- : next;
87
- window.location.href = target;
88
+ const params = new URLSearchParams();
89
+ if (method != null && method !== '') {
90
+ params.set(LOGIN_METHOD_QUERY, method);
91
+ }
92
+ params.set(LOGIN_FRESH_QUERY, '1');
93
+ window.location.href = `${next}${next.includes('?') ? '&' : '?'}${params.toString()}`;
88
94
  };
89
95
  void run().catch((e) => {
90
96
  console.error(e);
@@ -1 +1 @@
1
- {"version":3,"file":"screen.js","sourceRoot":"","sources":["../../src/login/screen.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"screen.js","sourceRoot":"","sources":["../../src/login/screen.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/login/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAIlE;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,WAAW,EAAE,CAAC,SAAS,YAAY,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,KAAG,CAMzF,CAAA"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/login/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAIlE;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,WAAW,EAAE,CAAC,SAAS,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,KAAG,CAMzF,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAGtE,eAAO,MAAM,MAAM,GAAI,MAAM,SAAS,EAAE,OAAO,aAAa,SAkB3D,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,SAAS,EAAE,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,EACpE,SAAS,CAAC,EAAE,OAAO,aAAa,EAAE,WAAW,SAAS,SACQ,CAAA"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAGtE,eAAO,MAAM,MAAM,SAAU,SAAS,SAAS,aAAa,SAkB3D,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,SAAS,EAAE,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,WAC3D,CAAC,SAAS,aAAa,aAAa,SAAS,SACQ,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAE9D,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAI3E,eAAO,MAAM,kBAAkB,GAAI,QAAO,MAAsB,KAAG,WAclE,CAAA;AAED,eAAO,MAAM,oBAAoB,GAAI,CAAC,SAAS,YAAY,EAAE,CAAC,SAAS,aAAa,CAAC,CAAC,CAAC,EACrF,KAAK,CAAC,EAAE,QAAO,MAAsB,KACpC,CAAC,GAAG,iBAaN,CAAA"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAE9D,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAI3E,eAAO,MAAM,kBAAkB,WAAW,MAAM,KAAmB,WAclE,CAAA;AAED,eAAO,MAAM,oBAAoB,GAAI,CAAC,SAAS,YAAY,EAAE,CAAC,SAAS,aAAa,CAAC,CAAC,CAAC,OAChF,CAAC,UAAS,MAAM,KACpB,CAAC,GAAG,iBAaN,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAE3C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAGtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAElD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,QAAgB,aAAa,EAAe,EAAE;IAC/E,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,CAAC,CAAA;IAEtC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAA;IAC7B,OAAO,CAAC,MAAM,GAAG,KAAK,EAAC,KAAK,EAAC,EAAE;QAC7B,MAAM,MAAM,CAAC,KAAK,CAAC,CAAA;QACnB,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;YAClB,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,UAAU,CAA2B,UAAU,CAAC,CAAA;YACnF,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,EAAE,CAAA;YAC9B,QAAQ,CAAC,QAAQ,CAAC,IAAI,GAAG,GAAG,CAAA;QAC9B,CAAC;IACH,CAAC,CAAA;IAED,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,GAAM,EAAE,QAAgB,aAAa,EACd,EAAE;IACzB,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAA;IAEzC,MAAM,OAAO,GAAG,GAA4B,CAAA;IAE5C,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAA;IAEhC,OAAO,CAAC,kBAAkB,CAAC,cAAc,CAAC,CAAA;IAC1C,OAAO,CAAC,kBAAkB,CAAC,gBAAgB,CAAC,CAAA;IAE5C,OAAO,CAAC,IAAI,GAAG,GAAG,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAE/C,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA"}
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAE3C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAGtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAElD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,KAAK,GAAW,aAAa,EAAe,EAAE;IAC/E,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,CAAC,CAAA;IAEtC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAA;IAC7B,OAAO,CAAC,MAAM,GAAG,KAAK,EAAC,KAAK,EAAC,EAAE;QAC7B,MAAM,MAAM,CAAC,KAAK,CAAC,CAAA;QACnB,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;YAClB,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,UAAU,CAA2B,UAAU,CAAC,CAAA;YACnF,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,EAAE,CAAA;YAC9B,QAAQ,CAAC,QAAQ,CAAC,IAAI,GAAG,GAAG,CAAA;QAC9B,CAAC;IACH,CAAC,CAAA;IAED,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,GAAM,EAAE,KAAK,GAAW,aAAa,EACd,EAAE;IACzB,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAA;IAEzC,MAAM,OAAO,GAAG,GAA4B,CAAA;IAE5C,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAA;IAEhC,OAAO,CAAC,kBAAkB,CAAC,cAAc,CAAC,CAAA;IAC1C,OAAO,CAAC,kBAAkB,CAAC,gBAAgB,CAAC,CAAA;IAE5C,OAAO,CAAC,IAAI,GAAG,GAAG,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAE/C,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/web-client",
3
- "version": "0.1.18-rc.23",
3
+ "version": "0.1.18-rc.25",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -21,22 +21,22 @@
21
21
  }
22
22
  },
23
23
  "dependencies": {
24
- "@owlmeans/auth": "^0.1.18-rc.8",
25
- "@owlmeans/auth-common": "^0.1.18-rc.12",
26
- "@owlmeans/client": "^0.1.18-rc.15",
27
- "@owlmeans/client-auth": "^0.1.18-rc.19",
28
- "@owlmeans/client-context": "^0.1.18-rc.12",
29
- "@owlmeans/client-i18n": "^0.1.18-rc.14",
30
- "@owlmeans/client-entrypoint": "^0.1.18-rc.12",
31
- "@owlmeans/client-resource": "^0.1.18-rc.12",
32
- "@owlmeans/client-route": "^0.1.18-rc.12",
33
- "@owlmeans/config": "^0.1.18-rc.11",
34
- "@owlmeans/context": "^0.1.18-rc.7",
35
- "@owlmeans/error": "^0.1.18-rc.7",
36
- "@owlmeans/i18n": "^0.1.18-rc.7",
37
- "@owlmeans/route": "^0.1.18-rc.8",
38
- "@owlmeans/web-db": "^0.1.18-rc.13",
39
- "@owlmeans/web-router": "^0.1.18-rc.13",
24
+ "@owlmeans/auth": "^0.1.18-rc.10",
25
+ "@owlmeans/auth-common": "^0.1.18-rc.14",
26
+ "@owlmeans/client": "^0.1.18-rc.17",
27
+ "@owlmeans/client-auth": "^0.1.18-rc.21",
28
+ "@owlmeans/client-context": "^0.1.18-rc.14",
29
+ "@owlmeans/client-i18n": "^0.1.18-rc.16",
30
+ "@owlmeans/client-entrypoint": "^0.1.18-rc.14",
31
+ "@owlmeans/client-resource": "^0.1.18-rc.14",
32
+ "@owlmeans/client-route": "^0.1.18-rc.14",
33
+ "@owlmeans/config": "^0.1.18-rc.13",
34
+ "@owlmeans/context": "^0.1.18-rc.9",
35
+ "@owlmeans/error": "^0.1.18-rc.9",
36
+ "@owlmeans/i18n": "^0.1.18-rc.9",
37
+ "@owlmeans/route": "^0.1.18-rc.10",
38
+ "@owlmeans/web-db": "^0.1.18-rc.15",
39
+ "@owlmeans/web-router": "^0.1.18-rc.15",
40
40
  "react-dropzone": "^14.3.5"
41
41
  },
42
42
  "peerDependencies": {
@@ -3,7 +3,7 @@ import { useEffect, useState } from 'react'
3
3
  import { useI18nLib } from '@owlmeans/client-i18n'
4
4
  import { AUTH_QUERY } from '@owlmeans/auth'
5
5
  import {
6
- FallbackLoginScreen, LoginIntent, LoginOutcome, ResumeAction, resumeAction,
6
+ FallbackLoginScreen, LoginIntent, LoginOutcome, ResumeAction, resumeAction, LOGIN_FRESH_QUERY,
7
7
  } from '@owlmeans/client-auth/login'
8
8
  import { LoginSurrogateView, SurrogateStage } from '../../login/view.js'
9
9
  import { useContext } from '../../context.js'
@@ -42,7 +42,14 @@ export const Dispatcher = DispatcherHOC(({ provideToken, navigate }) => {
42
42
  return
43
43
  }
44
44
 
45
- void context.auth().authenticated().then(async authzToken => {
45
+ // `fresh` says this window was opened to PRODUCE a session, so one it merely finds on this
46
+ // origin is not the answer — see LOGIN_FRESH_QUERY. Kept identical to the relying party's own
47
+ // dispatcher: the last time these two held their own copy of a decision they drifted.
48
+ const found = query.has(LOGIN_FRESH_QUERY)
49
+ ? Promise.resolve(null)
50
+ : context.auth().authenticated()
51
+
52
+ void found.then(async authzToken => {
46
53
  if (authzToken == null || authzToken === '') {
47
54
  setChoose(true)
48
55
 
package/src/helper.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { useEffect, useState } from 'react'
2
+ import { useContext } from '@owlmeans/client'
1
3
  import type { AppConfig, AppContext } from './types.js'
2
4
 
3
5
  export const extractPrimaryHost = <C extends AppConfig = AppConfig, T extends AppContext<C> = AppContext<C>>(context: T) => {
@@ -6,3 +8,39 @@ export const extractPrimaryHost = <C extends AppConfig = AppConfig, T extends Ap
6
8
  context.cfg.primaryPort = window.location.port != null && window.location.port != '' ? parseInt(window.location.port) : undefined
7
9
  }
8
10
  }
11
+
12
+ /**
13
+ * Whether this browsing context currently holds a session — the flag a screen renders against
14
+ * when it offers "Log in" or "Log out", a profile menu, or an authenticated-only panel.
15
+ *
16
+ * It reads the one channel the rest of the package reads, `context.auth().authenticated()`, which
17
+ * answers from the auth service's token or, in a cold document, rehydrates it from the auth
18
+ * resource. Nothing here touches storage or decodes a token: a second way to ask the same question
19
+ * is a second thing to keep in step with logout.
20
+ *
21
+ * The answer is `false` until that promise settles and is not re-checked afterwards, because both
22
+ * transitions out of the current answer replace the document — signing in navigates through the
23
+ * dispatcher, and `auth().update(undefined)` assigns `document.location.href`. There is no in-page
24
+ * change to subscribe to.
25
+ *
26
+ * This reports; it does not guard. `useSelfAuth` from `@owlmeans/client-auth` is the guard — it
27
+ * sends an anonymous visitor to the dispatcher, which is the wrong behaviour for a component that
28
+ * only wants to know which control to draw.
29
+ */
30
+ export const useAuthenticated = (): boolean => {
31
+ const context = useContext<AppConfig, AppContext>()
32
+ const [authenticated, setAuthenticated] = useState(false)
33
+
34
+ useEffect(() => {
35
+ let mounted = true
36
+ void context.auth().authenticated().then(token => {
37
+ if (mounted) {
38
+ setAuthenticated(token != null && token !== '')
39
+ }
40
+ })
41
+
42
+ return () => { mounted = false }
43
+ }, [])
44
+
45
+ return authenticated
46
+ }
@@ -3,7 +3,8 @@ import type { FC } from 'react'
3
3
  import { useContext } from '@owlmeans/client'
4
4
  import { useI18nLib } from '@owlmeans/client-i18n'
5
5
  import {
6
- LoginIntent, LoginOutcome, LOGIN_INTENT_QUERY, LOGIN_METHOD_QUERY, LOGIN_NEXT_QUERY,
6
+ LoginIntent, LoginOutcome, LOGIN_FRESH_QUERY, LOGIN_INTENT_QUERY, LOGIN_METHOD_QUERY,
7
+ LOGIN_NEXT_QUERY,
7
8
  } from '@owlmeans/client-auth/login'
8
9
  import type { LoginService } from '@owlmeans/client-auth/login'
9
10
  import type { AppContext } from '../types.js'
@@ -17,9 +18,22 @@ import { LoginSurrogateView, SurrogateStage } from './view.js'
17
18
  * application, with its navigation, inside itself. This screen has no continuation at all — it
18
19
  * either hands something back and closes, or it says what it is waiting for.
19
20
  *
20
- * It also never runs the authorization machine. When there is already a session here, that is the
21
- * answer, and it goes back to the opener immediately — no provider round trip, no PKCE exchange.
22
- * When there is not, it forwards to the dispatcher (`next`), which owns that flow.
21
+ * It does not run the authorization machine itself either: it forwards to the dispatcher (`next`),
22
+ * which owns that flow.
23
+ *
24
+ * It used to short-circuit that when a session was already present on this origin — hand it back,
25
+ * no provider round trip. That is wrong, and it was the whole of a reported defect. This window is
26
+ * first-party on the application's origin, so what it finds there is whatever the person's own
27
+ * tabs left behind, and a token being present says nothing about the record behind it still
28
+ * existing: `authenticated()` reads storage and decodes an envelope, and no client asks the server.
29
+ * A session that had been revoked, replaced or expired was therefore handed back to a window that
30
+ * had asked for a LOGIN, adopted, and used to fail every call — and the next press of the same
31
+ * button found the same dead token and did it again. Nothing recovered but clearing site data.
32
+ *
33
+ * So an explicit login PRODUCES a session. `fresh` says so to the dispatcher, which skips its own
34
+ * resume for the same reason. The cost is one provider round trip that the provider's own session
35
+ * normally answers without asking the person anything; the gain is that what comes back has just
36
+ * been vouched for, and that signing in as somebody else is possible at all.
23
37
  */
24
38
  export const SurrogateScreen: FC = () => {
25
39
  const context = useContext() as unknown as AppContext
@@ -62,8 +76,6 @@ export const SurrogateScreen: FC = () => {
62
76
  }
63
77
 
64
78
  const run = async (): Promise<void> => {
65
- const token = await context.auth().authenticated()
66
-
67
79
  if (intent === LoginIntent.Logout) {
68
80
  // Revoked FIRST, before anything can be awaited on a channel that may never answer: a
69
81
  // window the user closes half a second later must still have ended the session it opened
@@ -78,31 +90,22 @@ export const SurrogateScreen: FC = () => {
78
90
  return
79
91
  }
80
92
 
81
- if (token != null && token !== '') {
82
- // Already signed in here. The framed application is the one that asked, so the session
83
- // goes to it rather than being displayed to this window.
84
- const outcome = await login.resume(token)
85
- setStage(
86
- outcome === LoginOutcome.Handled ? SurrogateStage.Handing
87
- : outcome === LoginOutcome.Orphaned ? SurrogateStage.Orphaned
88
- : SurrogateStage.Failed
89
- )
90
- return
91
- }
92
-
93
- // Nothing here yet — the dispatcher owns the authorization round trip, and the provider's
94
- // callback lands there rather than on this route. The method the user already chose travels
95
- // with it, so the dispatcher does not ask a second time in a window with no one to ask.
93
+ // The dispatcher owns the authorization round trip, and the provider's callback lands there
94
+ // rather than on this route. Two things travel with it: the method the user already chose,
95
+ // so the dispatcher does not ask a second time in a window with no one to ask, and `fresh`,
96
+ // which tells it that a session it happens to find here is not the answer to this request.
96
97
  const next = query.get(LOGIN_NEXT_QUERY)
97
98
  if (next == null || next === '') {
98
99
  setStage(SurrogateStage.Gesture)
99
100
  return
100
101
  }
101
102
  const method = query.get(LOGIN_METHOD_QUERY)
102
- const target = method != null && method !== ''
103
- ? `${next}${next.includes('?') ? '&' : '?'}${LOGIN_METHOD_QUERY}=${encodeURIComponent(method)}`
104
- : next
105
- window.location.href = target
103
+ const params = new URLSearchParams()
104
+ if (method != null && method !== '') {
105
+ params.set(LOGIN_METHOD_QUERY, method)
106
+ }
107
+ params.set(LOGIN_FRESH_QUERY, '1')
108
+ window.location.href = `${next}${next.includes('?') ? '&' : '?'}${params.toString()}`
106
109
  }
107
110
 
108
111
  void run().catch((e: Error) => {