@owlmeans/client-auth 0.1.18-rc.6 → 0.1.18-rc.61

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.
Files changed (260) hide show
  1. package/README.md +283 -37
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/client-auth/SKILL.md +170 -21
  4. package/build/components/dispatcher/component.d.ts.map +1 -1
  5. package/build/components/dispatcher/component.js +26 -6
  6. package/build/components/dispatcher/component.js.map +1 -1
  7. package/build/components/dispatcher/types.local.d.ts +7 -0
  8. package/build/components/dispatcher/types.local.d.ts.map +1 -0
  9. package/build/components/dispatcher/types.local.js +2 -0
  10. package/build/components/dispatcher/types.local.js.map +1 -0
  11. package/build/entrypoints.d.ts +7 -0
  12. package/build/entrypoints.d.ts.map +1 -0
  13. package/build/entrypoints.js +7 -0
  14. package/build/entrypoints.js.map +1 -0
  15. package/build/helper.d.ts +2 -1
  16. package/build/helper.d.ts.map +1 -1
  17. package/build/helper.js +28 -5
  18. package/build/helper.js.map +1 -1
  19. package/build/index.d.ts +1 -1
  20. package/build/index.d.ts.map +1 -1
  21. package/build/index.js +1 -1
  22. package/build/index.js.map +1 -1
  23. package/build/login/adopt/types.d.ts +26 -0
  24. package/build/login/adopt/types.d.ts.map +1 -0
  25. package/build/login/adopt/types.js +2 -0
  26. package/build/login/adopt/types.js.map +1 -0
  27. package/build/login/adopt.d.ts +10 -0
  28. package/build/login/adopt.d.ts.map +1 -0
  29. package/build/login/adopt.js +18 -0
  30. package/build/login/adopt.js.map +1 -0
  31. package/build/login/consts.d.ts +103 -0
  32. package/build/login/consts.d.ts.map +1 -0
  33. package/build/login/consts.js +106 -0
  34. package/build/login/consts.js.map +1 -0
  35. package/build/login/consts.local.d.ts +16 -0
  36. package/build/login/consts.local.d.ts.map +1 -0
  37. package/build/login/consts.local.js +30 -0
  38. package/build/login/consts.local.js.map +1 -0
  39. package/build/login/credit/types.d.ts +7 -0
  40. package/build/login/credit/types.d.ts.map +1 -0
  41. package/build/login/credit/types.js +2 -0
  42. package/build/login/credit/types.js.map +1 -0
  43. package/build/login/credit.d.ts +19 -0
  44. package/build/login/credit.d.ts.map +1 -0
  45. package/build/login/credit.js +58 -0
  46. package/build/login/credit.js.map +1 -0
  47. package/build/login/env/types.d.ts +35 -0
  48. package/build/login/env/types.d.ts.map +1 -0
  49. package/build/login/env/types.js +2 -0
  50. package/build/login/env/types.js.map +1 -0
  51. package/build/login/env.d.ts +8 -0
  52. package/build/login/env.d.ts.map +1 -0
  53. package/build/login/env.js +67 -0
  54. package/build/login/env.js.map +1 -0
  55. package/build/login/flow.d.ts +20 -0
  56. package/build/login/flow.d.ts.map +1 -0
  57. package/build/login/flow.js +39 -0
  58. package/build/login/flow.js.map +1 -0
  59. package/build/login/hook.d.ts +52 -0
  60. package/build/login/hook.d.ts.map +1 -0
  61. package/build/login/hook.js +89 -0
  62. package/build/login/hook.js.map +1 -0
  63. package/build/login/i18n/be.json +41 -0
  64. package/build/login/i18n/de.json +41 -0
  65. package/build/login/i18n/en.json +41 -0
  66. package/build/login/i18n/es.json +41 -0
  67. package/build/login/i18n/fr.json +41 -0
  68. package/build/login/i18n/pl.json +41 -0
  69. package/build/login/i18n/ru.json +41 -0
  70. package/build/login/i18n/uk.json +41 -0
  71. package/build/login/i18n.d.ts +2 -0
  72. package/build/login/i18n.d.ts.map +1 -0
  73. package/build/login/i18n.js +27 -0
  74. package/build/login/i18n.js.map +1 -0
  75. package/build/login/index.d.ts +27 -0
  76. package/build/login/index.d.ts.map +1 -0
  77. package/build/login/index.js +18 -0
  78. package/build/login/index.js.map +1 -0
  79. package/build/login/land/types.d.ts +52 -0
  80. package/build/login/land/types.d.ts.map +1 -0
  81. package/build/login/land/types.js +2 -0
  82. package/build/login/land/types.js.map +1 -0
  83. package/build/login/land.d.ts +6 -0
  84. package/build/login/land.d.ts.map +1 -0
  85. package/build/login/land.js +122 -0
  86. package/build/login/land.js.map +1 -0
  87. package/build/login/methods/types.d.ts +30 -0
  88. package/build/login/methods/types.d.ts.map +1 -0
  89. package/build/login/methods/types.js +2 -0
  90. package/build/login/methods/types.js.map +1 -0
  91. package/build/login/methods.d.ts +7 -0
  92. package/build/login/methods.d.ts.map +1 -0
  93. package/build/login/methods.js +87 -0
  94. package/build/login/methods.js.map +1 -0
  95. package/build/login/resume/types.d.ts +24 -0
  96. package/build/login/resume/types.d.ts.map +1 -0
  97. package/build/login/resume/types.js +2 -0
  98. package/build/login/resume/types.js.map +1 -0
  99. package/build/login/resume.d.ts +9 -0
  100. package/build/login/resume.d.ts.map +1 -0
  101. package/build/login/resume.js +38 -0
  102. package/build/login/resume.js.map +1 -0
  103. package/build/login/screen.d.ts +17 -0
  104. package/build/login/screen.d.ts.map +1 -0
  105. package/build/login/screen.js +66 -0
  106. package/build/login/screen.js.map +1 -0
  107. package/build/login/service.d.ts +31 -0
  108. package/build/login/service.d.ts.map +1 -0
  109. package/build/login/service.js +210 -0
  110. package/build/login/service.js.map +1 -0
  111. package/build/login/start/types.d.ts +29 -0
  112. package/build/login/start/types.d.ts.map +1 -0
  113. package/build/login/start/types.js +2 -0
  114. package/build/login/start/types.js.map +1 -0
  115. package/build/login/start.d.ts +5 -0
  116. package/build/login/start.d.ts.map +1 -0
  117. package/build/login/start.js +36 -0
  118. package/build/login/start.js.map +1 -0
  119. package/build/login/surrogate/types.d.ts +9 -0
  120. package/build/login/surrogate/types.d.ts.map +1 -0
  121. package/build/login/surrogate/types.js +2 -0
  122. package/build/login/surrogate/types.js.map +1 -0
  123. package/build/login/surrogate.d.ts +13 -0
  124. package/build/login/surrogate.d.ts.map +1 -0
  125. package/build/login/surrogate.js +33 -0
  126. package/build/login/surrogate.js.map +1 -0
  127. package/build/login/terms/types.d.ts +112 -0
  128. package/build/login/terms/types.d.ts.map +1 -0
  129. package/build/login/terms/types.js +2 -0
  130. package/build/login/terms/types.js.map +1 -0
  131. package/build/login/terms-config.d.ts +65 -0
  132. package/build/login/terms-config.d.ts.map +1 -0
  133. package/build/login/terms-config.js +2 -0
  134. package/build/login/terms-config.js.map +1 -0
  135. package/build/login/terms.d.ts +21 -0
  136. package/build/login/terms.d.ts.map +1 -0
  137. package/build/login/terms.js +235 -0
  138. package/build/login/terms.js.map +1 -0
  139. package/build/login/types.d.ts +313 -0
  140. package/build/login/types.d.ts.map +1 -0
  141. package/build/login/types.js +2 -0
  142. package/build/login/types.js.map +1 -0
  143. package/build/manager/components/authentication/control.js +3 -3
  144. package/build/manager/components/authentication/control.js.map +1 -1
  145. package/build/manager/components/authentication/types.d.ts +2 -2
  146. package/build/manager/components/authentication/types.d.ts.map +1 -1
  147. package/build/manager/components/tunnel-consumer.js +2 -2
  148. package/build/manager/components/tunnel-consumer.js.map +1 -1
  149. package/build/manager/entrypoints.d.ts +11 -0
  150. package/build/manager/entrypoints.d.ts.map +1 -0
  151. package/build/manager/entrypoints.js +17 -0
  152. package/build/manager/entrypoints.js.map +1 -0
  153. package/build/manager/plugins/basic-ed25519.d.ts.map +1 -1
  154. package/build/manager/plugins/basic-ed25519.js +5 -0
  155. package/build/manager/plugins/basic-ed25519.js.map +1 -1
  156. package/build/manager/plugins/exports.d.ts +3 -0
  157. package/build/manager/plugins/exports.d.ts.map +1 -1
  158. package/build/manager/plugins/exports.js +2 -0
  159. package/build/manager/plugins/exports.js.map +1 -1
  160. package/build/manager/plugins/index.d.ts +2 -5
  161. package/build/manager/plugins/index.d.ts.map +1 -1
  162. package/build/manager/plugins/index.js +9 -1
  163. package/build/manager/plugins/index.js.map +1 -1
  164. package/build/manager/plugins/methods.d.ts +6 -0
  165. package/build/manager/plugins/methods.d.ts.map +1 -0
  166. package/build/manager/plugins/methods.js +65 -0
  167. package/build/manager/plugins/methods.js.map +1 -0
  168. package/build/manager/plugins/re-captcha.d.ts.map +1 -1
  169. package/build/manager/plugins/re-captcha.js +5 -0
  170. package/build/manager/plugins/re-captcha.js.map +1 -1
  171. package/build/manager/plugins/registry/types.d.ts +10 -0
  172. package/build/manager/plugins/registry/types.d.ts.map +1 -0
  173. package/build/manager/plugins/registry/types.js +2 -0
  174. package/build/manager/plugins/registry/types.js.map +1 -0
  175. package/build/manager/plugins/registry.d.ts +20 -0
  176. package/build/manager/plugins/registry.d.ts.map +1 -0
  177. package/build/manager/plugins/registry.js +23 -0
  178. package/build/manager/plugins/registry.js.map +1 -0
  179. package/build/manager/plugins/tunnel-consumer.d.ts.map +1 -1
  180. package/build/manager/plugins/tunnel-consumer.js +10 -3
  181. package/build/manager/plugins/tunnel-consumer.js.map +1 -1
  182. package/build/manager/plugins/types.d.ts +53 -0
  183. package/build/manager/plugins/types.d.ts.map +1 -1
  184. package/build/service.js +1 -1
  185. package/build/service.js.map +1 -1
  186. package/package.json +39 -29
  187. package/src/components/dispatcher/component.tsx +37 -14
  188. package/src/components/dispatcher/types.local.ts +7 -0
  189. package/src/entrypoints.ts +10 -0
  190. package/src/helper.ts +31 -5
  191. package/src/index.ts +1 -1
  192. package/src/login/adopt/types.ts +25 -0
  193. package/src/login/adopt.ts +26 -0
  194. package/src/login/consts.local.ts +34 -0
  195. package/src/login/consts.ts +124 -0
  196. package/src/login/credit/types.ts +6 -0
  197. package/src/login/credit.ts +69 -0
  198. package/src/login/env/types.ts +35 -0
  199. package/src/login/env.ts +74 -0
  200. package/src/login/flow.ts +44 -0
  201. package/src/login/hook.ts +99 -0
  202. package/src/login/i18n/be.json +41 -0
  203. package/src/login/i18n/de.json +41 -0
  204. package/src/login/i18n/en.json +41 -0
  205. package/src/login/i18n/es.json +41 -0
  206. package/src/login/i18n/fr.json +41 -0
  207. package/src/login/i18n/pl.json +41 -0
  208. package/src/login/i18n/ru.json +41 -0
  209. package/src/login/i18n/uk.json +41 -0
  210. package/src/login/i18n.ts +29 -0
  211. package/src/login/index.ts +27 -0
  212. package/src/login/land/types.ts +54 -0
  213. package/src/login/land.ts +149 -0
  214. package/src/login/methods/types.ts +32 -0
  215. package/src/login/methods.ts +109 -0
  216. package/src/login/resume/types.ts +24 -0
  217. package/src/login/resume.ts +45 -0
  218. package/src/login/screen.tsx +140 -0
  219. package/src/login/service.ts +271 -0
  220. package/src/login/start/types.ts +30 -0
  221. package/src/login/start.ts +44 -0
  222. package/src/login/surrogate/types.ts +9 -0
  223. package/src/login/surrogate.ts +37 -0
  224. package/src/login/terms/types.ts +126 -0
  225. package/src/login/terms-config.ts +55 -0
  226. package/src/login/terms.ts +315 -0
  227. package/src/login/types.ts +335 -0
  228. package/src/manager/README.md +37 -80
  229. package/src/manager/components/authentication/control.ts +3 -3
  230. package/src/manager/components/authentication/types.ts +2 -2
  231. package/src/manager/components/tunnel-consumer.tsx +2 -2
  232. package/src/manager/entrypoints.ts +18 -0
  233. package/src/manager/plugins/basic-ed25519.tsx +7 -0
  234. package/src/manager/plugins/exports.ts +3 -0
  235. package/src/manager/plugins/index.ts +11 -4
  236. package/src/manager/plugins/methods.ts +74 -0
  237. package/src/manager/plugins/re-captcha.tsx +7 -0
  238. package/src/manager/plugins/registry/types.ts +10 -0
  239. package/src/manager/plugins/registry.ts +33 -0
  240. package/src/manager/plugins/tunnel-consumer.tsx +12 -3
  241. package/src/manager/plugins/types.ts +54 -0
  242. package/src/service.ts +1 -1
  243. package/tests/context.ts +1 -2
  244. package/tests/i18n.spec.ts +68 -0
  245. package/tests/land.spec.ts +292 -0
  246. package/tests/login.spec.ts +332 -0
  247. package/tests/target.spec.ts +210 -0
  248. package/tests/terms-deferred.spec.ts +65 -0
  249. package/tests/terms.spec.ts +314 -0
  250. package/build/.gitkeep +0 -0
  251. package/build/manager/modules.d.ts +0 -2
  252. package/build/manager/modules.d.ts.map +0 -1
  253. package/build/manager/modules.js +0 -16
  254. package/build/manager/modules.js.map +0 -1
  255. package/build/modules.d.ts +0 -4
  256. package/build/modules.d.ts.map +0 -1
  257. package/build/modules.js +0 -9
  258. package/build/modules.js.map +0 -1
  259. package/src/manager/modules.ts +0 -18
  260. package/src/modules.ts +0 -13
