@owlmeans/web-client 0.1.18-rc.25 → 0.1.18-rc.27
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 +12 -12
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/web-client/SKILL.md +25 -175
- package/build/components/dispatcher/component.d.ts.map +1 -1
- package/build/components/dispatcher/component.js +2 -8
- package/build/components/dispatcher/component.js.map +1 -1
- package/build/entrypoints.d.ts +15 -1
- package/build/entrypoints.d.ts.map +1 -1
- package/build/entrypoints.js +9 -7
- package/build/entrypoints.js.map +1 -1
- package/build/exports.d.ts +1 -1
- package/build/exports.d.ts.map +1 -1
- package/build/exports.js +1 -1
- package/build/exports.js.map +1 -1
- package/build/helper.d.ts +0 -19
- package/build/helper.d.ts.map +1 -1
- package/build/helper.js +0 -34
- package/build/helper.js.map +1 -1
- package/build/login/screen.d.ts +3 -16
- package/build/login/screen.d.ts.map +1 -1
- package/build/login/screen.js +21 -27
- package/build/login/screen.js.map +1 -1
- package/package.json +17 -17
- package/src/components/dispatcher/component.tsx +2 -9
- package/src/entrypoints.ts +9 -9
- package/src/exports.ts +1 -1
- package/src/helper.ts +0 -38
- package/src/login/screen.tsx +25 -28
package/README.md
CHANGED
|
@@ -6,11 +6,11 @@ React web application bootstrap — context factory, entrypoint/routing utilitie
|
|
|
6
6
|
|
|
7
7
|
- `makeContext(cfg)` — creates the web application context (includes auth, router, and DB services)
|
|
8
8
|
- `renderApp(context)` — mounts the React app to the DOM
|
|
9
|
-
- Re-exports entrypoint/routing helpers: `
|
|
9
|
+
- Re-exports entrypoint/routing helpers: `bind`, `bindAll`, `bindScreen`, `route`, `frontend`, `handler`
|
|
10
10
|
- Re-exports route constants: `BASE`, `HOME`, `ROOT`, `GUEST`
|
|
11
11
|
- `entrypoints` — the base client entrypoints (dispatcher, login surrogate) every web app spreads
|
|
12
|
-
- `
|
|
13
|
-
`call()` / `invoke()` / `url()`
|
|
12
|
+
- `ClientProtocolEntrypoint<Protocol>` — the context-bound entrypoint interface inferred from a
|
|
13
|
+
shared protocol declaration; it exposes `call()` / `invoke()` / `url()`
|
|
14
14
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
|
@@ -34,20 +34,20 @@ renderApp<Config, Context>(context)
|
|
|
34
34
|
Define entrypoints:
|
|
35
35
|
|
|
36
36
|
```typescript
|
|
37
|
-
import {
|
|
37
|
+
import { bindScreen, bindAll } from '@owlmeans/client-entrypoint'
|
|
38
|
+
import { handler } from '@owlmeans/client'
|
|
39
|
+
import { appEntrypoints as protocols } from 'my-app-common'
|
|
38
40
|
|
|
39
|
-
appEntrypoints.push(
|
|
40
|
-
appEntrypoints.push(
|
|
41
|
-
|
|
41
|
+
appEntrypoints.push(bindScreen(protocols.web.base, handler(PublicLayout)))
|
|
42
|
+
appEntrypoints.push(bindScreen(protocols.web.home, handler(HomeScreen)))
|
|
43
|
+
appEntrypoints.push(...bindAll(protocols.api))
|
|
42
44
|
```
|
|
43
45
|
|
|
44
46
|
A screen entrypoint carries a renderer, so it is addressed by `url()` and never called over the
|
|
45
47
|
wire. Call an API entrypoint from a component:
|
|
46
48
|
|
|
47
49
|
```typescript
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const result = await context.entrypoint<ClientEntrypoint<MyType>>(alias)
|
|
50
|
+
const result = await context.entrypoint(appEntrypoints.api.project)
|
|
51
51
|
.call({ params: { id }, body: data })
|
|
52
52
|
```
|
|
53
53
|
|
|
@@ -78,7 +78,7 @@ Component that handles authentication routing (redirect to login, etc.).
|
|
|
78
78
|
- [`@owlmeans/client-context`](../client-context) — `ClientContext` base extended by `makeContext`
|
|
79
79
|
- [`@owlmeans/web-router`](../web-router) — default OwlMeans browser routing plugin registered by `makeContext`
|
|
80
80
|
- [`@owlmeans/web-db`](../web-db) — IndexedDB service registered by `makeContext`
|
|
81
|
-
- [`@owlmeans/client-entrypoint`](../client-entrypoint) —
|
|
81
|
+
- [`@owlmeans/client-entrypoint`](../client-entrypoint) — protocol binding helpers
|
|
82
82
|
|
|
83
83
|
<!-- owlmeans:agent-guidance:start -->
|
|
84
84
|
## Agent guidance
|
|
@@ -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@^0.1.18-rc.
|
|
91
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.15
|
|
92
92
|
```
|
|
93
93
|
|
|
94
94
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/web-client",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-09-
|
|
4
|
+
"version": "0.1.18-rc.27",
|
|
5
|
+
"generatedAt": "2026-09-12T08:39:52.726Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -1,189 +1,39 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: web-client
|
|
3
|
-
description:
|
|
4
|
-
user-invocable: false
|
|
3
|
+
description: Bind OwlMeans shared entrypoint protocols in a browser application. Use when registering client routes, attaching React screens, calling API protocols, or configuring the browser context.
|
|
5
4
|
---
|
|
6
5
|
<!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
|
|
7
6
|
|
|
8
|
-
#
|
|
7
|
+
# Browser protocol entrypoints
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
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.
|
|
20
|
-
|
|
21
|
-
## Key Exports
|
|
22
|
-
|
|
23
|
-
| Export | Description |
|
|
24
|
-
|--------|-------------|
|
|
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.
|
|
50
|
-
|
|
51
|
-
## Usage
|
|
52
|
-
|
|
53
|
-
### Entry point
|
|
54
|
-
```typescript
|
|
55
|
-
// index.tsx
|
|
56
|
-
import { renderApp } from '@owlmeans/web-client'
|
|
57
|
-
import { makeContext } from './context.js'
|
|
58
|
-
import { appEntrypoints } from './entrypoints.js'
|
|
59
|
-
import { MANAGER, MANAGER_API } from 'my-common'
|
|
60
|
-
import config from './config.js'
|
|
61
|
-
|
|
62
|
-
const context = makeContext(config)
|
|
63
|
-
context.registerEntrypoints(appEntrypoints)
|
|
64
|
-
context.serviceRoute(MANAGER, true)
|
|
65
|
-
context.serviceRoute(MANAGER_API, true)
|
|
66
|
-
renderApp<Config, Context>(context)
|
|
67
|
-
```
|
|
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
|
-
|
|
75
|
-
### Entrypoint elevation with React components
|
|
76
|
-
```typescript
|
|
77
|
-
import { elevate, route, frontend, handler, entrypoint } from '@owlmeans/web-client'
|
|
78
|
-
import { HomeScreen, ProjectDashboardScreen } from './screens/index.js'
|
|
79
|
-
|
|
80
|
-
elevate(entrypoints, manager.front.project.dashboard, handler(ProjectDashboardScreen))
|
|
81
|
-
|
|
82
|
-
entrypoints.push(
|
|
83
|
-
entrypoint(route(HOME, '/', frontend({ default: true, parent: BASE })), handler(HomeScreen))
|
|
84
|
-
)
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
`elevate` is idempotent — it replaces the declaration in the list, so re-elevating an alias (a
|
|
88
|
-
screen swapped for a variant build, a guard added to an already-elevated one) is allowed, and
|
|
89
|
-
guards named at elevation are unioned with the declared ones rather than replacing them. A backend
|
|
90
|
-
alias takes no handler at all: giving one is a wiring error and throws.
|
|
91
|
-
|
|
92
|
-
A **backend** alias the client calls needs a bare `elevate(entrypoints, alias)` — no handler. That
|
|
93
|
-
opts the alias into client-side callability, so:
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
const project = await context.entrypoint<ClientEntrypoint<Project>>(alias)
|
|
97
|
-
.call({ params, body, query }) // the value; the reply's error is thrown
|
|
98
|
-
const { value, outcome } = await context.entrypoint<ClientEntrypoint<Project>>(alias)
|
|
99
|
-
.invoke({ params }) // when the outcome decides what happens next
|
|
100
|
-
const href = await context.entrypoint<ClientEntrypoint<Project>>(alias)
|
|
101
|
-
.url({ params }, { absolute: true }) // the address, not the round trip
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
The auth header is attached automatically for guarded entrypoints. An entrypoint that renders a
|
|
105
|
-
screen answers `url()` and throws from `call()`/`invoke()` — a screen is navigated to, not called.
|
|
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
|
-
|
|
113
|
-
### Login comes with the context
|
|
114
|
-
|
|
115
|
-
`makeContext` calls `appendWebLogin`, which registers the login host (`@owlmeans/client-auth/login`)
|
|
116
|
-
and both browser plugins on it — redirect (`REDIRECT_LOGIN`, priority 0, applies always) and
|
|
117
|
-
surrogate window (`SURROGATE_LOGIN`, priority 100, applies in a frame or in the surrogate itself).
|
|
118
|
-
So every web app — and everything built on `web-panel` or `mui-panel` — has working sign-in,
|
|
119
|
-
including from an embedded app, with nothing registered by hand:
|
|
120
|
-
|
|
121
|
-
```typescript
|
|
122
|
-
import { useLogin, useLogout } from '@owlmeans/client-auth/login'
|
|
123
|
-
|
|
124
|
-
const [, onLogIn] = useLogin() // one control, one handler, no environment checks
|
|
125
|
-
```
|
|
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
|
-
|
|
132
|
-
An app that needs a different mechanic registers its own plugin at a higher priority after building
|
|
133
|
-
the base context (`context.login().registerPlugin(...)`); it never replaces these. The contract, the
|
|
134
|
-
cascade and the invariants are the [[login-plugins]] skill.
|
|
135
|
-
|
|
136
|
-
### Reading auth state
|
|
9
|
+
Shared protocol declarations are immutable. Bind a complete protocol tree for callable API routes,
|
|
10
|
+
then bind frontend declarations to screens.
|
|
137
11
|
|
|
138
12
|
```ts
|
|
139
|
-
import {
|
|
140
|
-
|
|
141
|
-
|
|
13
|
+
import { bindAll, bindScreen, entrypoints as frameworkEntrypoints, handler } from '@owlmeans/web-panel'
|
|
14
|
+
import { projectEntrypoints, webEntrypoints } from 'project-common'
|
|
15
|
+
import { ProjectScreen } from './screens/project.js'
|
|
16
|
+
|
|
17
|
+
export const appEntrypoints = [
|
|
18
|
+
...frameworkEntrypoints,
|
|
19
|
+
...bindAll(projectEntrypoints),
|
|
20
|
+
bindScreen(webEntrypoints.project, handler(ProjectScreen)),
|
|
21
|
+
]
|
|
142
22
|
```
|
|
143
23
|
|
|
144
|
-
|
|
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.
|
|
154
|
-
|
|
155
|
-
### Navigation
|
|
156
|
-
```typescript
|
|
157
|
-
import { useNavigate } from '@owlmeans/client' // not re-exported by web-client
|
|
158
|
-
import { useStoreList, useStoreModel } from '@owlmeans/client' // client state — also not re-exported
|
|
24
|
+
Call a protocol directly. Its request and response types come from its shared contract.
|
|
159
25
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
26
|
+
```ts
|
|
27
|
+
const project = await context.entrypoint(projectEntrypoints.get).call({
|
|
28
|
+
params: { id: projectId },
|
|
29
|
+
})
|
|
163
30
|
```
|
|
164
31
|
|
|
165
|
-
|
|
166
|
-
alias and never assembles a path. Render a plain `<a href>` the same way — `await
|
|
167
|
-
context.entrypoint(alias).url({ params })`, or the `Link` component from `@owlmeans/web-panel`,
|
|
168
|
-
which does it for you.
|
|
169
|
-
|
|
170
|
-
## Depends On
|
|
171
|
-
|
|
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
|
|
181
|
-
- `react`, `react-dom` (peer). It does not depend on `react-router` — opt into it with
|
|
182
|
-
`@owlmeans/web-router-react-router` (`appendReactRouter`). Routing needs no explicit provider:
|
|
183
|
-
`PanelApp`/`App` compile through `context.router()`, so nothing passes a `provide` prop.
|
|
184
|
-
|
|
185
|
-
## Related
|
|
32
|
+
## Rules
|
|
186
33
|
|
|
187
|
-
-
|
|
188
|
-
-
|
|
189
|
-
-
|
|
34
|
+
- Declare paths, guards, gates and contracts once in the shared protocol tree.
|
|
35
|
+
- Bind every API declaration the browser calls, including route parents.
|
|
36
|
+
- Use `bindScreen(protocol, handler(Component))` only for frontend route declarations.
|
|
37
|
+
- Do not replace request/response typing at a call site; update the shared contract instead.
|
|
38
|
+
- Keep framework entrypoints and application entrypoints in one registered array, without mutating a
|
|
39
|
+
shared declaration collection.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../../src/components/dispatcher/component.tsx"],"names":[],"mappings":"AAUA,eAAO,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../../src/components/dispatcher/component.tsx"],"names":[],"mappings":"AAUA,eAAO,MAAM,UAAU,6FA+FrB,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,
|
|
6
|
+
import { FallbackLoginScreen, LoginIntent, LoginOutcome, ResumeAction, resumeAction, } 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,13 +33,7 @@ export const Dispatcher = DispatcherHOC(({ provideToken, navigate }) => {
|
|
|
33
33
|
provideToken({ token }, params);
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
|
-
|
|
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) => {
|
|
36
|
+
void context.auth().authenticated().then(async (authzToken) => {
|
|
43
37
|
if (authzToken == null || authzToken === '') {
|
|
44
38
|
setChoose(true);
|
|
45
39
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component.js","sourceRoot":"","sources":["../../../src/components/dispatcher/component.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EACL,mBAAmB,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"component.js","sourceRoot":"","sources":["../../../src/components/dispatcher/component.tsx"],"names":[],"mappings":"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"}
|
package/build/entrypoints.d.ts
CHANGED
|
@@ -1,2 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Bound here rather than in a relying-party package on purpose.
|
|
3
|
+
*
|
|
4
|
+
* The surrogate mechanic is not OIDC-specific — it is "run the login route one window up and hand
|
|
5
|
+
* the result back" — and every web application already spreads this list. Putting the screen here
|
|
6
|
+
* means `web-panel`, `mui-panel` and every generated target gain a working login window with no
|
|
7
|
+
* edit of their own, which is the only way an application that is already deployed can get it.
|
|
8
|
+
*/
|
|
9
|
+
export declare const entrypoints: (import("@owlmeans/client-entrypoint").ClientProtocolEntrypoint<import("@owlmeans/entrypoint").EntrypointProtocol<{
|
|
10
|
+
query: import("@owlmeans/auth").AuthToken;
|
|
11
|
+
}, undefined>> | import("@owlmeans/client-entrypoint").ClientProtocolEntrypoint<import("@owlmeans/entrypoint").EntrypointProtocol<{
|
|
12
|
+
query: import("@owlmeans/auth-common").SurrogateQuery;
|
|
13
|
+
}, undefined>> | import("@owlmeans/client-entrypoint").ClientProtocolEntrypoint<import("@owlmeans/entrypoint").EntrypointProtocol<{
|
|
14
|
+
body: import("@owlmeans/auth").AuthToken;
|
|
15
|
+
}, import("@owlmeans/auth").AuthToken>>)[];
|
|
2
16
|
//# sourceMappingURL=entrypoints.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entrypoints.d.ts","sourceRoot":"","sources":["../src/entrypoints.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"entrypoints.d.ts","sourceRoot":"","sources":["../src/entrypoints.ts"],"names":[],"mappings":"AAOA;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW;;;;;;0CAIvB,CAAA"}
|
package/build/entrypoints.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { entrypoints as
|
|
3
|
-
import {
|
|
1
|
+
import { authProtocols } from '@owlmeans/auth-common';
|
|
2
|
+
import { entrypoints as authBindings } from '@owlmeans/client-auth';
|
|
3
|
+
import { bindScreen } from '@owlmeans/client-entrypoint';
|
|
4
4
|
import { handler } from '@owlmeans/client';
|
|
5
5
|
import { Dispatcher } from './components/dispatcher/component.js';
|
|
6
6
|
import { SurrogateScreen } from './login/screen.js';
|
|
7
|
-
elevate(list, DISPATCHER, handler(Dispatcher));
|
|
8
7
|
/**
|
|
9
|
-
*
|
|
8
|
+
* Bound here rather than in a relying-party package on purpose.
|
|
10
9
|
*
|
|
11
10
|
* The surrogate mechanic is not OIDC-specific — it is "run the login route one window up and hand
|
|
12
11
|
* the result back" — and every web application already spreads this list. Putting the screen here
|
|
13
12
|
* means `web-panel`, `mui-panel` and every generated target gain a working login window with no
|
|
14
13
|
* edit of their own, which is the only way an application that is already deployed can get it.
|
|
15
14
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
export const entrypoints = [
|
|
16
|
+
...authBindings,
|
|
17
|
+
bindScreen(authProtocols.dispatcher, handler(Dispatcher)),
|
|
18
|
+
bindScreen(authProtocols.surrogate, handler(SurrogateScreen)),
|
|
19
|
+
];
|
|
18
20
|
//# sourceMappingURL=entrypoints.js.map
|
package/build/entrypoints.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entrypoints.js","sourceRoot":"","sources":["../src/entrypoints.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"entrypoints.js","sourceRoot":"","sources":["../src/entrypoints.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,EAAE,WAAW,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAA;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAEnD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,GAAG,YAAY;IACf,UAAU,CAAC,aAAa,CAAC,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IACzD,UAAU,CAAC,aAAa,CAAC,SAAS,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;CAC9D,CAAA"}
|
package/build/exports.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { handler } from '@owlmeans/client';
|
|
|
2
2
|
export { config } from '@owlmeans/client-context';
|
|
3
3
|
export { service } from '@owlmeans/config';
|
|
4
4
|
export { AppType, HOME, ROOT, BASE, GUEST } from '@owlmeans/context';
|
|
5
|
-
export {
|
|
5
|
+
export { bind, bindAll, bindScreen, provideRequest, stab } from '@owlmeans/client-entrypoint';
|
|
6
6
|
export { route, frontend } from '@owlmeans/route';
|
|
7
7
|
export type { ClientEntrypoint } from '@owlmeans/client-entrypoint';
|
|
8
8
|
export type { ClientEntrypoint as Module } from '@owlmeans/client-entrypoint';
|
package/build/exports.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACpE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACpE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAA;AAC7F,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AACjD,YAAY,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AACnE,YAAY,EAAE,gBAAgB,IAAI,MAAM,EAAE,MAAM,6BAA6B,CAAA;AAC7E,YAAY,EAAE,WAAW,IAAI,KAAK,EAAE,MAAM,wBAAwB,CAAA"}
|
package/build/exports.js
CHANGED
|
@@ -2,6 +2,6 @@ export { handler } from '@owlmeans/client';
|
|
|
2
2
|
export { config } from '@owlmeans/client-context';
|
|
3
3
|
export { service } from '@owlmeans/config';
|
|
4
4
|
export { AppType, HOME, ROOT, BASE, GUEST } from '@owlmeans/context';
|
|
5
|
-
export {
|
|
5
|
+
export { bind, bindAll, bindScreen, provideRequest, stab } from '@owlmeans/client-entrypoint';
|
|
6
6
|
export { route, frontend } from '@owlmeans/route';
|
|
7
7
|
//# sourceMappingURL=exports.js.map
|
package/build/exports.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exports.js","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACpE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"exports.js","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACpE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAA;AAC7F,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA"}
|
package/build/helper.d.ts
CHANGED
|
@@ -1,22 +1,3 @@
|
|
|
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;
|
|
22
3
|
//# sourceMappingURL=helper.d.ts.map
|
package/build/helper.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"
|
|
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,WAAW,CAAC,SAKtH,CAAA"}
|
package/build/helper.js
CHANGED
|
@@ -1,41 +1,7 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
2
|
-
import { useContext } from '@owlmeans/client';
|
|
3
1
|
export const extractPrimaryHost = (context) => {
|
|
4
2
|
if (typeof window !== 'undefined') {
|
|
5
3
|
context.cfg.primaryHost = window.location.hostname;
|
|
6
4
|
context.cfg.primaryPort = window.location.port != null && window.location.port != '' ? parseInt(window.location.port) : undefined;
|
|
7
5
|
}
|
|
8
6
|
};
|
|
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
|
-
};
|
|
41
7
|
//# sourceMappingURL=helper.js.map
|
package/build/helper.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"
|
|
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"}
|
package/build/login/screen.d.ts
CHANGED
|
@@ -7,22 +7,9 @@ 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
|
|
11
|
-
*
|
|
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.
|
|
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.
|
|
26
13
|
*/
|
|
27
14
|
export declare const SurrogateScreen: FC;
|
|
28
15
|
//# 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;
|
|
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"}
|
package/build/login/screen.js
CHANGED
|
@@ -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,
|
|
5
|
+
import { LoginIntent, LoginOutcome, 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,22 +12,9 @@ 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
|
|
16
|
-
*
|
|
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.
|
|
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.
|
|
31
18
|
*/
|
|
32
19
|
export const SurrogateScreen = () => {
|
|
33
20
|
const context = useContext();
|
|
@@ -64,6 +51,7 @@ export const SurrogateScreen = () => {
|
|
|
64
51
|
return;
|
|
65
52
|
}
|
|
66
53
|
const run = async () => {
|
|
54
|
+
const token = await context.auth().authenticated();
|
|
67
55
|
if (intent === LoginIntent.Logout) {
|
|
68
56
|
// Revoked FIRST, before anything can be awaited on a channel that may never answer: a
|
|
69
57
|
// window the user closes half a second later must still have ended the session it opened
|
|
@@ -75,22 +63,28 @@ export const SurrogateScreen = () => {
|
|
|
75
63
|
: SurrogateStage.Failed);
|
|
76
64
|
return;
|
|
77
65
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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.
|
|
82
78
|
const next = query.get(LOGIN_NEXT_QUERY);
|
|
83
79
|
if (next == null || next === '') {
|
|
84
80
|
setStage(SurrogateStage.Gesture);
|
|
85
81
|
return;
|
|
86
82
|
}
|
|
87
83
|
const method = query.get(LOGIN_METHOD_QUERY);
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
params.set(LOGIN_FRESH_QUERY, '1');
|
|
93
|
-
window.location.href = `${next}${next.includes('?') ? '&' : '?'}${params.toString()}`;
|
|
84
|
+
const target = method != null && method !== ''
|
|
85
|
+
? `${next}${next.includes('?') ? '&' : '?'}${LOGIN_METHOD_QUERY}=${encodeURIComponent(method)}`
|
|
86
|
+
: next;
|
|
87
|
+
window.location.href = target;
|
|
94
88
|
};
|
|
95
89
|
void run().catch((e) => {
|
|
96
90
|
console.error(e);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screen.js","sourceRoot":"","sources":["../../src/login/screen.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAExD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EACL,WAAW,EAAE,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"screen.js","sourceRoot":"","sources":["../../src/login/screen.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/web-client",
|
|
3
|
-
"version": "0.1.18-rc.
|
|
3
|
+
"version": "0.1.18-rc.27",
|
|
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.
|
|
25
|
-
"@owlmeans/auth-common": "^0.1.18-rc.
|
|
26
|
-
"@owlmeans/client": "^0.1.18-rc.
|
|
27
|
-
"@owlmeans/client-auth": "^0.1.18-rc.
|
|
28
|
-
"@owlmeans/client-context": "^0.1.18-rc.
|
|
29
|
-
"@owlmeans/client-i18n": "^0.1.18-rc.
|
|
30
|
-
"@owlmeans/client-entrypoint": "^0.1.18-rc.
|
|
31
|
-
"@owlmeans/client-resource": "^0.1.18-rc.
|
|
32
|
-
"@owlmeans/client-route": "^0.1.18-rc.
|
|
33
|
-
"@owlmeans/config": "^0.1.18-rc.
|
|
34
|
-
"@owlmeans/context": "^0.1.18-rc.
|
|
35
|
-
"@owlmeans/error": "^0.1.18-rc.
|
|
36
|
-
"@owlmeans/i18n": "^0.1.18-rc.
|
|
37
|
-
"@owlmeans/route": "^0.1.18-rc.
|
|
38
|
-
"@owlmeans/web-db": "^0.1.18-rc.
|
|
39
|
-
"@owlmeans/web-router": "^0.1.18-rc.
|
|
24
|
+
"@owlmeans/auth": "^0.1.18-rc.12",
|
|
25
|
+
"@owlmeans/auth-common": "^0.1.18-rc.16",
|
|
26
|
+
"@owlmeans/client": "^0.1.18-rc.19",
|
|
27
|
+
"@owlmeans/client-auth": "^0.1.18-rc.23",
|
|
28
|
+
"@owlmeans/client-context": "^0.1.18-rc.16",
|
|
29
|
+
"@owlmeans/client-i18n": "^0.1.18-rc.18",
|
|
30
|
+
"@owlmeans/client-entrypoint": "^0.1.18-rc.16",
|
|
31
|
+
"@owlmeans/client-resource": "^0.1.18-rc.16",
|
|
32
|
+
"@owlmeans/client-route": "^0.1.18-rc.16",
|
|
33
|
+
"@owlmeans/config": "^0.1.18-rc.15",
|
|
34
|
+
"@owlmeans/context": "^0.1.18-rc.11",
|
|
35
|
+
"@owlmeans/error": "^0.1.18-rc.11",
|
|
36
|
+
"@owlmeans/i18n": "^0.1.18-rc.11",
|
|
37
|
+
"@owlmeans/route": "^0.1.18-rc.12",
|
|
38
|
+
"@owlmeans/web-db": "^0.1.18-rc.17",
|
|
39
|
+
"@owlmeans/web-router": "^0.1.18-rc.17",
|
|
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,
|
|
7
7
|
} from '@owlmeans/client-auth/login'
|
|
8
8
|
import { LoginSurrogateView, SurrogateStage } from '../../login/view.js'
|
|
9
9
|
import { useContext } from '../../context.js'
|
|
@@ -42,14 +42,7 @@ export const Dispatcher = DispatcherHOC(({ provideToken, navigate }) => {
|
|
|
42
42
|
return
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
|
|
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 => {
|
|
45
|
+
void context.auth().authenticated().then(async authzToken => {
|
|
53
46
|
if (authzToken == null || authzToken === '') {
|
|
54
47
|
setChoose(true)
|
|
55
48
|
|
package/src/entrypoints.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { entrypoints as
|
|
3
|
-
import {
|
|
1
|
+
import { authProtocols } from '@owlmeans/auth-common'
|
|
2
|
+
import { entrypoints as authBindings } from '@owlmeans/client-auth'
|
|
3
|
+
import { bindScreen } from '@owlmeans/client-entrypoint'
|
|
4
4
|
import { handler } from '@owlmeans/client'
|
|
5
5
|
import { Dispatcher } from './components/dispatcher/component.js'
|
|
6
6
|
import { SurrogateScreen } from './login/screen.js'
|
|
7
7
|
|
|
8
|
-
elevate(list, DISPATCHER, handler(Dispatcher))
|
|
9
|
-
|
|
10
8
|
/**
|
|
11
|
-
*
|
|
9
|
+
* Bound here rather than in a relying-party package on purpose.
|
|
12
10
|
*
|
|
13
11
|
* The surrogate mechanic is not OIDC-specific — it is "run the login route one window up and hand
|
|
14
12
|
* the result back" — and every web application already spreads this list. Putting the screen here
|
|
15
13
|
* means `web-panel`, `mui-panel` and every generated target gain a working login window with no
|
|
16
14
|
* edit of their own, which is the only way an application that is already deployed can get it.
|
|
17
15
|
*/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
export const entrypoints = [
|
|
17
|
+
...authBindings,
|
|
18
|
+
bindScreen(authProtocols.dispatcher, handler(Dispatcher)),
|
|
19
|
+
bindScreen(authProtocols.surrogate, handler(SurrogateScreen)),
|
|
20
|
+
]
|
package/src/exports.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { handler } from '@owlmeans/client'
|
|
|
3
3
|
export { config } from '@owlmeans/client-context'
|
|
4
4
|
export { service } from '@owlmeans/config'
|
|
5
5
|
export { AppType, HOME, ROOT, BASE, GUEST } from '@owlmeans/context'
|
|
6
|
-
export {
|
|
6
|
+
export { bind, bindAll, bindScreen, provideRequest, stab } from '@owlmeans/client-entrypoint'
|
|
7
7
|
export { route, frontend } from '@owlmeans/route'
|
|
8
8
|
export type { ClientEntrypoint } from '@owlmeans/client-entrypoint'
|
|
9
9
|
export type { ClientEntrypoint as Module } from '@owlmeans/client-entrypoint'
|
package/src/helper.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react'
|
|
2
|
-
import { useContext } from '@owlmeans/client'
|
|
3
1
|
import type { AppConfig, AppContext } from './types.js'
|
|
4
2
|
|
|
5
3
|
export const extractPrimaryHost = <C extends AppConfig = AppConfig, T extends AppContext<C> = AppContext<C>>(context: T) => {
|
|
@@ -8,39 +6,3 @@ export const extractPrimaryHost = <C extends AppConfig = AppConfig, T extends Ap
|
|
|
8
6
|
context.cfg.primaryPort = window.location.port != null && window.location.port != '' ? parseInt(window.location.port) : undefined
|
|
9
7
|
}
|
|
10
8
|
}
|
|
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
|
-
}
|
package/src/login/screen.tsx
CHANGED
|
@@ -3,8 +3,7 @@ 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,
|
|
7
|
-
LOGIN_NEXT_QUERY,
|
|
6
|
+
LoginIntent, LoginOutcome, LOGIN_INTENT_QUERY, LOGIN_METHOD_QUERY, LOGIN_NEXT_QUERY,
|
|
8
7
|
} from '@owlmeans/client-auth/login'
|
|
9
8
|
import type { LoginService } from '@owlmeans/client-auth/login'
|
|
10
9
|
import type { AppContext } from '../types.js'
|
|
@@ -18,22 +17,9 @@ import { LoginSurrogateView, SurrogateStage } from './view.js'
|
|
|
18
17
|
* application, with its navigation, inside itself. This screen has no continuation at all — it
|
|
19
18
|
* either hands something back and closes, or it says what it is waiting for.
|
|
20
19
|
*
|
|
21
|
-
* It
|
|
22
|
-
*
|
|
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.
|
|
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.
|
|
37
23
|
*/
|
|
38
24
|
export const SurrogateScreen: FC = () => {
|
|
39
25
|
const context = useContext() as unknown as AppContext
|
|
@@ -76,6 +62,8 @@ export const SurrogateScreen: FC = () => {
|
|
|
76
62
|
}
|
|
77
63
|
|
|
78
64
|
const run = async (): Promise<void> => {
|
|
65
|
+
const token = await context.auth().authenticated()
|
|
66
|
+
|
|
79
67
|
if (intent === LoginIntent.Logout) {
|
|
80
68
|
// Revoked FIRST, before anything can be awaited on a channel that may never answer: a
|
|
81
69
|
// window the user closes half a second later must still have ended the session it opened
|
|
@@ -90,22 +78,31 @@ export const SurrogateScreen: FC = () => {
|
|
|
90
78
|
return
|
|
91
79
|
}
|
|
92
80
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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.
|
|
97
96
|
const next = query.get(LOGIN_NEXT_QUERY)
|
|
98
97
|
if (next == null || next === '') {
|
|
99
98
|
setStage(SurrogateStage.Gesture)
|
|
100
99
|
return
|
|
101
100
|
}
|
|
102
101
|
const method = query.get(LOGIN_METHOD_QUERY)
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
params.set(LOGIN_FRESH_QUERY, '1')
|
|
108
|
-
window.location.href = `${next}${next.includes('?') ? '&' : '?'}${params.toString()}`
|
|
102
|
+
const target = method != null && method !== ''
|
|
103
|
+
? `${next}${next.includes('?') ? '&' : '?'}${LOGIN_METHOD_QUERY}=${encodeURIComponent(method)}`
|
|
104
|
+
: next
|
|
105
|
+
window.location.href = target
|
|
109
106
|
}
|
|
110
107
|
|
|
111
108
|
void run().catch((e: Error) => {
|