@owlmeans/client-auth 0.1.18-rc.4 → 0.1.18-rc.41
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 +280 -37
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/client-auth/SKILL.md +139 -21
- package/build/components/dispatcher/component.d.ts.map +1 -1
- package/build/components/dispatcher/component.js +14 -3
- package/build/components/dispatcher/component.js.map +1 -1
- package/build/entrypoints.d.ts +7 -0
- package/build/entrypoints.d.ts.map +1 -0
- package/build/entrypoints.js +7 -0
- package/build/entrypoints.js.map +1 -0
- package/build/helper.d.ts +2 -1
- package/build/helper.d.ts.map +1 -1
- package/build/helper.js +24 -3
- package/build/helper.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/build/login/adopt.d.ts +24 -0
- package/build/login/adopt.d.ts.map +1 -0
- package/build/login/adopt.js +28 -0
- package/build/login/adopt.js.map +1 -0
- package/build/login/consts.d.ts +49 -0
- package/build/login/consts.d.ts.map +1 -0
- package/build/login/consts.js +49 -0
- package/build/login/consts.js.map +1 -0
- package/build/login/credit.d.ts +24 -0
- package/build/login/credit.d.ts.map +1 -0
- package/build/login/credit.js +58 -0
- package/build/login/credit.js.map +1 -0
- package/build/login/env.d.ts +32 -0
- package/build/login/env.d.ts.map +1 -0
- package/build/login/env.js +84 -0
- package/build/login/env.js.map +1 -0
- package/build/login/flow.d.ts +20 -0
- package/build/login/flow.d.ts.map +1 -0
- package/build/login/flow.js +39 -0
- package/build/login/flow.js.map +1 -0
- package/build/login/hook.d.ts +37 -0
- package/build/login/hook.d.ts.map +1 -0
- package/build/login/hook.js +72 -0
- package/build/login/hook.js.map +1 -0
- package/build/login/i18n/be.json +37 -0
- package/build/login/i18n/de.json +37 -0
- package/build/login/i18n/en.json +37 -0
- package/build/login/i18n/es.json +37 -0
- package/build/login/i18n/fr.json +37 -0
- package/build/login/i18n/pl.json +37 -0
- package/build/login/i18n/ru.json +37 -0
- package/build/login/i18n/uk.json +37 -0
- package/build/login/i18n.d.ts +2 -0
- package/build/login/i18n.d.ts.map +1 -0
- package/build/login/i18n.js +27 -0
- package/build/login/i18n.js.map +1 -0
- package/build/login/index.d.ts +15 -0
- package/build/login/index.d.ts.map +1 -0
- package/build/login/index.js +15 -0
- package/build/login/index.js.map +1 -0
- package/build/login/methods.d.ts +25 -0
- package/build/login/methods.d.ts.map +1 -0
- package/build/login/methods.js +97 -0
- package/build/login/methods.js.map +1 -0
- package/build/login/resume.d.ts +30 -0
- package/build/login/resume.d.ts.map +1 -0
- package/build/login/resume.js +57 -0
- package/build/login/resume.js.map +1 -0
- package/build/login/screen.d.ts +17 -0
- package/build/login/screen.d.ts.map +1 -0
- package/build/login/screen.js +76 -0
- package/build/login/screen.js.map +1 -0
- package/build/login/service.d.ts +31 -0
- package/build/login/service.d.ts.map +1 -0
- package/build/login/service.js +148 -0
- package/build/login/service.js.map +1 -0
- package/build/login/surrogate.d.ts +20 -0
- package/build/login/surrogate.d.ts.map +1 -0
- package/build/login/surrogate.js +33 -0
- package/build/login/surrogate.js.map +1 -0
- package/build/login/terms.d.ts +20 -0
- package/build/login/terms.d.ts.map +1 -0
- package/build/login/terms.js +0 -0
- package/build/login/terms.js.map +1 -0
- package/build/login/types.d.ts +256 -0
- package/build/login/types.d.ts.map +1 -0
- package/build/login/types.js +31 -0
- package/build/login/types.js.map +1 -0
- package/build/manager/components/authentication/control.js +3 -3
- package/build/manager/components/authentication/control.js.map +1 -1
- package/build/manager/components/authentication/types.d.ts +2 -2
- package/build/manager/components/authentication/types.d.ts.map +1 -1
- package/build/manager/components/tunnel-consumer.js +2 -2
- package/build/manager/components/tunnel-consumer.js.map +1 -1
- package/build/manager/entrypoints.d.ts +11 -0
- package/build/manager/entrypoints.d.ts.map +1 -0
- package/build/manager/entrypoints.js +17 -0
- package/build/manager/entrypoints.js.map +1 -0
- package/build/manager/plugins/basic-ed25519.d.ts.map +1 -1
- package/build/manager/plugins/basic-ed25519.js +5 -0
- package/build/manager/plugins/basic-ed25519.js.map +1 -1
- package/build/manager/plugins/exports.d.ts +2 -0
- package/build/manager/plugins/exports.d.ts.map +1 -1
- package/build/manager/plugins/exports.js +2 -0
- package/build/manager/plugins/exports.js.map +1 -1
- package/build/manager/plugins/index.d.ts +1 -5
- package/build/manager/plugins/index.d.ts.map +1 -1
- package/build/manager/plugins/index.js +9 -1
- package/build/manager/plugins/index.js.map +1 -1
- package/build/manager/plugins/methods.d.ts +6 -0
- package/build/manager/plugins/methods.d.ts.map +1 -0
- package/build/manager/plugins/methods.js +65 -0
- package/build/manager/plugins/methods.js.map +1 -0
- package/build/manager/plugins/re-captcha.d.ts.map +1 -1
- package/build/manager/plugins/re-captcha.js +5 -0
- package/build/manager/plugins/re-captcha.js.map +1 -1
- package/build/manager/plugins/registry.d.ts +20 -0
- package/build/manager/plugins/registry.d.ts.map +1 -0
- package/build/manager/plugins/registry.js +19 -0
- package/build/manager/plugins/registry.js.map +1 -0
- package/build/manager/plugins/tunnel-consumer.d.ts.map +1 -1
- package/build/manager/plugins/tunnel-consumer.js +8 -1
- package/build/manager/plugins/tunnel-consumer.js.map +1 -1
- package/build/manager/plugins/types.d.ts +53 -0
- package/build/manager/plugins/types.d.ts.map +1 -1
- package/build/service.js +1 -1
- package/build/service.js.map +1 -1
- package/package.json +36 -27
- package/src/components/dispatcher/component.tsx +16 -3
- package/src/entrypoints.ts +10 -0
- package/src/helper.ts +26 -3
- package/src/index.ts +1 -1
- package/src/login/adopt.ts +31 -0
- package/src/login/consts.ts +64 -0
- package/src/login/credit.ts +75 -0
- package/src/login/env.ts +86 -0
- package/src/login/flow.ts +44 -0
- package/src/login/hook.ts +80 -0
- package/src/login/i18n/be.json +37 -0
- package/src/login/i18n/de.json +37 -0
- package/src/login/i18n/en.json +37 -0
- package/src/login/i18n/es.json +37 -0
- package/src/login/i18n/fr.json +37 -0
- package/src/login/i18n/pl.json +37 -0
- package/src/login/i18n/ru.json +37 -0
- package/src/login/i18n/uk.json +37 -0
- package/src/login/i18n.ts +28 -0
- package/src/login/index.ts +15 -0
- package/src/login/methods.ts +113 -0
- package/src/login/resume.ts +58 -0
- package/src/login/screen.tsx +136 -0
- package/src/login/service.ts +195 -0
- package/src/login/surrogate.ts +45 -0
- package/src/login/terms.ts +0 -0
- package/src/login/types.ts +279 -0
- package/src/manager/README.md +37 -80
- package/src/manager/components/authentication/control.ts +3 -3
- package/src/manager/components/authentication/types.ts +2 -2
- package/src/manager/components/tunnel-consumer.tsx +2 -2
- package/src/manager/entrypoints.ts +18 -0
- package/src/manager/plugins/basic-ed25519.tsx +7 -0
- package/src/manager/plugins/exports.ts +2 -0
- package/src/manager/plugins/index.ts +10 -4
- package/src/manager/plugins/methods.ts +74 -0
- package/src/manager/plugins/re-captcha.tsx +7 -0
- package/src/manager/plugins/registry.ts +26 -0
- package/src/manager/plugins/tunnel-consumer.tsx +10 -1
- package/src/manager/plugins/types.ts +54 -0
- package/src/service.ts +1 -1
- package/tests/context.ts +1 -2
- package/tests/login.spec.ts +332 -0
- package/build/.gitkeep +0 -0
- package/build/manager/modules.d.ts +0 -2
- package/build/manager/modules.d.ts.map +0 -1
- package/build/manager/modules.js +0 -16
- package/build/manager/modules.js.map +0 -1
- package/build/modules.d.ts +0 -4
- package/build/modules.d.ts.map +0 -1
- package/build/modules.js +0 -9
- package/build/modules.js.map +0 -1
- package/src/manager/modules.ts +0 -18
- package/src/modules.ts +0 -13
package/README.md
CHANGED
|
@@ -1,72 +1,315 @@
|
|
|
1
1
|
# @owlmeans/client-auth
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
The browser side of OwlMeans authentication: the client `AuthService` that holds the bearer token,
|
|
4
|
+
the dispatcher HOC that handles the authentication return leg, the authentication-plugin registry
|
|
5
|
+
and screen (`./manager`), and the login-plugin host with its sign-in method registry, terms
|
|
6
|
+
confirmation and `useLogin` / `useLogout` hooks (`./login`). A web app normally gets the auth
|
|
7
|
+
service and the login host from `@owlmeans/web-client`'s `makeContext`, and the styled sign-in
|
|
8
|
+
screen from `@owlmeans/web-panel`. It imports this package for the hooks, the dispatcher contract
|
|
9
|
+
and plugin authoring. The OIDC relying party is `@owlmeans/web-oidc-rp` (`@owlmeans/mui-oidc-rp` is
|
|
10
|
+
legacy). The server-side counterpart is `@owlmeans/server-auth`.
|
|
11
11
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
bun add @owlmeans/client-auth
|
|
15
|
+
bun add @owlmeans/client-auth@^0.1.18-rc.41
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
## Concepts
|
|
19
|
+
|
|
20
|
+
- **Auth service** — `context.auth()`. The token lives in the `AUTH_RESOURCE` resource under the
|
|
21
|
+
single id `USER_ID`. `authenticated()` resolves to the token or `null`, `user()` returns the
|
|
22
|
+
decoded `Auth` (throwing `AuthorizationError` when there is none), and `update(token | undefined)`
|
|
23
|
+
adopts or drops a session.
|
|
24
|
+
- **Dispatcher** — `DispatcherHOC` wraps a renderer that reads the return-leg query. It hands the
|
|
25
|
+
renderer `provideToken(token, query)` and `navigate()`, adopts the token, and strips `AUTH_QUERY`
|
|
26
|
+
before navigating on.
|
|
27
|
+
- **Authentication plugin** (`./manager/plugins`) — *how* a person proves identity: `type`,
|
|
28
|
+
`Implementation`, an optional `Renderer` a UI package supplies, and `method` metadata when it is
|
|
29
|
+
offered on the sign-in screen.
|
|
30
|
+
- **Login plugin** (`./login`) — *where* the round trip runs. Plugins are selected by `LoginEnv`
|
|
31
|
+
(embedded, surrogate) and priority. `@owlmeans/web-client` registers the redirect and surrogate
|
|
32
|
+
plugins.
|
|
33
|
+
- **Login method** — one offerable button. `LoginMethodSource`s produce candidates;
|
|
34
|
+
`resolveLoginMethods` drops restricted ones the config did not name and applies the configured
|
|
35
|
+
order.
|
|
36
|
+
- **Login outcome** — `LoginOutcome` (`Handled`, `Passed`, `Redirected`, `Gesture`, `Orphaned`,
|
|
37
|
+
`Failed`) tells the caller what to do next. `resumeAction` and `loginAttemptError` are the only
|
|
38
|
+
readings of it.
|
|
39
|
+
|
|
18
40
|
## Usage
|
|
19
41
|
|
|
20
|
-
|
|
42
|
+
### 1. A context built by hand
|
|
43
|
+
|
|
44
|
+
`@owlmeans/web-client`'s `makeContext` already does this. Only a context composed without it
|
|
45
|
+
registers the pieces itself.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { makeClientContext } from '@owlmeans/client'
|
|
49
|
+
import { appendAuthService, bindExternalAuthentication, entrypoints as authEntrypoints } from '@owlmeans/client-auth'
|
|
50
|
+
import { appendLogin } from '@owlmeans/client-auth/login'
|
|
51
|
+
import { MY_APP_WEB } from 'my-app-common'
|
|
52
|
+
import type { Config, Context } from './types.js'
|
|
53
|
+
|
|
54
|
+
export const makeContext = <C extends Config, T extends Context<C>>(cfg: C): T => {
|
|
55
|
+
const context = makeClientContext(cfg) as T
|
|
56
|
+
appendAuthService<C, T>(context) // registers under DEFAULT_ALIAS ('auth'), exposes context.auth()
|
|
57
|
+
appendLogin<C, T>(context) // registers the login host, exposes context.login()
|
|
58
|
+
|
|
59
|
+
return context
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const clientBindings = [
|
|
63
|
+
...authEntrypoints,
|
|
64
|
+
// The screen an external provider redirects into, pinned to this app's service.
|
|
65
|
+
bindExternalAuthentication(MY_APP_WEB),
|
|
66
|
+
]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2. Guarding a layout and reading the identity
|
|
21
70
|
|
|
22
|
-
```
|
|
71
|
+
```tsx
|
|
72
|
+
import { entitySlugOf } from '@owlmeans/auth'
|
|
23
73
|
import { useSelfAuth } from '@owlmeans/client-auth'
|
|
74
|
+
import type { FC, PropsWithChildren } from 'react'
|
|
75
|
+
import { useContext } from '../context.js'
|
|
24
76
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
77
|
+
export const UserLayout: FC<PropsWithChildren> = ({ children }) => {
|
|
78
|
+
// `true` navigates to DISPATCHER when there is no session.
|
|
79
|
+
const authenticated = useSelfAuth(true)
|
|
80
|
+
const context = useContext()
|
|
81
|
+
|
|
82
|
+
if (!authenticated) {
|
|
83
|
+
return null
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const auth = context.auth().user()
|
|
87
|
+
// The organization is its renameable entitySlug; the stable entityId never reaches the browser.
|
|
88
|
+
const organization = entitySlugOf(auth)
|
|
89
|
+
|
|
90
|
+
return <main data-organization={organization}>{children}</main>
|
|
28
91
|
}
|
|
29
92
|
```
|
|
30
93
|
|
|
31
|
-
|
|
94
|
+
### 3. Sign-in and sign-out controls
|
|
32
95
|
|
|
33
|
-
```
|
|
34
|
-
import {
|
|
96
|
+
```tsx
|
|
97
|
+
import type { FC } from 'react'
|
|
98
|
+
import { useLogin, useLogout } from '@owlmeans/client-auth/login'
|
|
35
99
|
|
|
36
|
-
|
|
37
|
-
|
|
100
|
+
export const HeaderActions: FC<{ authenticated: boolean | null }> = ({ authenticated }) => {
|
|
101
|
+
const [loginPath, onLogIn] = useLogin()
|
|
102
|
+
const onLogOut = useLogout()
|
|
103
|
+
|
|
104
|
+
if (authenticated == null) {
|
|
105
|
+
return null
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return authenticated
|
|
109
|
+
? <button type="button" onClick={onLogOut}>Log out</button>
|
|
110
|
+
: <a href={loginPath} onClick={onLogIn}>Log in</a>
|
|
111
|
+
}
|
|
38
112
|
```
|
|
39
113
|
|
|
114
|
+
One control and one handler, with no conditions around them. The login service decides whether the
|
|
115
|
+
flow redirects this tab or runs one window up, because the app is framed.
|
|
116
|
+
|
|
117
|
+
### 4. Offering a sign-in method and gating the flow
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
import { ensureLoginService, LoginOutcome } from '@owlmeans/client-auth/login'
|
|
121
|
+
import type { LoginMethodSource, LoginPrecondition } from '@owlmeans/client-auth/login'
|
|
122
|
+
|
|
123
|
+
const partnerMethods: LoginMethodSource = {
|
|
124
|
+
alias: 'my-app-partner-methods',
|
|
125
|
+
list: () => [{
|
|
126
|
+
id: 'partner-sso',
|
|
127
|
+
label: 'Partner SSO',
|
|
128
|
+
i18nKey: 'partner',
|
|
129
|
+
icon: 'building',
|
|
130
|
+
order: 20,
|
|
131
|
+
// Callable synchronously from the click: leave before the first await.
|
|
132
|
+
start: () => {
|
|
133
|
+
window.location.href = partnerAuthorizeUrl()
|
|
134
|
+
|
|
135
|
+
return Promise.resolve(LoginOutcome.Redirected)
|
|
136
|
+
},
|
|
137
|
+
}],
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const maintenanceWindow: LoginPrecondition = {
|
|
141
|
+
alias: 'my-app-maintenance',
|
|
142
|
+
// Synchronous by contract — a precondition that asks a server belongs elsewhere.
|
|
143
|
+
check: () => !isMaintenanceBannerOpen(),
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export const appendPartnerLogin = <C extends Config, T extends Context<C>>(context: T): T => {
|
|
147
|
+
const login = ensureLoginService(context)
|
|
148
|
+
login.registerMethodSource(partnerMethods)
|
|
149
|
+
login.registerPrecondition(maintenanceWindow)
|
|
150
|
+
|
|
151
|
+
return context
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### 5. Authoring an authentication plugin
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
import { AuthenticationStage } from '@owlmeans/auth'
|
|
159
|
+
import type { AuthenticationRendererProps } from '@owlmeans/client-auth/manager'
|
|
160
|
+
import { registerAuthPlugin } from '@owlmeans/client-auth/manager/plugins'
|
|
161
|
+
import type { AuthenticationPlugin } from '@owlmeans/client-auth/manager/plugins'
|
|
162
|
+
import { useEffect } from 'react'
|
|
163
|
+
|
|
164
|
+
const MY_APP_PASSKEY = 'my-app-passkey'
|
|
165
|
+
|
|
166
|
+
export const passkeyPlugin: AuthenticationPlugin = {
|
|
167
|
+
type: MY_APP_PASSKEY,
|
|
168
|
+
method: { label: 'Passkey', icon: 'key', order: 50 },
|
|
169
|
+
// The form comes from a UI package; without one the screen must not offer the method.
|
|
170
|
+
requiresRenderer: true,
|
|
171
|
+
Implementation: Renderer => ({ type, stage, control, params }: AuthenticationRendererProps) => {
|
|
172
|
+
useEffect(() => {
|
|
173
|
+
if (control.stage === AuthenticationStage.Init) {
|
|
174
|
+
void control.requestAllowence()
|
|
175
|
+
}
|
|
176
|
+
}, [type])
|
|
177
|
+
|
|
178
|
+
if (Renderer == null) {
|
|
179
|
+
throw new SyntaxError(`Renderer is not defined for ${MY_APP_PASSKEY}`)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return <Renderer type={type} stage={stage} control={control} params={params} />
|
|
183
|
+
},
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
registerAuthPlugin(passkeyPlugin)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
A UI package then assigns `plugins[MY_APP_PASSKEY].Renderer`, and the renderer calls
|
|
190
|
+
`control.authenticate({ userId, credential })` once the person has answered the challenge.
|
|
191
|
+
|
|
40
192
|
## API
|
|
41
193
|
|
|
42
|
-
### `
|
|
194
|
+
### `@owlmeans/client-auth`
|
|
195
|
+
|
|
196
|
+
| Symbol | Kind | Purpose |
|
|
197
|
+
|---|---|---|
|
|
198
|
+
| `makeAuthService(alias?)` | function | The client `AuthService` — `authenticate`, `update`, `authenticated`, `user`, `store` |
|
|
199
|
+
| `appendAuthService(ctx, alias?)` | function | Register it and `authMiddleware`; expose `context.auth()` |
|
|
200
|
+
| `entrypoints` | const | `[bind(authProtocols.dispatcherAuthenticate)]` |
|
|
201
|
+
| `bindExternalAuthentication(service)` | function | Bind `authProtocols.flowEnter` as a URL-only screen on the given service |
|
|
202
|
+
| `useSelfAuth(force = true)` | hook | Resolves to `boolean`; navigates to `DISPATCHER` when unauthenticated and `force` is set |
|
|
203
|
+
| `useWs(entrypoint, request?, options?)` | hook | `@owlmeans/client-socket`'s `useWs` with the token in `AUTH_QUERY`, refreshed before every reconnect unless the caller supplied its own |
|
|
204
|
+
| `DispatcherHOC` | HOC | The return-leg wrapper — see *Concepts* |
|
|
205
|
+
| `DispatcherProps`, `TDispatcherHOC`, `DispatcherRenderer`, `DispatcherRendererProps` | type | Dispatcher contract |
|
|
206
|
+
| `DEFAULT_ALIAS` | const | `'auth'` — the service alias and the client guard alias |
|
|
207
|
+
| `AUTH_RESOURCE`, `USER_ID` | const | `'auth'` resource and `'user'` record id holding the token |
|
|
208
|
+
| `DEFAULT_ENTITY` | const | `'owlmeans'` |
|
|
209
|
+
| `AuthServiceAppend`, `ClientAuthRecord`, `ClientAuthResource` | type | `context.auth()` and the stored record |
|
|
210
|
+
|
|
211
|
+
### `@owlmeans/client-auth/manager`
|
|
212
|
+
|
|
213
|
+
Importing it registers `basic-ed25519`, `re-captcha` and `wallet-consumer`, plus
|
|
214
|
+
`pluginMethodSource`, by side effect.
|
|
215
|
+
|
|
216
|
+
| Symbol | Kind | Purpose |
|
|
217
|
+
|---|---|---|
|
|
218
|
+
| `plugins`, `registerAuthPlugin`, `getAuthPlugin`, `listAuthPlugins` | registry | The module-global authentication-plugin registry |
|
|
219
|
+
| `AuthenticationHOC(Renderer?, type?)` | HOC | The authentication screen hosting a plugin |
|
|
220
|
+
| `AuthenticationProps`, `TAuthenticationHOC` | type | Screen props (`type?`, `callback?`, `source?`) |
|
|
221
|
+
| `makeControl(context, callback?)` | function | The control a plugin drives: `requestAllowence`, `authenticate`, `persist` / `restore` / `hasPersistentState` / `cleanUpState`, `setError`, `flow` |
|
|
222
|
+
| `AuthenticationControl`, `AuthenticationControlState` | type | Control shape and the state persisted across a provider redirect |
|
|
223
|
+
| `AuthenticationRenderer`, `AuthenticationRendererProps`, `ClientAuthType`, `ClientAuthenticationMethod`, `AuthenticationCallback` | type | Rendering contract |
|
|
224
|
+
| `TunnelConsumer`, `TunnelAuthenticationProps`, `TunnelAuthCallback` | component, type | Wallet-tunnel consumer screen |
|
|
225
|
+
| `AuthenCredError` | class | The entered credential cannot be used |
|
|
226
|
+
| `CONTROL_STATE_ID` | const | Resource id of the persisted control state |
|
|
227
|
+
|
|
228
|
+
### `@owlmeans/client-auth/manager/entrypoints`
|
|
43
229
|
|
|
44
|
-
|
|
230
|
+
| Symbol | Kind | Purpose |
|
|
231
|
+
|---|---|---|
|
|
232
|
+
| `entrypoints` | const | The authentication manager app's bindings: `authen`, `init`, `authenticate`, `rely`, `client`, `login`, the `loginDefault` / `loginTyped` screens and the dispatcher |
|
|
45
233
|
|
|
46
|
-
### `
|
|
234
|
+
### `@owlmeans/client-auth/manager/plugins`
|
|
47
235
|
|
|
48
|
-
|
|
236
|
+
The plugin-authoring surface, with no registration side effect.
|
|
49
237
|
|
|
50
|
-
|
|
238
|
+
| Symbol | Kind | Purpose |
|
|
239
|
+
|---|---|---|
|
|
240
|
+
| `AuthenticationPlugin` | type | `type`, `Implementation`, `Renderer?`, `requiresRenderer?`, `method?`, `authenticate` / `beforeAuthenticate` / `afterAuthenticate` |
|
|
241
|
+
| `PluginImplemnetation` (sic) | type | `(Renderer?) => FC<AuthenticationRendererProps>` |
|
|
242
|
+
| `AuthMethodMeta` | type | `id`, `label`, `i18nKey`, `icon`, `order`, `emphasis`, `restricted`, `hidden`, `available(ctx)` |
|
|
243
|
+
| `plugins`, `registerAuthPlugin`, `getAuthPlugin`, `listAuthPlugins` | registry | Same registry as `./manager` |
|
|
244
|
+
| `pluginMethodSource` | const | The `LoginMethodSource` that offers registered plugins |
|
|
245
|
+
| `createWalletFacade`, `PinSchema`, `PinForm`, `TunnelAuthenticationRenderer`, `TunnelAuthenticationRendererProps` | function, const, type | Wallet-tunnel helpers |
|
|
246
|
+
| `ed25519BasicUIPlugin`, `reCaptchaPlugin`, `tunnelConsumerUIPlugin` | const | Shipped plugin objects |
|
|
51
247
|
|
|
52
|
-
|
|
248
|
+
### `@owlmeans/client-auth/login`
|
|
53
249
|
|
|
54
|
-
|
|
250
|
+
| Symbol | Kind | Purpose |
|
|
251
|
+
|---|---|---|
|
|
252
|
+
| `appendLogin(ctx)` | function | Register the host; expose `context.login()` |
|
|
253
|
+
| `makeLoginService(alias?)`, `ensureLoginService(ctx)` | function | The lazy login service, and the idempotent getter a plugin package calls first |
|
|
254
|
+
| `LoginService`, `LoginServiceAppend`, `LoginContext` | type | `registerPlugin`, `registerPrecondition`, `registerMethodSource`, `registerScreen`, `screen`, `env`, and the `enter` / `begin` / `authorize` / `complete` / `resume` / `logout` / `logoutComplete` / `adopt` / `revoke` facade |
|
|
255
|
+
| `LoginPlugin`, `LoginEnv`, `LoginRequest`, `LogoutRequest`, `LoginPrecondition` | type | Plugin contract |
|
|
256
|
+
| `LoginOutcome`, `LoginIntent` | enum | Stage results; login or logout surrogate |
|
|
257
|
+
| `useLogin(target?)` | hook | `[dispatcherPath, onLogIn]` |
|
|
258
|
+
| `useLogout(target?)` | hook | `onLogOut` |
|
|
259
|
+
| `registerMethodSource`, `listMethodSources`, `resolveLoginMethods(ctx, cfg?, extra?)`, `primaryLoginMethod(methods)` | function | Global method sources and resolution |
|
|
260
|
+
| `LoginMethod`, `LoginMethodSource`, `LoginMethodContext` | type | Method contract |
|
|
261
|
+
| `resolveTerms(cfg?)`, `termsAccepted(resolved)`, `acceptTerms(resolved, accepted)`, `ResolvedTerms` | function, type | Terms confirmation, stored in `localStorage` against a version derived from the URLs |
|
|
262
|
+
| `resolveCredit(cfg?, brand?, service?)`, `ResolvedCredit` | function, type | The credit and copyright line |
|
|
263
|
+
| `FallbackLoginScreen`, `LoginScreenProps`, `LoginScreenComponent` | component, type | The plain screen used when no UI family registered one |
|
|
264
|
+
| `surrogatePath(ctx, target)`, `SurrogateTarget` | function, type | Where a surrogate window opens; `null` on an entrypoint list without the surrogate route |
|
|
265
|
+
| `resumeAction(outcome)`, `ResumeAction`, `loginAttemptError(outcome)` | function, enum | The shared readings of a `resume` outcome and of a finished attempt |
|
|
266
|
+
| `enterOidcAuthorization(model)` | function | Move a flow to the step that can authorize; idempotent |
|
|
267
|
+
| `adoptToken(ctx, token)`, `revokeToken(ctx)` | function | The single adoption and de-adoption paths |
|
|
268
|
+
| `isEmbedded`, `isSurrogate`, `markSurrogate`, `clearSurrogate`, `defaultLoginEnv` | function | Environment probes behind `LoginEnv` |
|
|
269
|
+
| `LOGIN_SERVICE`, `DEFAULT_ALIAS`, `DEFAULT_LOGIN_PRIORITY`, `DEFAULT_METHOD_ORDER`, `LOGIN_SURROGATE_NAME`, `LOGIN_TOKEN_MESSAGE`, `LOGIN_LOGOUT_MESSAGE`, `LOGIN_SURROGATE_MARKER`, `LOGIN_SURROGATE_FEATURES`, `LOGIN_WATCH_INTERVAL`, `LOGIN_INTENT_QUERY`, `LOGIN_NEXT_QUERY`, `LOGIN_METHOD_QUERY`, `LOGIN_TERMS_STORAGE` | const | Service alias, ordering defaults and cross-document wire values |
|
|
55
270
|
|
|
56
|
-
|
|
271
|
+
Importing `./login` also registers the `login` strings of the `auth` library namespace in all
|
|
272
|
+
seven supported languages.
|
|
57
273
|
|
|
58
|
-
##
|
|
274
|
+
## Common pitfalls
|
|
59
275
|
|
|
60
|
-
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
276
|
+
- **Keep sign-in handlers synchronous.** `useLogin` and `useLogout` handlers await nothing before
|
|
277
|
+
delegating. Wrapping them in an `async` handler that awaits first makes the popup blocker eat the
|
|
278
|
+
surrogate window. The same rule binds a plugin's `begin` / `logout`, a method's `start` and every
|
|
279
|
+
precondition.
|
|
280
|
+
- **Never start login from an effect.** Without a user gesture the window is blocked. Render a
|
|
281
|
+
control and let the person click.
|
|
282
|
+
- **Adopt and drop tokens only through the auth service or `adoptToken` / `revokeToken`.**
|
|
283
|
+
Hand-written access to `AUTH_RESOURCE` drifts from the envelope decoding beside it.
|
|
284
|
+
- **Import names from the right subpath.** The plugin contract (`AuthenticationPlugin`,
|
|
285
|
+
`AuthMethodMeta`, `pluginMethodSource`, tunnel helpers) resolves only through
|
|
286
|
+
`./manager/plugins`. Rendering types and the registry resolve through `./manager`. Importing
|
|
287
|
+
`./login` registers no authentication plugin.
|
|
288
|
+
- **Gate operator methods with `method.restricted`,** so the configuration has to name them. Gate
|
|
289
|
+
dev-only methods on `cfg.debug.supervisor`, never `cfg.debug.all`.
|
|
290
|
+
- **Declare `requiresRenderer`** on a plugin whose `Implementation` cannot render without a UI
|
|
291
|
+
package's `Renderer`. Otherwise the screen offers a button that crashes the page it opens.
|
|
292
|
+
- **Call `enterOidcAuthorization(model)` before every `authenticate`** in an OIDC flow, rather than
|
|
293
|
+
re-deriving the transition.
|
|
294
|
+
- **Authorization stays server-side.** The browser ends up holding an ordinary OwlMeans bearer token
|
|
295
|
+
whichever provider issued it. Product checks live in entrypoint gates and handlers, never in
|
|
296
|
+
client-only state.
|
|
297
|
+
- **The organization on the wire is its `entitySlug`.** Read it with `entitySlugOf` from
|
|
298
|
+
`@owlmeans/auth`; the stable `entityId` never reaches the browser.
|
|
299
|
+
- **Overrides of the `login` strings cover all seven languages** (`en`, `pl`, `ru`, `be`, `uk`,
|
|
300
|
+
`es`, `de`).
|
|
64
301
|
|
|
65
|
-
## Related
|
|
302
|
+
## Related packages
|
|
66
303
|
|
|
67
|
-
- [`@owlmeans/auth`](../auth) — `Auth
|
|
68
|
-
- [`@owlmeans/auth-common`](../auth-common) —
|
|
69
|
-
- [`@owlmeans/
|
|
304
|
+
- [`@owlmeans/auth`](../auth) — `Auth`, aliases, `entitySlugOf`, auth errors
|
|
305
|
+
- [`@owlmeans/auth-common`](../auth-common) — `authProtocols`, `authMiddleware`, guard aliases
|
|
306
|
+
- [`@owlmeans/web-client`](../web-client) — appends the auth service and login host; ships the dispatcher and login plugins
|
|
307
|
+
- [`@owlmeans/web-panel`](../web-panel) — the shadcn sign-in screen (`appendLoginScreen`)
|
|
308
|
+
- [`@owlmeans/client-panel`](../client-panel) — headless login-method and terms models
|
|
309
|
+
- [`@owlmeans/web-oidc-rp`](../web-oidc-rp) — OIDC relying party and its method source
|
|
310
|
+
- [`@owlmeans/web-auth`](../web-auth) — supervisor (PK) login plugin
|
|
311
|
+
- [`@owlmeans/client-socket`](../client-socket) — the socket hook `useWs` wraps
|
|
312
|
+
- [`@owlmeans/server-auth`](../server-auth) — the server-side counterpart
|
|
70
313
|
|
|
71
314
|
<!-- owlmeans:agent-guidance:start -->
|
|
72
315
|
## Agent guidance
|
|
@@ -76,7 +319,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
76
319
|
your project's skill store (`.agents/skills/`):
|
|
77
320
|
|
|
78
321
|
```sh
|
|
79
|
-
npx @owlmeans/agent-skills
|
|
322
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.32
|
|
80
323
|
```
|
|
81
324
|
|
|
82
325
|
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/client-auth",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-
|
|
4
|
+
"version": "0.1.18-rc.41",
|
|
5
|
+
"generatedAt": "2026-09-22T12:36:21.781Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: client-auth
|
|
3
|
-
description: How to use @owlmeans/client-auth —
|
|
3
|
+
description: How to use @owlmeans/client-auth — the browser side of OwlMeans authentication. Root exports the auth service, the shared client entrypoints and the dispatcher HOC; ./manager holds the authentication screen, its control and the plugin registry; ./manager/plugins holds the authentication-plugin contract a plugin package writes against; ./login holds the login-plugin host, the sign-in method registry, the terms confirmation and the useLogin/useLogout hooks. Auto-invoked when importing client-auth helpers, registering an authentication plugin, or wiring a sign-in control.
|
|
4
4
|
user-invocable: false
|
|
5
5
|
---
|
|
6
6
|
<!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
|
|
@@ -8,41 +8,159 @@ user-invocable: false
|
|
|
8
8
|
# @owlmeans/client-auth
|
|
9
9
|
|
|
10
10
|
**Layer:** Client
|
|
11
|
-
**Install:** `"@owlmeans/client-auth": "^0.1.18-rc.
|
|
11
|
+
**Install:** `"@owlmeans/client-auth": "^0.1.18-rc.41"` in `dependencies`
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Five subpaths, five jobs:
|
|
14
|
+
|
|
15
|
+
| Subpath | Job |
|
|
16
|
+
|---------|-----|
|
|
17
|
+
| `.` | The client `AuthService`, the shared auth entrypoints, and the dispatcher HOC |
|
|
18
|
+
| `./manager` | The registry's four names plus the authentication screen, control and error — *how* a user proves identity. Importing it registers the three shipped browser plugins and `pluginMethodSource` by side effect |
|
|
19
|
+
| `./manager/entrypoints` | The manager application's own front-end entrypoint bindings |
|
|
20
|
+
| `./manager/plugins` | The plugin-authoring surface, and a superset of the registry: the `AuthenticationPlugin` contract, `pluginMethodSource`, the wallet-tunnel helpers and the shipped plugin objects — with no registration side effect |
|
|
21
|
+
| `./login` | The **login**-plugin host — *where* the authorization round trip runs, and which method a person picks |
|
|
22
|
+
|
|
23
|
+
A plugin package imports from both, and the split is fixed: `AuthenticationPlugin`,
|
|
24
|
+
`AuthMethodMeta`, `pluginMethodSource`, `PinSchema`, `createWalletFacade`,
|
|
25
|
+
`TunnelAuthenticationRenderer` and the shipped plugin objects resolve **only** through
|
|
26
|
+
`./manager/plugins`; the registry (`plugins`, `registerAuthPlugin`, …) and the rendering types
|
|
27
|
+
(`AuthenticationRenderer`, `AuthenticationControl`, `ClientAuthType`, `AuthenticationHOC`) resolve
|
|
28
|
+
through `./manager`. Importing the contract from `./manager` gets an unresolvable name.
|
|
29
|
+
|
|
30
|
+
`./manager/plugins` and `./login` answer different questions and are separate registries. Do not
|
|
31
|
+
conflate them.
|
|
32
|
+
|
|
33
|
+
## Key Exports — root
|
|
34
|
+
|
|
35
|
+
| Export | Description |
|
|
36
|
+
|--------|-------------|
|
|
37
|
+
| `makeAuthService(alias?)` | The client `AuthService` — `authenticate(token)`, `update(token)`, `authenticated()`, `user()`, `store()`. Decodes the bearer envelope and persists the record |
|
|
38
|
+
| `appendAuthService(ctx, alias?)` | Register it, register `authMiddleware`, and expose `context.auth()` |
|
|
39
|
+
| `setupExternalAuthentication(service)` | Point the `CAUTHEN_FLOW_ENTER` entrypoint at a service, so an external provider can redirect into this app |
|
|
40
|
+
| `entrypoints` | The shared auth entrypoint list with `DISPATCHER_AUTHEN` bound |
|
|
41
|
+
| `DEFAULT_ALIAS` | `'auth'` — the client-side counterpart of `DEFAULT_GUARD` |
|
|
42
|
+
| `AUTH_RESOURCE` | `'auth'` — the resource the token record is stored in |
|
|
43
|
+
| `USER_ID` | `'user'` — the id of that single record |
|
|
44
|
+
| `DEFAULT_ENTITY` | `'owlmeans'` |
|
|
45
|
+
| `DispatcherHOC` | The return-leg HOC. It wraps a `DispatcherRenderer`, hands it `provideToken(token, query)` and `navigate()`, adopts the supplied token through the auth service, and strips `AUTH_QUERY` before navigating on. Reading the return-leg query is the renderer's job — `@owlmeans/web-client` reads `AUTH_QUERY`, `@owlmeans/web-oidc-rp` also reads `OIDC_ERROR_QUERY` and forwards the remaining params |
|
|
46
|
+
| `DispatcherProps`, `TDispatcherHOC`, `DispatcherRenderer`, `DispatcherRendererProps` | Dispatcher types |
|
|
47
|
+
| `useWs(entrypoint, request?, options?)` | A socket hook that attaches the current token as the `AUTH_QUERY` param and refreshes it on every reconnect attempt (via `WsOptions.beforeConnect`), unless the request already carried its own token — in which case that stays untouched across reconnects too. `options` passes straight through to `@owlmeans/client-socket`'s `useWs`, so `{ reconnect: false }` still opts a stateful one-shot handshake out of retries (see `manager/plugins/tunnel-consumer.tsx`) |
|
|
48
|
+
| `useSelfAuth(force?)` | Whether this context is authenticated; navigates to `DISPATCHER` when it is not and `force` |
|
|
49
|
+
| `AuthServiceAppend`, `ClientAuthRecord`, `ClientAuthResource` | Types |
|
|
50
|
+
|
|
51
|
+
## `./manager` — the authentication screen and the registry
|
|
52
|
+
|
|
53
|
+
| Export | Description |
|
|
54
|
+
|--------|-------------|
|
|
55
|
+
| `plugins`, `registerAuthPlugin`, `getAuthPlugin`, `listAuthPlugins` | The module-global registry |
|
|
56
|
+
| `AuthenticationHOC`, `AuthenticationProps`, `TAuthenticationHOC` | The screen that hosts a plugin's implementation |
|
|
57
|
+
| `makeControl`, `AuthenticationControl`, `AuthenticationControlState` | The control a plugin drives: `requestAllowence`, `authenticate`, and the state it persists across a provider redirect |
|
|
58
|
+
| `AuthenticationRenderer`, `AuthenticationRendererProps`, `ClientAuthType`, `ClientAuthenticationMethod`, `AuthenticationCallback` | Rendering contract |
|
|
59
|
+
| `TunnelConsumer`, `TunnelAuthenticationProps`, `TunnelAuthCallback` | The wallet-tunnel consumer screen |
|
|
60
|
+
| `AuthenCredError` | Thrown when the entered credential cannot be used |
|
|
61
|
+
|
|
62
|
+
## `./manager/plugins` — the authentication-plugin contract
|
|
14
63
|
|
|
15
64
|
| Export | Description |
|
|
16
65
|
|--------|-------------|
|
|
17
|
-
| `
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
66
|
+
| `AuthenticationPlugin` | `type`, `Implementation`, optional `Renderer`, `requiresRenderer?`, `method?` and the `authenticate` / `beforeAuthenticate` / `afterAuthenticate` hooks |
|
|
67
|
+
| `PluginImplemnetation` (sic) | `(Renderer?) => FC<AuthenticationRendererProps>` — the shape of `Implementation` |
|
|
68
|
+
| `AuthMethodMeta` | How the plugin presents itself as a sign-in method — see `login-methods` |
|
|
69
|
+
| `plugins`, `registerAuthPlugin`, `getAuthPlugin`, `listAuthPlugins` | The same registry as `./manager` |
|
|
70
|
+
| `pluginMethodSource` | The `LoginMethodSource` that turns registered plugins into offerable methods |
|
|
71
|
+
| `createWalletFacade`, `PinSchema`, `PinForm`, `TunnelAuthenticationRenderer`, `TunnelAuthenticationRendererProps` | Wallet-tunnel helpers a consumer plugin builds on |
|
|
72
|
+
| `ed25519BasicUIPlugin`, `reCaptchaPlugin`, `tunnelConsumerUIPlugin` | The shipped plugin objects |
|
|
22
73
|
|
|
23
|
-
|
|
74
|
+
Shipped plugins register themselves by side effect: `basic-ed25519`, `re-captcha` and
|
|
75
|
+
`wallet-consumer` when `@owlmeans/client-auth/manager` is imported, OIDC and Google from
|
|
76
|
+
`@owlmeans/web-oidc-rp/auth/plugins`, the PK supervisor from `@owlmeans/web-auth`.
|
|
24
77
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
78
|
+
## `./login` — the login-plugin host
|
|
79
|
+
|
|
80
|
+
An `AuthenticationPlugin` answers how a credential is proven; a `LoginPlugin` answers in which
|
|
81
|
+
browsing context the round trip can complete at all.
|
|
82
|
+
|
|
83
|
+
| Export | Description |
|
|
84
|
+
|--------|-------------|
|
|
85
|
+
| `appendLogin(ctx)` | Register the host and expose it as `context.login()` |
|
|
86
|
+
| `makeLoginService(alias?)`, `ensureLoginService(ctx)` | The host itself — a **lazy** service, so `makeContext` can reach it at the Loading stage — and the idempotent getter a plugin package calls before `registerPlugin` |
|
|
87
|
+
| `LoginPlugin`, `LoginEnv`, `LoginRequest`, `LogoutRequest`, `LoginOutcome`, `LoginIntent`, `LoginService`, `LoginContext`, `LoginPrecondition` | The contract |
|
|
88
|
+
| `registerMethodSource`, `listMethodSources`, `resolveLoginMethods`, `primaryLoginMethod` | Which sign-in methods are offered — see `login-methods` |
|
|
89
|
+
| `LoginMethod`, `LoginMethodSource`, `LoginMethodContext` | Method types |
|
|
90
|
+
| `resolveTerms`, `termsAccepted`, `acceptTerms`, `ResolvedTerms` | The confirmation, recorded in `localStorage` against a version derived from the resolved URLs |
|
|
91
|
+
| `resolveCredit`, `ResolvedCredit` | The credit and copyright line |
|
|
92
|
+
| `FallbackLoginScreen`, `LoginScreenProps`, `LoginScreenComponent` | The plain sign-in screen a relying party renders when no UI family registered one |
|
|
93
|
+
| `surrogatePath(ctx, target)`, `SurrogateTarget` | Where a surrogate login window opens; `null` on an older entrypoint list |
|
|
94
|
+
| `resumeAction(outcome)`, `ResumeAction`, `loginAttemptError(outcome)` | The one reading of a `resume` outcome, and the one reading of a finished attempt |
|
|
95
|
+
| `registerNotifier(notifier)` (on `LoginService`), `LoginNotifier` | Surfaces a `begin`/`logout` outcome that has no inline screen to render it on — e.g. a toast on `LoginOutcome.Blocked` for a header "Log in"/"Log out" control. `web-panel`'s `appendLoginScreen` registers a default; unregistered, it is silence |
|
|
96
|
+
| `enterOidcAuthorization(model)` | Move a flow to the step that can authorize — idempotent, call it before every `authenticate` |
|
|
97
|
+
| `adoptToken(ctx, token)`, `revokeToken(ctx)` | The single adoption and de-adoption paths |
|
|
98
|
+
| `useLogin(target?)`, `useLogout(target?)` | Wiring for a sign-in / sign-out control; `target` is the entrypoint alias the flow lands on when it is over |
|
|
99
|
+
| `isEmbedded`, `isSurrogate`, `markSurrogate`, `clearSurrogate`, `defaultLoginEnv` | Environment probes the host builds `LoginEnv` from |
|
|
100
|
+
| `LOGIN_SERVICE`, `LOGIN_SURROGATE_NAME`, `LOGIN_TOKEN_MESSAGE`, `LOGIN_LOGOUT_MESSAGE`, `LOGIN_SURROGATE_MARKER`, `LOGIN_SURROGATE_WIDTH`, `LOGIN_SURROGATE_HEIGHT`, `LOGIN_WATCH_INTERVAL`, `LOGIN_INTENT_QUERY`, `LOGIN_NEXT_QUERY`, `LOGIN_METHOD_QUERY`, `LOGIN_TERMS_STORAGE`, `DEFAULT_LOGIN_PRIORITY`, `DEFAULT_METHOD_ORDER` | Aliases and the fixed cross-document wire values. `LOGIN_SURROGATE_FEATURES` also still exports (deprecated, never centered) — `@owlmeans/web-client`'s `centeredPopupFeatures(LOGIN_SURROGATE_WIDTH, LOGIN_SURROGATE_HEIGHT)` is what the surrogate plugin actually opens the window with |
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { useLogin, useLogout } from '@owlmeans/client-auth/login'
|
|
104
|
+
|
|
105
|
+
const [loginPath, onLogIn] = useLogin()
|
|
106
|
+
const onLogOut = useLogout()
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Both handlers are deliberately **not** async and await nothing before delegating — a window opened
|
|
110
|
+
after the user gesture has finished being handled is eaten by the popup blocker. Each resolves its
|
|
111
|
+
entrypoint **inside** the hook, never at module scope — `DISPATCHER` for `useLogin`, the surrogate
|
|
112
|
+
path for `useLogout`. A module body runs before `registerEntrypoints`, so `useLogin`'s lookup at
|
|
113
|
+
top level throws `Entrypoint dispatcher not found` during import, taking the whole render down.
|
|
114
|
+
|
|
115
|
+
`target` is an entrypoint alias, and it is what supplies the request's `navigate` continuation —
|
|
116
|
+
the in-app step the plugin runs once the round trip is done. `useLogin` always sends one, falling
|
|
117
|
+
back to `DISPATCHER`; `useLogout` sends one only when a target is given. Omitting it does not leave
|
|
118
|
+
the session behind: the token is revoked either way, and the plugin decides what the document does
|
|
119
|
+
with no continuation to run — `@owlmeans/web-client`'s redirect plugin reloads the page, because
|
|
120
|
+
cached auth reads only forget a session when the application is rebuilt. `useLogin` returns
|
|
121
|
+
`[path, handler]` and `useLogout` a bare handler — a logout control has no address to point at.
|
|
122
|
+
|
|
123
|
+
Plugin selection, the shipped browser flows and the full invariant list: the `login-plugins` skill.
|
|
28
124
|
|
|
29
125
|
## Usage
|
|
30
126
|
|
|
31
127
|
```typescript
|
|
32
|
-
import { setupExternalAuthentication } from '@owlmeans/client-auth'
|
|
128
|
+
import { appendAuthService, setupExternalAuthentication, DEFAULT_ALIAS } from '@owlmeans/client-auth'
|
|
129
|
+
import { appendLogin } from '@owlmeans/client-auth/login'
|
|
33
130
|
|
|
34
|
-
|
|
131
|
+
// in makeContext:
|
|
132
|
+
appendAuthService(context) // registers under DEFAULT_ALIAS ('auth')
|
|
133
|
+
appendLogin(context)
|
|
134
|
+
setupExternalAuthentication(MY_WEB_SERVICE) // the service alias the provider redirects into
|
|
35
135
|
```
|
|
36
136
|
|
|
37
|
-
|
|
137
|
+
`@owlmeans/web-client`'s own `makeContext` already appends the auth service and the login host, so
|
|
138
|
+
a web application only calls these when it builds its context by hand.
|
|
139
|
+
|
|
140
|
+
## Rules
|
|
38
141
|
|
|
39
|
-
- The
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
-
|
|
142
|
+
- The bearer token lives in the `AUTH_RESOURCE` resource under the single id `USER_ID`. Read and
|
|
143
|
+
write it through the auth service or through `adoptToken` / `revokeToken`; hand-written storage
|
|
144
|
+
access drifts from the envelope decoding that happens beside it.
|
|
145
|
+
- A plugin that persists state across a provider redirect drives the control's own
|
|
146
|
+
`persist()` / `restore()` / `hasPersistentState()` / `cleanUpState()`. They keep the type, stage
|
|
147
|
+
and allowance in the `FLOW_STATE` resource under an id this package owns and does not export —
|
|
148
|
+
restore before submitting the credential, and clean up after.
|
|
149
|
+
- After a sign-in, `DispatcherHOC.navigate` resumes a landing suspended in `@owlmeans/client-flow`
|
|
150
|
+
(`resumeSuspendedFlow`, one-shot, an entrypoint alias plus its query) before it falls back to `HOME`;
|
|
151
|
+
it is read BEFORE `alias` is defaulted to `HOME`. Login plugins that navigate on their own do the same
|
|
152
|
+
— see `login-plugins`.
|
|
153
|
+
- The browser ends up holding an ordinary OwlMeans bearer token whichever provider issued the
|
|
154
|
+
login. Product authorization stays server-side, in entrypoint gates and handler checks — never in
|
|
155
|
+
client-only state.
|
|
156
|
+
- The organization entity a token names is its `entitySlug`, the renameable public name. Read it
|
|
157
|
+
with `entitySlugOf` from `@owlmeans/auth`; the stable `entityId` never reaches the browser.
|
|
43
158
|
|
|
44
159
|
## Depends On
|
|
45
160
|
|
|
46
|
-
- `@owlmeans/auth`, `@owlmeans/auth-common`
|
|
47
|
-
- `@owlmeans/client-context`, `@owlmeans/client-entrypoint`
|
|
161
|
+
- `@owlmeans/auth`, `@owlmeans/auth-common` — types, aliases, `authMiddleware`
|
|
162
|
+
- `@owlmeans/client`, `@owlmeans/client-context`, `@owlmeans/client-entrypoint`, `@owlmeans/client-socket`
|
|
163
|
+
- `@owlmeans/config` — the login screen, terms and credit configuration
|
|
164
|
+
- `@owlmeans/basic-envelope` — decoding the bearer envelope
|
|
48
165
|
- `react` (peer)
|
|
166
|
+
- `ajv` (peer) — `PinSchema` is typed as `JSONSchemaType<PinForm>`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../../src/components/dispatcher/component.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA2B,cAAc,EAAE,MAAM,YAAY,CAAA;AAczE,eAAO,MAAM,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../../src/components/dispatcher/component.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA2B,cAAc,EAAE,MAAM,YAAY,CAAA;AAczE,eAAO,MAAM,aAAa,EAAE,cAyF3B,CAAA"}
|
|
@@ -4,15 +4,26 @@ import { AUTH_QUERY, DISPATCHER } from '@owlmeans/auth';
|
|
|
4
4
|
import { HOME } from '@owlmeans/context';
|
|
5
5
|
import { DEFAULT_ALIAS, DEFAULT_ENTITY } from '../../consts.js';
|
|
6
6
|
import { useNavigate } from '@owlmeans/client';
|
|
7
|
-
import { DEFAULT_ALIAS as FLOW_SERVICE } from '@owlmeans/client-flow';
|
|
7
|
+
import { DEFAULT_ALIAS as FLOW_SERVICE, resumeSuspendedFlow } from '@owlmeans/client-flow';
|
|
8
8
|
import { FLOW_PLACEHOLDER, OidcAuthStep, STD_OIDC_FLOW } from '@owlmeans/flow';
|
|
9
9
|
import { SERVICE_PARAM } from '@owlmeans/web-flow';
|
|
10
10
|
export const DispatcherHOC = Renderer => ({ context, params, alias, query, payload }) => {
|
|
11
11
|
const [forwarding, setForwarding] = useState();
|
|
12
12
|
const navigator = useNavigate();
|
|
13
13
|
const navigate = useCallback(async () => {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
if (alias == null || alias === DISPATCHER) {
|
|
15
|
+
// A sign-in that started elsewhere — a device or authorization-code consent screen, most
|
|
16
|
+
// commonly — suspended itself here before leaving. Resuming it takes priority over the
|
|
17
|
+
// ordinary HOME landing, and does so BEFORE `alias` is overwritten, because once it is
|
|
18
|
+
// `HOME` there is no way back to tell the two cases apart.
|
|
19
|
+
const landing = await resumeSuspendedFlow(context);
|
|
20
|
+
if (landing != null) {
|
|
21
|
+
await navigator.navigate(context.entrypoint(landing.entrypoint), { query: landing.query });
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
alias = HOME;
|
|
25
|
+
}
|
|
26
|
+
const module = context.entrypoint(alias);
|
|
16
27
|
if (alias === HOME) {
|
|
17
28
|
params = {};
|
|
18
29
|
query = {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component.js","sourceRoot":"","sources":["../../../src/components/dispatcher/component.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAGxD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAEvD,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAE/D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAG9C,OAAO,EAAE,aAAa,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"component.js","sourceRoot":"","sources":["../../../src/components/dispatcher/component.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Browser entrypoints for the authentication protocols every client may call. */
|
|
2
|
+
export declare const entrypoints: import("@owlmeans/client-entrypoint").ClientProtocolEntrypoint<import("@owlmeans/entrypoint").EntrypointProtocol<{
|
|
3
|
+
body: import("@owlmeans/auth").AuthToken;
|
|
4
|
+
}, import("@owlmeans/auth").AuthToken>>[];
|
|
5
|
+
/** Bind the external-authentication screen to one explicitly selected service. */
|
|
6
|
+
export declare const bindExternalAuthentication: (service: string) => import("@owlmeans/client-entrypoint").ClientProtocolEntrypoint<import("@owlmeans/entrypoint").EntrypointProtocol<import("@owlmeans/entrypoint").OpenRequest, import("@owlmeans/entrypoint").OpenValue>>;
|
|
7
|
+
//# sourceMappingURL=entrypoints.d.ts.map
|