package/README.md CHANGED
@@ -1,72 +1,318 @@
1
1
  # @owlmeans/client-auth
2
2
 
3
- Client-side authentication service providing user auth state and external auth flow setup.
4
-
5
- ## Overview
6
-
7
- - `useSelfAuth()` — React hook that returns the current authenticated user's `Auth` object
8
- - `setupExternalAuthentication()` — configure the client to authenticate against an external identity provider
9
- - `modules` — pre-built client modules for the auth flow (login, dispatcher)
10
- - `DEFAULT_ALIAS` — the auth service alias (`'auth'`)
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.61
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
+ `loginMethodsHelper.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. `loginResumeHelper.resumeAction` and `.loginAttemptError` are the only
38
+ readings of it.
39
+
18
40
  ## Usage
19
41
 
20
- Access current auth state in a component:
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
- ```typescript
71
+ ```tsx
72
+ import { authHelper } 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
- function UserMenu() {
26
- const auth = useSelfAuth()
27
- return <span>{auth?.userId}</span>
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 = authHelper.entitySlugOf(auth)
89
+
90
+ return <main data-organization={organization}>{children}</main>
28
91
  }
29
92
  ```
30
93
 
31
- Set up external authentication (e.g. OIDC redirect):
94
+ ### 3. Sign-in and sign-out controls
32
95
 
33
- ```typescript
34
- import { setupExternalAuthentication } from '@owlmeans/client-auth'
96
+ ```tsx
97
+ import type { FC } from 'react'
98
+ import { useLogin, useLogout } from '@owlmeans/client-auth/login'
35
99
 
36
- // In context setup
37
- setupExternalAuthentication(context, { serviceUrl: process.env.AUTH_URL })
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 { authPluginHelper } 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
+ authPluginHelper.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
- ### `useSelfAuth(): Auth | null`
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`, `authPluginHelper` (`registerAuthPlugin`, `getAuthPlugin`, `listAuthPlugins`), `AuthPluginHelper` | 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
- Returns the current `Auth` object from the context, or `null` if not authenticated.
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
- ### `setupExternalAuthentication(ctx, opts?)`
234
+ ### `@owlmeans/client-auth/manager/plugins`
47
235
 
48
- Configures the context to redirect to an external identity provider for authentication.
236
+ The plugin-authoring surface, with no registration side effect.
49
237
 
50
- ### `modules`
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`, `authPluginHelper`, `AuthPluginHelper` | 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
- Pre-built `ClientModule[]` for the auth flow (mirrors `@owlmeans/auth-common`'s modules with client route models).
248
+ ### `@owlmeans/client-auth/login`
53
249
 
54
- ### `DEFAULT_ALIAS`
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
+ | `loginMethodsHelper` — `.registerMethodSource`, `.listMethodSources`, `.resolveLoginMethods(ctx, cfg?, extra?)`, `.primaryLoginMethod(methods)`; `LoginMethodsHelper` | helper, type | Global method sources and resolution |
260
+ | `LoginMethod`, `LoginMethodSource`, `LoginMethodContext` | type | Method contract |
261
+ | `loginTermsHelper` — `.resolveTerms(cfg?)`, `.termsAccepted(resolved)`, `.acceptTerms(resolved, accepted)`; `LoginTermsHelper`, `ResolvedTerms` | helper, type | Terms confirmation, stored in `localStorage` against a version derived from the URLs |
262
+ | `loginTermsHelper` — `.termsLabelResolver(translate, locale)`, `.termsAcceptanceOf(resolved, locale?)`; `LoginTermsAcceptance`, `TermsAcceptanceRef` | helper, type | The one document-label resolver every terms renderer shares; the wire shape a sign-in-time acceptance sends the server (structurally `@owlmeans/marketing-consent`'s `TermsAcceptance`, no dependency on it) |
263
+ | `loginTermsHelper.termsDeferred(ctx)` | helper | True once a registered AND bound `LoginStep` declares `confirmsTerms` — the sign-in screen then renders no checkbox and blocks nothing, because the confirmation lives on that step instead |
264
+ | `resolveCredit(cfg?, brand?, service?)`, `ResolvedCredit` | function, type | The credit and copyright line |
265
+ | `FallbackLoginScreen`, `LoginScreenProps`, `LoginScreenComponent` | component, type | The plain screen used when no UI family registered one |
266
+ | `surrogatePath(ctx, target)`, `SurrogateTarget` | function, type | Where a surrogate window opens; `null` on an entrypoint list without the surrogate route |
267
+ | `loginResumeHelper` — `.resumeAction(outcome)`, `.loginAttemptError(outcome)`; `LoginResumeHelper`, `ResumeAction` | helper, enum | The shared readings of a `resume` outcome and of a finished attempt |
268
+ | `enterOidcAuthorization(model)` | function | Move a flow to the step that can authorize; idempotent |
269
+ | `loginTokenOf(ctx)` — `.adoptToken(token)`, `.revokeToken()`; `makeLoginTokenHelper`, `LoginTokenHelper` | helper | The single adoption and de-adoption paths |
270
+ | `loginLandingOf(ctx)` — `.landAfterLogin(opts?)`, `.continueLogin(opts?)`, `.landingUrl(landing)`; `makeLoginLandingHelper`, `LoginLandingHelper`, `useContinueLogin()` | helper, hook | Where a finished sign-in lands: pending steps, landing hooks, a suspended flow, else the fallback |
271
+ | `loginEnvHelper` — `isEmbedded`, `isSurrogate`, `markSurrogate`, `clearSurrogate`, `defaultLoginEnv`; `LoginEnvHelper` | helper | Environment probes behind `LoginEnv` |
272
+ | `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
273
 
56
- Auth service alias: `'auth'`.
274
+ Importing `./login` also registers the `login` strings of the `auth` library namespace in all
275
+ seven supported languages.
57
276
 
58
- ## Product-Viable Integration Notes
277
+ ## Common pitfalls
59
278
 
60
- - The manager web app imports `DEFAULT_ALIAS` from this package as the client-side guard alias.
61
- - `@owlmeans/web-oidc-rp/auth/plugins` is imported for side effects to register the `OIDC_CLIENT_AUTH` and `GOOGLE_CLIENT_AUTH` plugins.
62
- - Redirect plugins persist auth control state before leaving the app and restore it before submitting provider query params as `AuthCredentials`.
63
- - After login, the browser stores a normal OwlMeans bearer token; product authorization is enforced by server gates and handler checks.
279
+ - **Keep sign-in handlers synchronous.** `useLogin` and `useLogout` handlers await nothing before
280
+ delegating. Wrapping them in an `async` handler that awaits first makes the popup blocker eat the
281
+ surrogate window. The same rule binds a plugin's `begin` / `logout`, a method's `start` and every
282
+ precondition.
283
+ - **Never start login from an effect.** Without a user gesture the window is blocked. Render a
284
+ control and let the person click.
285
+ - **Adopt and drop tokens only through the auth service or `loginTokenOf(ctx).adoptToken` / `.revokeToken`.**
286
+ Hand-written access to `AUTH_RESOURCE` drifts from the envelope decoding beside it.
287
+ - **Import names from the right subpath.** The plugin contract (`AuthenticationPlugin`,
288
+ `AuthMethodMeta`, `pluginMethodSource`, tunnel helpers) resolves only through
289
+ `./manager/plugins`. Rendering types and the registry resolve through `./manager`. Importing
290
+ `./login` registers no authentication plugin.
291
+ - **Gate operator methods with `method.restricted`,** so the configuration has to name them. Gate
292
+ dev-only methods on `cfg.debug.supervisor`, never `cfg.debug.all`.
293
+ - **Declare `requiresRenderer`** on a plugin whose `Implementation` cannot render without a UI
294
+ package's `Renderer`. Otherwise the screen offers a button that crashes the page it opens.
295
+ - **Call `enterOidcAuthorization(model)` before every `authenticate`** in an OIDC flow, rather than
296
+ re-deriving the transition.
297
+ - **Authorization stays server-side.** The browser ends up holding an ordinary OwlMeans bearer token
298
+ whichever provider issued it. Product checks live in entrypoint gates and handlers, never in
299
+ client-only state.
300
+ - **The organization on the wire is its `entitySlug`.** Read it with `authHelper.entitySlugOf` from
301
+ `@owlmeans/auth`; the stable `entityId` never reaches the browser.
302
+ - **Overrides of the `login` strings cover all seven languages** (`en`, `pl`, `ru`, `be`, `uk`,
303
+ `es`, `de`).
64
304
 
65
- ## Related Packages
305
+ ## Related packages
66
306
 
67
- - [`@owlmeans/auth`](../auth) — `Auth` type returned by `useSelfAuth`
68
- - [`@owlmeans/auth-common`](../auth-common) — auth module route definitions
69
- - [`@owlmeans/server-auth`](../server-auth) — server-side counterpart
307
+ - [`@owlmeans/auth`](../auth) — `Auth`, aliases, `authHelper.entitySlugOf`, auth errors
308
+ - [`@owlmeans/auth-common`](../auth-common) — `authProtocols`, `authMiddleware`, guard aliases
309
+ - [`@owlmeans/web-client`](../web-client) — appends the auth service and login host; ships the dispatcher and login plugins
310
+ - [`@owlmeans/web-panel`](../web-panel) — the shadcn sign-in screen (`appendLoginScreen`)
311
+ - [`@owlmeans/client-panel`](../client-panel) — headless login-method and terms models
312
+ - [`@owlmeans/web-oidc-rp`](../web-oidc-rp) — OIDC relying party and its method source
313
+ - [`@owlmeans/web-auth`](../web-auth) — supervisor (PK) login plugin
314
+ - [`@owlmeans/client-socket`](../client-socket) — the socket hook `useWs` wraps
315
+ - [`@owlmeans/server-auth`](../server-auth) — the server-side counterpart
70
316
 
71
317
  <!-- owlmeans:agent-guidance:start -->
72
318
  ## Agent guidance
@@ -76,7 +322,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
76
322
  your project's skill store (`.agents/skills/`):
77
323
 
78
324
  ```sh
79
- npx @owlmeans/agent-skills
325
+ npx @owlmeans/agent-skills@^0.1.18-rc.53
80
326
  ```
81
327
 
82
328
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/client-auth",
4
- "version": "0.1.18-rc.6",
5
- "generatedAt": "2026-08-18T14:48:00.497Z",
4
+ "version": "0.1.18-rc.61",
5
+ "generatedAt": "2026-10-09T11:30:42.733Z",
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 — client-side auth manager and UI components, setupExternalAuthentication() to wire OAuth/OIDC flows. Auto-invoked when importing client-auth helpers or registering external authentication.
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,190 @@ user-invocable: false
8
8
  # @owlmeans/client-auth
9
9
 
10
10
  **Layer:** Client
11
- **Install:** `"@owlmeans/client-auth": "^0.1.18-rc.6"` in `dependencies`
11
+ **Install:** `"@owlmeans/client-auth": "^0.1.18-rc.61"` in `dependencies`
12
12
 
13
- ## Key Exports
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
14
34
 
15
35
  | Export | Description |
16
36
  |--------|-------------|
17
- | `setupExternalAuthentication(alias)` | Register OAuth/OIDC external auth flow module |
18
- | Auth components | Login UI primitives |
19
- | Auth service | Client-side auth manager service |
20
- | Modules | Auth module declarations |
21
- | Constants | Auth aliases (re-exports `AUTH_API`) |
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`, `authPluginHelper` → `.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
63
+
64
+ | Export | Description |
65
+ |--------|-------------|
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`, `authPluginHelper` | 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 |
73
+
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`.
22
77
 
23
- ## Subpath Exports
78
+ ## `./login` — the login-plugin host
24
79
 
25
- - `./manager` — client-side auth manager
26
- - `./manager/modules` — manager module declarations
27
- - `./manager/plugins` — pluggable token providers
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
+ | `loginMethodsHelper` → `.registerMethodSource`, `.listMethodSources`, `.resolveLoginMethods`, `.primaryLoginMethod` | Which sign-in methods are offered — see `login-methods` |
89
+ | `LoginMethod`, `LoginMethodSource`, `LoginMethodContext` | Method types |
90
+ | `loginLandingOf(ctx)` → `.landAfterLogin(opts?)`, `.continueLogin(opts?)`, `.landingUrl(landing)`; `useContinueLogin()` | The post-login landing decision (`src/login/land.ts`) — see `login-plugins`. `landAfterLogin` runs due landing hooks then delegates to `continueLogin`, which walks pending `LoginStep`s, then `flowLandingOf(ctx).resumeSuspendedFlow`, then `LandOptions.fallback ?? { alias: HOME }`; `landingUrl` builds the absolute URL a plugin's own `window.location.href` needs; `useContinueLogin()` is what a step's own screen calls once satisfied |
91
+ | `LoginStep`, `LoginLanding`, `LoginLandingHook`, `LandOptions`, `LoginLandingParams` | Landing-seam types. `registerStep`/`steps`/`onLanded`/`landingHooks` on `LoginService` are the same replace-by-alias, priority-sorted registries as `registerPlugin`. `LoginStep` also carries `confirmsTerms?: boolean` (this step is where the Terms confirmation lives — see `loginTermsHelper.termsDeferred` below) and `required?: boolean` (a throw/timeout in `pending` reads as PENDING, not "not pending" — see `login-plugins`) |
92
+ | `LOGIN_STEP_TIMEOUT`, `LOGIN_LANDED_STORAGE` | A step's `pending`/a hook's `landed` is bounded by the former; the latter is where the last-landed token is recorded (the raw string, never a digest) |
93
+ | `loginTermsHelper` → `.resolveTerms`, `.termsAccepted`, `.acceptTerms`, `.termsSentence`, `ResolvedTerms`, `ResolvedTermsDocument`, `TermsSentencePart` | The confirmation. `resolveTerms` produces `documents` (what the checkbox agrees to: terms, then billing/product/custom when configured) and `notices` (what is only disclosed: privacy, plus cookies per its own inclusion rule) — see the terms-confirmation section of `login-methods`. The extra `LoginTermsConfig` fields (`billing`, `product`, `documents`, `revisions`, `showRevision`, `localizedHrefs`) are added by module augmentation in `src/login/terms-config.ts`, never by editing `@owlmeans/config` — importing anything from `@owlmeans/client-auth/login` pulls it in |
94
+ | `loginTermsHelper.termsLabelResolver(translate, locale)`, `loginTermsHelper.termsAcceptanceOf(resolved, locale?)`, `TermsAcceptanceRef` | The ONE document-label resolver and ONE wire-shape builder every terms renderer/recorder shares (`FallbackLoginScreen`, `web-panel`'s `LoginTerms`, `web-marketing-consent`'s Terms box and `termsRecorder`) — no more hand-kept duplicate `DEFAULT_LABEL`/`resolveLabelFor` per package. `termsAcceptanceOf` keeps only `{key, href, revisedAt}` per document — structurally `@owlmeans/marketing-consent`'s `TermsAcceptance`, with no dependency on that package |
95
+ | `loginTermsHelper.termsDeferred(ctx)` | True once a registered AND BOUND `LoginStep` declares `confirmsTerms` — see the "Deferring the confirmation" section of `login-methods`. Reads `ctx.hasService`/`.hasEntrypoint` directly, never `ensureLoginService` (which has the side effect of registering an empty host) |
96
+ | `resolveCredit`, `ResolvedCredit` | The credit and copyright line |
97
+ | `FallbackLoginScreen`, `LoginScreenProps` (now also carries `locale?: string`, for `Intl.ListFormat` and a custom document's locale-keyed label), `LoginScreenComponent` | The plain sign-in screen a relying party renders when no UI family registered one |
98
+ | `surrogatePath(ctx, target)`, `SurrogateTarget` | Where a surrogate login window opens; `null` on an older entrypoint list |
99
+ | `loginResumeHelper.resumeAction(outcome)`, `ResumeAction`, `loginResumeHelper.loginAttemptError(outcome)` | The one reading of a `resume` outcome, and the one reading of a finished attempt |
100
+ | `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 |
101
+ | `enterOidcAuthorization(model)` | Move a flow to the step that can authorize — idempotent, call it before every `authenticate` |
102
+ | `loginTokenOf(ctx).adoptToken(token)`, `.revokeToken()` | The single adoption and de-adoption paths |
103
+ | `useLogin(target?)`, `useLogout(target?)` | Wiring for a sign-in / sign-out control. `useLogin`'s `target` is the screen to land on AFTER sign-in (parked, never navigated to first); `useLogout`'s is where the document goes once the session is gone |
104
+ | `loginStartOf(ctx).startLogin({ url, target?, go })`, `LoginStart` | The React-free decision behind `useLogin` (`src/login/start.ts`): a target with a session already held goes straight there; otherwise `begin` with the target and a continuation to `DISPATCHER` |
105
+ | `loginEnvHelper` → `.isEmbedded`, `.isSurrogate`, `.markSurrogate`, `.clearSurrogate`, `.defaultLoginEnv` | Environment probes the host builds `LoginEnv` from |
106
+ | `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`, `LOGIN_TARGET_TTL`, `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 |
107
+
108
+ ```typescript
109
+ import { useLogin, useLogout } from '@owlmeans/client-auth/login'
110
+
111
+ const [loginPath, onLogIn] = useLogin()
112
+ const onLogOut = useLogout()
113
+ ```
114
+
115
+ Both handlers are deliberately **not** async and await nothing before delegating — a window opened
116
+ after the user gesture has finished being handled is eaten by the popup blocker. Each resolves its
117
+ entrypoint **inside** the hook, never at module scope — `DISPATCHER` for `useLogin`, the surrogate
118
+ path for `useLogout`. A module body runs before `registerEntrypoints`, so `useLogin`'s lookup at
119
+ top level throws `Entrypoint dispatcher not found` during import, taking the whole render down.
120
+
121
+ `useLogin(target)` signs in FIRST and lands on `target` after, in an ordinary tab (redirect plugin)
122
+ and in a framed application (surrogate popup) alike, with every pending post-sign-in step run on the
123
+ way. Its `navigate` continuation is ALWAYS `DISPATCHER` — never the target, because a guarded screen
124
+ reached before sign-in renders signed out. The target travels as `LoginRequest.target`: the facade's
125
+ `begin`, past the preconditions, parks it with `flowLandingOf(ctx).suspendLanding`
126
+ (`@owlmeans/client-flow`, the `RESUME_FLOW` record its `resumeSuspendedFlow` reads, expiring after
127
+ `LOGIN_TARGET_TTL`) — the write is started, never awaited before the plugin, so a popup still opens
128
+ inside the gesture — and holds the continuation until the write has landed. The dispatcher's
129
+ `landAfterLogin` (landing hooks → pending steps such as a consent screen → the parked target) then
130
+ ends on it. An attempt that signs nobody in (`Blocked`, `Failed`, `Gesture`) discards the parked
131
+ target; a refused precondition never parks it. A session already held (the auth service's in-memory
132
+ `token`) goes straight to the target. Without a target, `useLogin()` is the plain sign-in to the
133
+ ordinary landing. A caller of `login().begin` that passes a `target` without a `navigate` leaves the
134
+ document at once, so its parked target is best-effort.
135
+
136
+ `useLogout` sends a continuation only when a target is given. Omitting it does not leave the session
137
+ behind: the token is revoked either way, and the plugin decides what the document does with no
138
+ continuation to run — `@owlmeans/web-client`'s redirect plugin reloads the page, because cached auth
139
+ reads only forget a session when the application is rebuilt. `useLogin` returns `[path, handler]`
140
+ (the path is the dispatcher's, with or without a target) and `useLogout` a bare handler — a logout
141
+ control has no address to point at.
142
+
143
+ Plugin selection, the shipped browser flows and the full invariant list: the `login-plugins` skill.
28
144
 
29
145
  ## Usage
30
146
 
31
147
  ```typescript
32
- import { setupExternalAuthentication } from '@owlmeans/client-auth'
148
+ import { appendAuthService, setupExternalAuthentication, DEFAULT_ALIAS } from '@owlmeans/client-auth'
149
+ import { appendLogin } from '@owlmeans/client-auth/login'
33
150
 
34
- setupExternalAuthentication(AUTH_WEB) // alias of the web auth service
151
+ // in makeContext:
152
+ appendAuthService(context) // registers under DEFAULT_ALIAS ('auth')
153
+ appendLogin(context)
154
+ setupExternalAuthentication(MY_WEB_SERVICE) // the service alias the provider redirects into
35
155
  ```
36
156
 
37
- ## Product-Viable Usage Notes
157
+ `@owlmeans/web-client`'s own `makeContext` already appends the auth service and the login host, so
158
+ a web application only calls these when it builds its context by hand.
159
+
160
+ ## Rules
38
161
 
39
- - The manager web app imports `DEFAULT_ALIAS` from `@owlmeans/client-auth` as the client-side equivalent of `DEFAULT_GUARD`.
40
- - It imports `@owlmeans/web-oidc-rp/auth/plugins` for side effects; that registers OIDC and Google authentication plugins in the client auth manager.
41
- - During Google/OIDC redirects, plugins persist the client auth control state, restore it on return, then submit `AuthCredentials` to the server auth flow.
42
- - The browser receives and stores a normal OwlMeans bearer token. Product authorization remains server-side through module gates and handler checks, not through client-only state.
162
+ - An application's translated legal destinations are opt-in `login.terms.localizedHrefs`, keyed by
163
+ document/notice key, then locale. `loginTermsHelper.termsHrefOf(doc, locale?)` selects the exact
164
+ configured locale, then its base language, then `doc.href`. `termsSentence` and `termsAcceptanceOf`
165
+ share that resolver: pass the same live UI language to both. The canonical href and revisions still
166
+ determine the agreement digest; translations alone never ask for a new acceptance. Supply known
167
+ published destinations, never infer another application's URL layout. Omitted maps preserve defaults.
168
+ - The bearer token lives in the `AUTH_RESOURCE` resource under the single id `USER_ID`. Read and
169
+ write it through the auth service or through `loginTokenOf(ctx).adoptToken` / `.revokeToken`;
170
+ hand-written storage access drifts from the envelope decoding that happens beside it.
171
+ - A plugin that persists state across a provider redirect drives the control's own
172
+ `persist()` / `restore()` / `hasPersistentState()` / `cleanUpState()`. They keep the type, stage
173
+ and allowance in the `FLOW_STATE` resource under an id this package owns and does not export —
174
+ restore before submitting the credential, and clean up after.
175
+ - After a sign-in, `DispatcherHOC.navigate` calls `loginLandingOf(ctx).landAfterLogin` — a pending
176
+ `LoginStep`, else a landing suspended in `@owlmeans/client-flow`
177
+ (`flowLandingOf(ctx).resumeSuspendedFlow`, one-shot, an entrypoint alias plus its query — parked
178
+ by a flow's `.suspendFlow` or by `useLogin(target)`), else `HOME` — read BEFORE `alias` is
179
+ defaulted to `HOME`. Login plugins that navigate on their own do the same — see `login-plugins`.
180
+ - A control that leads to a screen through sign-in is `useLogin(target)`. Never navigate to a guarded
181
+ screen to start a sign-in, and never hand a plugin a continuation that goes anywhere but the
182
+ dispatcher: the landing is the dispatcher's decision.
183
+ - The browser ends up holding an ordinary OwlMeans bearer token whichever provider issued the
184
+ login. Product authorization stays server-side, in entrypoint gates and handler checks — never in
185
+ client-only state.
186
+ - The organization entity a token names is its `entitySlug`, the renameable public name. Read it
187
+ with `authHelper.entitySlugOf` from `@owlmeans/auth`; the stable `entityId` never reaches the
188
+ browser.
43
189
 
44
190
  ## Depends On
45
191
 
46
- - `@owlmeans/auth`, `@owlmeans/auth-common`
47
- - `@owlmeans/client-context`, `@owlmeans/client-entrypoint`
192
+ - `@owlmeans/auth`, `@owlmeans/auth-common` — types, aliases, `authMiddleware`
193
+ - `@owlmeans/client`, `@owlmeans/client-context`, `@owlmeans/client-entrypoint`, `@owlmeans/client-socket`
194
+ - `@owlmeans/config` — the login screen, terms and credit configuration
195
+ - `@owlmeans/basic-envelope` — decoding the bearer envelope
48
196
  - `react` (peer)
197
+ - `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,cA4E3B,CAAA"}
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;AAgBzE,eAAO,MAAM,aAAa,EAAE,cAqG3B,CAAA"}