@learnorama/mozaik-sdk 0.14.1-staging.51.1 → 0.15.0-staging.53.1

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 (294) hide show
  1. package/CUSTOMER-ADMIN.md +101 -0
  2. package/CVS-PLAYBACK.md +135 -0
  3. package/PRESS.md +208 -0
  4. package/README.md +25 -11
  5. package/TV-LOGIN.md +137 -0
  6. package/dist/api/account.d.ts +1 -1
  7. package/dist/api/account.js.map +1 -1
  8. package/dist/api/admin-catalogue.d.ts +13 -1
  9. package/dist/api/admin-catalogue.js +53 -1
  10. package/dist/api/admin-catalogue.js.map +1 -1
  11. package/dist/api/admin-consumption.d.ts +1 -1
  12. package/dist/api/admin-consumption.js.map +1 -1
  13. package/dist/api/admin-contracts.d.ts +1 -1
  14. package/dist/api/admin-contracts.js.map +1 -1
  15. package/dist/api/admin-customer.d.ts +79 -0
  16. package/dist/api/admin-customer.js +213 -0
  17. package/dist/api/admin-customer.js.map +1 -0
  18. package/dist/api/admin-desk.d.ts +1 -1
  19. package/dist/api/admin-desk.js.map +1 -1
  20. package/dist/api/admin-licenses.js.map +1 -1
  21. package/dist/api/admin-partner-selections.d.ts +1 -1
  22. package/dist/api/admin-partner-selections.js.map +1 -1
  23. package/dist/api/admin-patrons.d.ts +1 -1
  24. package/dist/api/admin-patrons.js.map +1 -1
  25. package/dist/api/admin-reviews.d.ts +1 -1
  26. package/dist/api/admin-reviews.js.map +1 -1
  27. package/dist/api/admin-roles.d.ts +1 -1
  28. package/dist/api/admin-roles.js.map +1 -1
  29. package/dist/api/admin-support.d.ts +1 -1
  30. package/dist/api/admin-support.js.map +1 -1
  31. package/dist/api/admin-tenants.js.map +1 -1
  32. package/dist/api/admin-tutoring.d.ts +1 -1
  33. package/dist/api/admin-tutoring.js.map +1 -1
  34. package/dist/api/auth-device.d.ts +1 -1
  35. package/dist/api/auth-device.js.map +1 -1
  36. package/dist/api/auth.d.ts +1 -1
  37. package/dist/api/auth.js.map +1 -1
  38. package/dist/api/biblio-billing-webhooks.d.ts +1 -1
  39. package/dist/api/biblio-billing-webhooks.js.map +1 -1
  40. package/dist/api/biblio-billing.d.ts +1 -1
  41. package/dist/api/biblio-billing.js.map +1 -1
  42. package/dist/api/biblio-catalogue.d.ts +1 -1
  43. package/dist/api/biblio-catalogue.js.map +1 -1
  44. package/dist/api/biblio-home.d.ts +1 -1
  45. package/dist/api/biblio-home.js.map +1 -1
  46. package/dist/api/biblio-learning-author-profiles.d.ts +1 -1
  47. package/dist/api/biblio-learning-author-profiles.js.map +1 -1
  48. package/dist/api/biblio-learning-authoring.d.ts +1 -1
  49. package/dist/api/biblio-learning-authoring.js.map +1 -1
  50. package/dist/api/biblio-learning-editorial.d.ts +1 -1
  51. package/dist/api/biblio-learning-editorial.js.map +1 -1
  52. package/dist/api/biblio-learning-feedback.d.ts +1 -1
  53. package/dist/api/biblio-learning-feedback.js.map +1 -1
  54. package/dist/api/biblio-learning-finance.d.ts +1 -1
  55. package/dist/api/biblio-learning-finance.js.map +1 -1
  56. package/dist/api/biblio-learning-integrations.d.ts +1 -1
  57. package/dist/api/biblio-learning-integrations.js.map +1 -1
  58. package/dist/api/biblio-learning-membership.d.ts +1 -1
  59. package/dist/api/biblio-learning-membership.js.map +1 -1
  60. package/dist/api/biblio-learning.d.ts +1 -1
  61. package/dist/api/biblio-learning.js.map +1 -1
  62. package/dist/api/biblio-library.d.ts +13 -1
  63. package/dist/api/biblio-library.js +25 -1
  64. package/dist/api/biblio-library.js.map +1 -1
  65. package/dist/api/biblio-nextory.d.ts +1 -1
  66. package/dist/api/biblio-nextory.js.map +1 -1
  67. package/dist/api/biblio-press.d.ts +45 -1
  68. package/dist/api/biblio-press.js +125 -1
  69. package/dist/api/biblio-press.js.map +1 -1
  70. package/dist/api/biblio-search.js.map +1 -1
  71. package/dist/api/biblio-telemetry.js.map +1 -1
  72. package/dist/api/biblio-youscribe.d.ts +51 -0
  73. package/dist/api/biblio-youscribe.js +66 -0
  74. package/dist/api/biblio-youscribe.js.map +1 -0
  75. package/dist/api/consumption.d.ts +4 -4
  76. package/dist/api/consumption.js +3 -3
  77. package/dist/api/consumption.js.map +1 -1
  78. package/dist/api/cvs-app.d.ts +1 -1
  79. package/dist/api/cvs-app.js.map +1 -1
  80. package/dist/api/cvs-documents.d.ts +33 -1
  81. package/dist/api/cvs-documents.js +81 -1
  82. package/dist/api/cvs-documents.js.map +1 -1
  83. package/dist/api/cvs-favorites.d.ts +1 -1
  84. package/dist/api/cvs-favorites.js.map +1 -1
  85. package/dist/api/cvs-ils.d.ts +1 -1
  86. package/dist/api/cvs-ils.js.map +1 -1
  87. package/dist/api/cvs-loans.d.ts +1 -1
  88. package/dist/api/cvs-loans.js.map +1 -1
  89. package/dist/api/cvs-payments.d.ts +1 -1
  90. package/dist/api/cvs-payments.js.map +1 -1
  91. package/dist/api/cvs-streaming.d.ts +11 -0
  92. package/dist/api/cvs-streaming.js +19 -1
  93. package/dist/api/cvs-streaming.js.map +1 -1
  94. package/dist/api/index.d.ts +11 -8
  95. package/dist/api/index.js +11 -8
  96. package/dist/api/partner-catalogue.d.ts +21 -1
  97. package/dist/api/partner-catalogue.js +28 -1
  98. package/dist/api/partner-catalogue.js.map +1 -1
  99. package/dist/api/partner-reader-nextory.d.ts +52 -0
  100. package/dist/api/partner-reader-nextory.js +92 -0
  101. package/dist/api/partner-reader-nextory.js.map +1 -0
  102. package/dist/api/partner-reader-youscribe.d.ts +63 -0
  103. package/dist/api/partner-reader-youscribe.js +97 -0
  104. package/dist/api/partner-reader-youscribe.js.map +1 -0
  105. package/dist/api/profiles.d.ts +1 -1
  106. package/dist/api/profiles.js.map +1 -1
  107. package/dist/api/search.d.ts +6 -1
  108. package/dist/api/search.js +10 -1
  109. package/dist/api/search.js.map +1 -1
  110. package/dist/api/tutoring-catalogue.d.ts +1 -1
  111. package/dist/api/tutoring-catalogue.js.map +1 -1
  112. package/dist/api/tutoring-chat.d.ts +1 -1
  113. package/dist/api/tutoring-chat.js.map +1 -1
  114. package/dist/api/tutoring-dispatch.d.ts +1 -1
  115. package/dist/api/tutoring-dispatch.js.map +1 -1
  116. package/dist/api/tutoring-practitioner.js.map +1 -1
  117. package/dist/api/tutoring-requests.d.ts +1 -1
  118. package/dist/api/tutoring-requests.js.map +1 -1
  119. package/dist/auth.d.ts +1 -1
  120. package/dist/chunks/client2.d.ts +1 -1
  121. package/dist/chunks/connection.d.ts +1 -1
  122. package/dist/chunks/index.d.ts +1506 -560
  123. package/dist/index.d.ts +3 -3
  124. package/dist/index.js +3 -3
  125. package/dist/index.js.map +1 -1
  126. package/dist/models.d.ts +2 -2
  127. package/dist/models.js +157 -2
  128. package/dist/models.js.map +1 -1
  129. package/dist/react/account.d.ts +1 -1
  130. package/dist/react/account.js +1 -1
  131. package/dist/react/account.js.map +1 -1
  132. package/dist/react/admin-catalogue.d.ts +51 -1
  133. package/dist/react/admin-catalogue.js +110 -2
  134. package/dist/react/admin-catalogue.js.map +1 -1
  135. package/dist/react/admin-consumption.d.ts +1 -1
  136. package/dist/react/admin-consumption.js +1 -1
  137. package/dist/react/admin-consumption.js.map +1 -1
  138. package/dist/react/admin-contracts.d.ts +1 -1
  139. package/dist/react/admin-contracts.js +1 -1
  140. package/dist/react/admin-contracts.js.map +1 -1
  141. package/dist/react/admin-customer.d.ts +347 -0
  142. package/dist/react/admin-customer.js +545 -0
  143. package/dist/react/admin-customer.js.map +1 -0
  144. package/dist/react/admin-desk.d.ts +1 -1
  145. package/dist/react/admin-desk.js +1 -1
  146. package/dist/react/admin-desk.js.map +1 -1
  147. package/dist/react/admin-licenses.d.ts +1 -1
  148. package/dist/react/admin-licenses.js +1 -1
  149. package/dist/react/admin-licenses.js.map +1 -1
  150. package/dist/react/admin-partner-selections.d.ts +1 -1
  151. package/dist/react/admin-partner-selections.js +1 -1
  152. package/dist/react/admin-partner-selections.js.map +1 -1
  153. package/dist/react/admin-patrons.d.ts +1 -1
  154. package/dist/react/admin-patrons.js +1 -1
  155. package/dist/react/admin-patrons.js.map +1 -1
  156. package/dist/react/admin-reviews.d.ts +1 -1
  157. package/dist/react/admin-reviews.js +1 -1
  158. package/dist/react/admin-reviews.js.map +1 -1
  159. package/dist/react/admin-roles.d.ts +1 -1
  160. package/dist/react/admin-roles.js +1 -1
  161. package/dist/react/admin-roles.js.map +1 -1
  162. package/dist/react/admin-support.d.ts +1 -1
  163. package/dist/react/admin-support.js +1 -1
  164. package/dist/react/admin-support.js.map +1 -1
  165. package/dist/react/admin-tenants.d.ts +1 -1
  166. package/dist/react/admin-tenants.js +1 -1
  167. package/dist/react/admin-tenants.js.map +1 -1
  168. package/dist/react/admin-tutoring.d.ts +1 -1
  169. package/dist/react/admin-tutoring.js +1 -1
  170. package/dist/react/admin-tutoring.js.map +1 -1
  171. package/dist/react/auth-device.d.ts +1 -1
  172. package/dist/react/auth-device.js +1 -1
  173. package/dist/react/auth-device.js.map +1 -1
  174. package/dist/react/auth.d.ts +1 -1
  175. package/dist/react/auth.js +1 -1
  176. package/dist/react/auth.js.map +1 -1
  177. package/dist/react/biblio-billing-webhooks.d.ts +1 -1
  178. package/dist/react/biblio-billing-webhooks.js +1 -1
  179. package/dist/react/biblio-billing-webhooks.js.map +1 -1
  180. package/dist/react/biblio-billing.d.ts +1 -1
  181. package/dist/react/biblio-billing.js +1 -1
  182. package/dist/react/biblio-billing.js.map +1 -1
  183. package/dist/react/biblio-catalogue.d.ts +1 -1
  184. package/dist/react/biblio-catalogue.js +1 -1
  185. package/dist/react/biblio-catalogue.js.map +1 -1
  186. package/dist/react/biblio-home.d.ts +1 -1
  187. package/dist/react/biblio-home.js +1 -1
  188. package/dist/react/biblio-home.js.map +1 -1
  189. package/dist/react/biblio-learning-author-profiles.d.ts +1 -1
  190. package/dist/react/biblio-learning-author-profiles.js +1 -1
  191. package/dist/react/biblio-learning-author-profiles.js.map +1 -1
  192. package/dist/react/biblio-learning-authoring.d.ts +1 -1
  193. package/dist/react/biblio-learning-authoring.js +1 -1
  194. package/dist/react/biblio-learning-authoring.js.map +1 -1
  195. package/dist/react/biblio-learning-editorial.d.ts +1 -1
  196. package/dist/react/biblio-learning-editorial.js +1 -1
  197. package/dist/react/biblio-learning-editorial.js.map +1 -1
  198. package/dist/react/biblio-learning-feedback.d.ts +1 -1
  199. package/dist/react/biblio-learning-feedback.js +1 -1
  200. package/dist/react/biblio-learning-feedback.js.map +1 -1
  201. package/dist/react/biblio-learning-finance.d.ts +1 -1
  202. package/dist/react/biblio-learning-finance.js +1 -1
  203. package/dist/react/biblio-learning-finance.js.map +1 -1
  204. package/dist/react/biblio-learning-integrations.d.ts +1 -1
  205. package/dist/react/biblio-learning-integrations.js +1 -1
  206. package/dist/react/biblio-learning-integrations.js.map +1 -1
  207. package/dist/react/biblio-learning-membership.d.ts +1 -1
  208. package/dist/react/biblio-learning-membership.js +1 -1
  209. package/dist/react/biblio-learning-membership.js.map +1 -1
  210. package/dist/react/biblio-learning.d.ts +1 -1
  211. package/dist/react/biblio-learning.js +1 -1
  212. package/dist/react/biblio-learning.js.map +1 -1
  213. package/dist/react/biblio-library.d.ts +30 -1
  214. package/dist/react/biblio-library.js +54 -2
  215. package/dist/react/biblio-library.js.map +1 -1
  216. package/dist/react/biblio-nextory.d.ts +1 -1
  217. package/dist/react/biblio-nextory.js +1 -1
  218. package/dist/react/biblio-nextory.js.map +1 -1
  219. package/dist/react/biblio-press.d.ts +196 -1
  220. package/dist/react/biblio-press.js +310 -2
  221. package/dist/react/biblio-press.js.map +1 -1
  222. package/dist/react/biblio-search.d.ts +1 -1
  223. package/dist/react/biblio-search.js +1 -1
  224. package/dist/react/biblio-search.js.map +1 -1
  225. package/dist/react/biblio-telemetry.d.ts +1 -1
  226. package/dist/react/biblio-telemetry.js +1 -1
  227. package/dist/react/biblio-telemetry.js.map +1 -1
  228. package/dist/react/biblio-youscribe.d.ts +81 -0
  229. package/dist/react/biblio-youscribe.js +128 -0
  230. package/dist/react/biblio-youscribe.js.map +1 -0
  231. package/dist/react/chat.d.ts +1 -1
  232. package/dist/react/consumption.d.ts +4 -4
  233. package/dist/react/consumption.js +4 -4
  234. package/dist/react/consumption.js.map +1 -1
  235. package/dist/react/cvs-app.d.ts +1 -1
  236. package/dist/react/cvs-app.js +1 -1
  237. package/dist/react/cvs-app.js.map +1 -1
  238. package/dist/react/cvs-documents.d.ts +141 -1
  239. package/dist/react/cvs-documents.js +179 -2
  240. package/dist/react/cvs-documents.js.map +1 -1
  241. package/dist/react/cvs-favorites.d.ts +1 -1
  242. package/dist/react/cvs-favorites.js +1 -1
  243. package/dist/react/cvs-favorites.js.map +1 -1
  244. package/dist/react/cvs-ils.d.ts +1 -1
  245. package/dist/react/cvs-ils.js +1 -1
  246. package/dist/react/cvs-ils.js.map +1 -1
  247. package/dist/react/cvs-loans.d.ts +1 -1
  248. package/dist/react/cvs-loans.js +1 -1
  249. package/dist/react/cvs-loans.js.map +1 -1
  250. package/dist/react/cvs-payments.d.ts +1 -1
  251. package/dist/react/cvs-payments.js +1 -1
  252. package/dist/react/cvs-payments.js.map +1 -1
  253. package/dist/react/cvs-streaming.d.ts +39 -1
  254. package/dist/react/cvs-streaming.js +45 -2
  255. package/dist/react/cvs-streaming.js.map +1 -1
  256. package/dist/react/index.d.ts +11 -8
  257. package/dist/react/index.js +11 -8
  258. package/dist/react/partner-catalogue.d.ts +48 -1
  259. package/dist/react/partner-catalogue.js +54 -2
  260. package/dist/react/partner-catalogue.js.map +1 -1
  261. package/dist/react/partner-reader-nextory.d.ts +127 -0
  262. package/dist/react/partner-reader-nextory.js +190 -0
  263. package/dist/react/partner-reader-nextory.js.map +1 -0
  264. package/dist/react/partner-reader-youscribe.d.ts +152 -0
  265. package/dist/react/partner-reader-youscribe.js +224 -0
  266. package/dist/react/partner-reader-youscribe.js.map +1 -0
  267. package/dist/react/profiles.d.ts +1 -1
  268. package/dist/react/profiles.js +1 -1
  269. package/dist/react/profiles.js.map +1 -1
  270. package/dist/react/search.d.ts +6 -1
  271. package/dist/react/search.js +11 -2
  272. package/dist/react/search.js.map +1 -1
  273. package/dist/react/tutoring-catalogue.d.ts +1 -1
  274. package/dist/react/tutoring-catalogue.js +1 -1
  275. package/dist/react/tutoring-catalogue.js.map +1 -1
  276. package/dist/react/tutoring-chat.d.ts +1 -1
  277. package/dist/react/tutoring-chat.js +1 -1
  278. package/dist/react/tutoring-chat.js.map +1 -1
  279. package/dist/react/tutoring-dispatch.d.ts +1 -1
  280. package/dist/react/tutoring-dispatch.js +1 -1
  281. package/dist/react/tutoring-dispatch.js.map +1 -1
  282. package/dist/react/tutoring-practitioner.d.ts +1 -1
  283. package/dist/react/tutoring-practitioner.js +1 -1
  284. package/dist/react/tutoring-practitioner.js.map +1 -1
  285. package/dist/react/tutoring-requests.d.ts +1 -1
  286. package/dist/react/tutoring-requests.js +1 -1
  287. package/dist/react/tutoring-requests.js.map +1 -1
  288. package/package.json +12 -8
  289. package/dist/api/partner-reader.d.ts +0 -96
  290. package/dist/api/partner-reader.js +0 -164
  291. package/dist/api/partner-reader.js.map +0 -1
  292. package/dist/react/partner-reader.d.ts +0 -261
  293. package/dist/react/partner-reader.js +0 -368
  294. package/dist/react/partner-reader.js.map +0 -1
@@ -0,0 +1,101 @@
1
+ # Customer administrator integration
2
+
3
+ Use the release containing the `admin-customer` SDK module. Customer permissions
4
+ are separate from Mozaik staff permissions. No customer gets the internal desk
5
+ DTOs or unrestricted tenant selectors.
6
+
7
+ ## Sign in
8
+
9
+ Use `createMozaikAuth` from `@learnorama/mozaik-sdk/auth`, with the normal
10
+ `prepareSso` / `completeSso` PKCE flow and a callback at `/auth/callback`.
11
+ The initial integration shares the `mozaik-web` public Keycloak client. The
12
+ operator must allow the exact frontend origin in API redirects, Keycloak and
13
+ CORS: local port 5175, `https://admin.stg.mozaik.co` for staging, and the production
14
+ origin in production only. Configure `CUSTOMER_ADMIN_ORIGIN` consistently.
15
+
16
+ Passwords and password setup stay in Keycloak. This change does not implement
17
+ the mockup's separate six-digit email MFA or trusted device for 30 days. The
18
+ realm password policy applies to all newly set/changed passwords, not just
19
+ customer administrators; existing passwords are not reset.
20
+
21
+ An invitation email may first complete Keycloak required actions. A callback
22
+ with `kc_action_status=success` **and no OAuth authorization code** is not a
23
+ session: start the normal PKCE login. Do not pass that status as a login code.
24
+ After an authenticated verified callback from the configured admin origin, the
25
+ backend accepts the matching pending invitation. The explicit
26
+ `acceptCustomerAdminInvitation()` operation also supports accepting after login.
27
+
28
+ ## Read the organization and capabilities
29
+
30
+ ```ts
31
+ import {
32
+ readCustomerOrganization, readCustomerDashboard,
33
+ listCustomerBeneficiaries, listCustomerAdministrators,
34
+ } from '@learnorama/mozaik-sdk/api/admin-customer';
35
+
36
+ const organization = await readCustomerOrganization({ client });
37
+ if (organization.statsOpen) {
38
+ const dashboard = await readCustomerDashboard({ days: 90 }, { client });
39
+ }
40
+ if (organization.enrolledVisible) {
41
+ const page = await listCustomerBeneficiaries({ page: 1, limit: 24 }, { client });
42
+ }
43
+ ```
44
+
45
+ Use `organization.role` (also projected in auth principal roles):
46
+
47
+ | Role | Access in this first integration |
48
+ | --- | --- |
49
+ | `customer_admin_principal` | Enabled reads; invite/revoke administrators |
50
+ | `customer_admin_manager` | Enabled reads; no administrator or subscription management |
51
+ | `customer_admin_readonly` | Enabled reads; no mutations |
52
+
53
+ Beneficiary management/communication mutations are future rubrics; their absence
54
+ is not permission to reuse internal staff routes. All requests recheck native
55
+ grants, so revocation does not wait for access-token expiration. Users without
56
+ a customer grant receive 403. Support impersonation and child profiles cannot
57
+ act as customer administrators.
58
+
59
+ The home organization controls scope. A CSE cannot read descendants. A library
60
+ consortium may include its permitted library sites. `statsOpen` and
61
+ `enrolledVisible` are settings controlled by Mozaik, false until enabled;
62
+ administrators cannot enable their own access to personal data. The beneficiary
63
+ directory never exposes individual reading, tutoring or consumption history.
64
+
65
+ Use `readCustomerOrganizationOverview()` for totals and
66
+ `readCustomerContractUsage(contractId)` for organization-contract usage. A
67
+ personal contract or another customer's contract is refused. Usage units are
68
+ not an activation-code stock count. Missing branding/access/account-manager
69
+ metadata remains null. Connection metrics state their definition and tracking
70
+ coverage; a null previous value means no reliable comparison, not zero.
71
+
72
+ ## Invite and revoke
73
+
74
+ ```ts
75
+ import {
76
+ inviteCustomerAdministrator, revokeCustomerAdministrator,
77
+ } from '@learnorama/mozaik-sdk/api/admin-customer';
78
+
79
+ const invitation = await inviteCustomerAdministrator({
80
+ email: 'colleague@example.org', firstName: 'Camille', lastName: 'Martin',
81
+ role: 'customer_admin_readonly',
82
+ }, { client });
83
+ ```
84
+
85
+ An invitation lasts seven days and acceptance consumes it once. Existing
86
+ verified accounts keep their password; new accounts complete Keycloak email
87
+ verification/password setup. The local pending account grants no patron or
88
+ administrative rights before acceptance. A person already attached to another
89
+ organization requires staff reconciliation; email matching never moves them.
90
+
91
+ Only an existing principal can invite from the customer app. Mozaik staff use
92
+ `inviteFirstCustomerAdministrator(tenantId, body)` under their existing scope
93
+ and high-assurance authentication; the first role must be principal.
94
+ `listCustomerAdministrators` includes active and pending/expired invitations.
95
+ Use `revokeCustomerAdministrator(userId)` for active grants and
96
+ `revokeCustomerAdministratorInvitation(invitationId)` for pending invitations.
97
+ The last active principal is protected. Revocation removes the administrative
98
+ grant, not an existing beneficiary's personal account.
99
+
100
+ No existing legacy customer administrators are automatically granted access.
101
+ A release still needs explicit organization settings and first invitations.
@@ -0,0 +1,135 @@
1
+ # Cinema integration with the SDK
2
+
3
+ This guide targets the frontend-request update prepared on 27 October 2026.
4
+ Use the SDK from the release containing these operations; this document does
5
+ not mean the pending backend changes have already reached staging.
6
+
7
+ ## Browse and quote
8
+
9
+ Reuse the authenticated SDK client and active profile headers. Show Cinema
10
+ when `readCapabilities().cinema.catalogueAllowed` is true. Its `homeUrl`,
11
+ `catalogueUrl` and `accessUrl` correspond to these typed operations:
12
+
13
+ ```ts
14
+ import {
15
+ readCinemaHome, listCinemaFilms, readCinemaAccess,
16
+ } from '@learnorama/mozaik-sdk/api/cvs-documents';
17
+
18
+ const home = await readCinemaHome({ limit: 8 }, { client });
19
+ const films = await listCinemaFilms({ page: 1, limit: 24, sort: 'newest' }, { client });
20
+ const film = films.data[0];
21
+ const quote = film && await readCinemaAccess({ docId: film.id }, { client });
22
+ ```
23
+
24
+ Cards include `genres`, `durationSeconds`, `directors`, and `coverUrl` when known.
25
+ Null metadata is unknown. Follow each home row's `documentsUrl` for subsequent
26
+ pages; keep its filters. `listCvsSelectionDocuments` retrieves an owned CVS
27
+ list in recorded order. Search remains available separately; do not fetch the
28
+ whole inventory to classify it on the frontend.
29
+
30
+ `readCinemaAccess()` without `docId` is an overview. Several allowances must
31
+ **not be added together**: they can apply to different films. An overview with
32
+ more than 100 allowances sets `balancesComplete=false`. A film-specific quote
33
+ resolves the actual act, cost and allowance:
34
+
35
+ - `credits.left`, `total`, `until`, `grantedBy`, `contractId`, `contractReference`
36
+ describe the selected grant. `unlimited=true` has null amounts, not zero.
37
+ - `scope` says whether the allowance belongs to a profile, household or tenant.
38
+ - `allowed` and `reason` describe the current decision; this is not a reservation.
39
+ - `unitCost` is the actual consumption policy, currently one unit for a new act,
40
+ zero when starting playback under an existing valid loan. It is not the
41
+ document's catalogue price or legacy `unitCount`.
42
+ - `act`, `loanHours`, `validUntil`, and `actionUrl` distinguish direct viewing
43
+ from borrowing and from an already-held loan.
44
+
45
+ After borrowing or starting playback, invalidate/refetch `/me/cinema` for the
46
+ film and the overview. Never decrement a simulated balance. The mutation checks
47
+ rights and quota again, so a concurrent household spend may still return 409.
48
+
49
+ ## Explicit Play action
50
+
51
+ ```ts
52
+ import { borrow } from '@learnorama/mozaik-sdk/api/cvs-loans';
53
+ import { startSession, readDashManifest } from '@learnorama/mozaik-sdk/api/cvs-streaming';
54
+ import { renewLease, closeLease } from '@learnorama/mozaik-sdk/api/consumption';
55
+
56
+ // Run only after the user presses Play, with a fresh quote for this docId.
57
+ if (!quote?.allowed) throw new Error('This film is not currently available');
58
+ if (quote.actionUrl === '/cvs/loans') {
59
+ if (quote.act !== 'loan_view' && quote.act !== 'loan_download') {
60
+ throw new Error('Unexpected loan contract');
61
+ }
62
+ await borrow({ workId: film.id, act: quote.act, requestKey: crypto.randomUUID() }, { client });
63
+ }
64
+ const playback = await startSession({ docId: film.id }, {
65
+ client,
66
+ headers: {
67
+ 'Idempotency-Key': crypto.randomUUID(),
68
+ 'X-Client-Platform': 'web',
69
+ },
70
+ });
71
+ await readCinemaAccess({ docId: film.id }, { client });
72
+ ```
73
+
74
+ Retain each request key for an intentional retry of that same action. Borrow
75
+ and playback need different keys. Do not prefetch these mutations or blindly
76
+ retry them: opening DRM is an external operation. `streamingDetails(docId)` is
77
+ a read-only readiness check and returns no licence or lease.
78
+
79
+ Use `web` for Tizen/webOS, `android` for Android TV, `ios` for tvOS. These are
80
+ DRM/OS families; there is no fourth DRM family named `tv`.
81
+
82
+ ## Player and DRM
83
+
84
+ | Response field | Meaning |
85
+ | --- | --- |
86
+ | `streamUrl` / `drm.hls` | HLS manifest |
87
+ | `drm.dash` | Direct CDN MPEG-DASH manifest |
88
+ | `drm.normalizedDashUrl` | Optional authenticated backend MPD normalization route |
89
+ | `drm.widevine`, `drm.fairplay` | Licence acquisition URLs |
90
+ | `drm.certificate` | URL of public DER FairPlay application-certificate bytes |
91
+ | `drm.token` | Provider licence token; not the Mozaik bearer |
92
+ | `drm.kid` | Content key identifier; not a raw decryption key |
93
+ | `drm.contentId` | Provider media identity; not the CVS document ID |
94
+ | `lease.eventId`, `lease.expiresAt` | Playback heartbeat handle and deadline |
95
+
96
+ The SDK does not implement a player/CDM. Configure the player with its supported
97
+ provider protocol; its licence challenge returns the protected key material.
98
+ Fetch the public FairPlay certificate as bytes, not as a JSON or base64 field.
99
+ The configured legacy certificate is reachable but actual Safari/iOS playback
100
+ still needs device validation. Its embedded X.509 expiration alone was not used
101
+ to claim successful or failed FairPlay decryption.
102
+
103
+ For a DASH player affected by self-referencing `dependencyId`, fetch the
104
+ normalized MPD with the SDK, then pass a Blob URL to the player:
105
+
106
+ ```ts
107
+ const xml = await readDashManifest(film.id, { client });
108
+ const manifestUrl = URL.createObjectURL(new Blob([xml], { type: 'application/dash+xml' }));
109
+ // await player.load(manifestUrl);
110
+ // Revoke after player unload: URL.revokeObjectURL(manifestUrl);
111
+ ```
112
+
113
+ The backend removes only a representation's dependency on itself. Other
114
+ dependencies remain; segment BaseURL resolves to the original CDN. The endpoint
115
+ is bounded and read-only, and does not grant playback rights by itself. Do not
116
+ send API bearer/profile headers to CDN, certificate or licence hosts.
117
+
118
+ ## Heartbeat and stop
119
+
120
+ Call `renewLease(playback.lease.eventId, { client })` while playing, typically
121
+ every 30 seconds. Honor its returned deadline; a 404 means the lease is gone.
122
+ Call `closeLease` on stop/unmount/player failure. JSON carries the handle; no
123
+ `Location` header is needed. The durable database expiry survives Redis loss
124
+ and is the same deadline used by the reaper.
125
+
126
+ Playback has its own event, linked to a fixed-term loan where applicable.
127
+ Closing playback does not return the loan; renewing playback cannot extend the
128
+ loan's contractual deadline. Returning a loan releases a seat without refunding
129
+ spent units. Never substitute a loan ID, docId, or key ID for the playback event.
130
+
131
+ Handle 401 through SSO, 403 as a profile restriction, 404 as unavailable/unready,
132
+ 409 as exhausted quota or no seat, 429 using Retry-After, and 503 as provider
133
+ unavailability. Verify video **and audio**, licence challenge, several heartbeats,
134
+ and stop on each target platform after release. Provider-stubbed tests prove the
135
+ backend contract, not device/CDM interoperability.
package/PRESS.md ADDED
@@ -0,0 +1,208 @@
1
+ # Press integration
2
+
3
+ Use the SDK generated from the release containing these routes. Publications,
4
+ dated editions and articles have distinct identities:
5
+
6
+ | Object | Identity | Listing |
7
+ | --- | --- | --- |
8
+ | Publication / newspaper | `contentId`, plus provider `publicationId` when applicable | `listPressTitles` |
9
+ | Dated PressReader edition | publication + `issueDate` | `readIssues(contentId)` |
10
+ | Article | native `articleId`; separate `providerArticleId` | `listPressArticles`, `readPressReview` |
11
+ | Newspaper voucher offer | `lemonde`, `lequipe`, `lefigaro` | `listPressPartnerAccess` |
12
+
13
+ `readCapabilities()` supplies `biblio.pressArticlesUrl`, `pressReviewUrl` and
14
+ `pressPartnersUrl`. Provider cards identify their catalogue and reader action.
15
+ Use the same authenticated effective profile throughout. The backend filters
16
+ rights and age restrictions before counts, deduplication and pagination.
17
+
18
+ ## Article review
19
+
20
+ ```ts
21
+ import { request } from '@learnorama/mozaik-sdk';
22
+ import {
23
+ readPressReview, listPressArticles, openPressArticleReader,
24
+ } from '@learnorama/mozaik-sdk/api/biblio-press';
25
+ import type { PressArticlePage } from '@learnorama/mozaik-sdk/models';
26
+ import { renewLease, closeLease } from '@learnorama/mozaik-sdk/api/consumption';
27
+
28
+ const review = await readPressReview({ limit: 6 }, { client });
29
+ const first = review.sections[0];
30
+ if (first && first.articles.meta.totalPages > 1) {
31
+ // Keep the server's selection, date window and page-size filters intact.
32
+ const url = new URL(first.articlesUrl, client.baseUrl);
33
+ url.searchParams.set('page', '2');
34
+ const next = await request<PressArticlePage>(url.pathname + url.search, { client });
35
+ }
36
+
37
+ const page = await listPressArticles({
38
+ publicationId: '2526', page: 1, limit: 24,
39
+ }, { client });
40
+
41
+ // Only after the reader presses Open. Do not prefetch this mutation.
42
+ const article = page.data[0];
43
+ if (article) {
44
+ const opened = await openPressArticleReader(article.articleId, {
45
+ client, headers: { 'Idempotency-Key': crypto.randomUUID() },
46
+ });
47
+ // Open opened.url in the licensed reader. Never log its token-bearing URL.
48
+ // Renew while actually reading, and close when the reader exits:
49
+ await renewLease(opened.lease.eventId, { client });
50
+ await closeLease(opened.lease.eventId, { client });
51
+ }
52
+ ```
53
+
54
+ `/press-feed` remains a feed of publication cards. `/press-review` contains real
55
+ article cards and uses publication favourites already saved with
56
+ `mode=press_title`. It offers selected headlines and related themes; each
57
+ section includes its own page and continuation URL. Review selection applies
58
+ the editorial rules and a maximum of four articles per publication before
59
+ pagination. Monthly and other long-period editions remain eligible for their
60
+ recorded editorial lifetime. An ordinary article query defaults to seven days;
61
+ explicit `since`/`until` queries can cover up to 366 days.
62
+
63
+ Article metadata is local: GET requests do not contact PressReader or grant
64
+ reader access. `publishedAt` can be null because an issue date is not an article
65
+ publication timestamp. There is no full-text body endpoint; the licensed reader
66
+ opens the stored provider article ID and issue date. Empty review sections are
67
+ omitted. Invalidate the review after changing publication favourites and clear
68
+ profile-scoped caches when switching profiles.
69
+
70
+ ## Publisher codes
71
+
72
+ ```ts
73
+ import {
74
+ readPressPartnerAccess, claimPressPartnerCode,
75
+ } from '@learnorama/mozaik-sdk/api/biblio-press';
76
+
77
+ const state = await readPressPartnerAccess('lemonde', { client });
78
+ // Explicit user action only; GET/capabilities/onboarding never claim a code.
79
+ if (state.canRequestCode) {
80
+ const access = await claimPressPartnerCode('lemonde', { client });
81
+ // Show access.code and access.activationUrl privately to this household.
82
+ // Never log, persist in analytics, or automatically visit the activation URL.
83
+ }
84
+ ```
85
+
86
+ The same household reuses its existing eligible code across devices and adult
87
+ profiles. Concurrent requests cannot spend two codes for that household. Support
88
+ impersonation cannot reveal or allocate codes. A generic press category does
89
+ not override provider exclusions; contracts can permit each newspaper separately.
90
+
91
+ Allocation is not activation. `activatedOn`, `benefitEndsOn` and
92
+ `renewalAvailableOn` stay null until a publisher report confirms the date.
93
+ The legacy policies are Le Monde three calendar months; L’Équipe thirty days
94
+ with renewal after twenty-eight (older banks used three months); Le Figaro one
95
+ calendar month. Actual bank policy is explicit in the response.
96
+
97
+ Handle 404 as unavailable to this profile, 409 as no currently usable stock,
98
+ and 503 as unavailable configuration. Refetch status after a claim. A code claim
99
+ records an allocation audit, not a newspaper-reading session.
100
+
101
+ ## Operator setup
102
+
103
+ Global catalogue administrators with high-assurance authentication use the
104
+ generated `admin-catalogue` functions for offer/title bindings, bank policies,
105
+ code import, disablement and publisher activation reports. Imports are bounded
106
+ to 500 codes/report entries and are atomic. Same-bank retries are idempotent;
107
+ conflicting activation dates need an explicit correction reason. Lists and audit
108
+ records never contain code values.
109
+
110
+ Article refresh requires `PRESSREADER_DISCOVERY_ENABLED`,
111
+ `PRESSREADER_DISCOVERY_URL` and the private `PRESSREADER_DISCOVERY_KEY`.
112
+ The worker and minute dispatcher process one bounded page per task, retaining
113
+ previous article data on failures. Global administrators can enqueue a refresh
114
+ with a unique request key and inspect progress through
115
+ `/admin/press/article-refreshes`.
116
+
117
+ Voucher encryption uses the existing `COMMERCE_SECRET_KEY`; keep it stable.
118
+ Stock and confirmed-activation reports must be supplied separately. Importing
119
+ legacy records does not enable an offer or allocate any new code. Staging exports
120
+ omit real voucher stock; create synthetic test banks explicitly for frontend work.
121
+
122
+ ## Article enrichment and profile preferences
123
+
124
+ The frontend-request update adds `language`, `wordCount`, `readingMinutes`,
125
+ `topics`, catalogue `themes`, `read` and `lastOpenedAt` to article cards.
126
+ `read` means this profile opened the licensed reader, not that it finished the
127
+ article. `readingMinutes` is estimated at 200 words/minute **only when a full
128
+ article word count is known**. The sampled Discovery response has no such count;
129
+ null must remain absent in the UI. A short summary is not the full article.
130
+
131
+ `topics` are PressReader-supplied named entities, with stable native `topicId`
132
+ and counts over the accessible cached articles. They are distinct from the
133
+ publication's catalogue `themeId`; they are not an inferred editorial grouping
134
+ such as “pension reform.” `listPressTopics` defaults to issues since yesterday.
135
+ Counts describe imported, accessible articles, not the entire provider archive.
136
+
137
+ ```ts
138
+ import {
139
+ listPressTopics, followPressTopic, unfollowPressTopic,
140
+ readPressFacets, updateReadingPreferences, acknowledgePressReview,
141
+ } from '@learnorama/mozaik-sdk/api/biblio-press';
142
+
143
+ const topics = await listPressTopics({ limit: 20 }, { client });
144
+ // On the user's explicit Follow action:
145
+ if (topics.data[0]) await followPressTopic(topics.data[0].topicId, { client });
146
+ const filtered = await listPressArticles({ topicId: 21, lang: 'en', page: 1 }, { client });
147
+ const facets = await readPressFacets({ client });
148
+ await updateReadingPreferences({ language: 'fr', regionCode: 'FR-ARA' }, { client });
149
+ // Only once this review was actually displayed, never on prefetch:
150
+ await acknowledgePressReview({ through: review.generatedAt }, { client });
151
+ ```
152
+
153
+ Use actual IDs/codes from topics/facets in production. Regional facets require
154
+ explicitly mapped publications; a country or IP address does not establish a
155
+ publication's region. No mappings are fabricated. Language filters preserve
156
+ original-language headlines. GETs never advance the visit cursor or follow a
157
+ subject.
158
+
159
+ The section `key` is now a generated enum:
160
+
161
+ | Key | Selection |
162
+ | --- | --- |
163
+ | `headlines` | Selected headlines |
164
+ | `favoritesHeadlines` | Headlines from followed publications, unopened first |
165
+ | `basedOnFavorites` | Themes related to followed publications |
166
+ | `theme` | Explicit catalogue theme |
167
+ | `focus` | Most-covered supplied subject since yesterday, with counts |
168
+ | `followedTopics` | Subjects followed by this profile |
169
+ | `shortReads` | Known full word count from 1 to 200 |
170
+ | `longReads` | Known full word count of at least 1,600, recent month |
171
+ | `regional` | Explicit publication mapping and profile's selected region |
172
+ | `international` | Available articles outside the selected profile language |
173
+
174
+ Render existing nonempty sections; use `features[].available` and `reason` to
175
+ explain unavailable functionality. There is no integrated press-video source:
176
+ `video` is unavailable and no video section is invented. A long article is not
177
+ necessarily evergreen; the backend does not claim that classification.
178
+
179
+ `updatedAt` describes cached metadata updates. `generatedAt` is the display
180
+ snapshot for the explicit visit acknowledgment. `newCount` counts newly ingested
181
+ accessible articles after the stored visit; `newSince=null` on the first visit
182
+ means no prior baseline. It is not a count of articles the person finished.
183
+ Refetch article/review queries after opening, following, changing preferences,
184
+ or acknowledging a displayed review. Clear caches on profile switches.
185
+
186
+ Le Monde, L’Équipe and Le Figaro voucher offers stay outside publication/edition
187
+ kiosk lists. Use their explicit partner access operations; do not try to open
188
+ PressReader issues for a voucher offer.
189
+
190
+ ## Clear Biblio reading history
191
+
192
+ ```ts
193
+ import { clearReadingHistory } from '@learnorama/mozaik-sdk/api/biblio-library';
194
+ await clearReadingHistory({ state: 'finished' }, { client });
195
+ // state: 'in_progress', or omit the query for all Biblio profile history.
196
+ ```
197
+
198
+ The mutation is idempotent (204) and uses the active authenticated profile.
199
+ `listHistory` accepts the same state filter and returns its page total.
200
+ After clearing, refetch history and home-feed. Finished means progress >= 100;
201
+ other previously opened entries are in progress. New reading after the clearing
202
+ cutoff can reappear. Progress, favourites, downloads, loans, credits and the
203
+ accounting ledger stay intact. Support impersonation cannot clear it.
204
+
205
+ This is profile-visible Biblio history clearing. It is not a claim that all
206
+ provider, cinema, search or legally retained accounting records are erased.
207
+ The broader privacy-screen wording must match that scope until its separate
208
+ retention/erasure policy is implemented.
package/README.md CHANGED
@@ -131,17 +131,23 @@ articles or trending topics that the upstream catalogue does not contain.
131
131
  `reason: "license_required"`; a configured activation can be requested when the
132
132
  eligible adult explicitly opens the reader.
133
133
 
134
- - `POST /partners/youscribe/user/auth` opens that profile's reader. It provisions
135
- its subscription on first use and returns `{licenseNumber, token}`.
136
- - `POST /partners/youscribe/media/{mediaId}/reader-auth` additionally validates
137
- the chosen title and returns its consumption lease. Prefer it when a title
138
- has already been selected.
139
- - `GET /partners/youscribe/user/auth` and `GET /media/{mediaId}/package` require
140
- an existing licence and never create one. For the hosted reader, activate on
141
- the explicit user action before fetching its package URL.
142
-
143
- Call the generated `openNativeAuth`/`openNativeReader` functions, or invoke their
144
- TanStack **mutations** from the reader-opening event. Never invoke them on mount,
134
+ - `POST /biblio/me/youscribe/license` takes the seat. Call it on the patron's
135
+ Read click and nowhere else — not on entering the books section, prefetch,
136
+ bootstrap or onboarding: a seat is a paid subscription. It is idempotent and
137
+ the only call that allocates one, and it answers `409` with counters when the
138
+ household's four are taken. `DELETE` on the same path gives a seat back.
139
+ - `GET /partners/youscribe/user/auth` returns `{licenseNumber, token}` for the
140
+ native SDK. It requires an existing seat and never creates one. There is no
141
+ per-title variant: a YouScribe token is issued per patron, not per title.
142
+ - For the hosted (web) reader, read `reader.url` from
143
+ `GET /partners/youscribe/media/{mediaId}`. A `null` url carries
144
+ `unavailable: "forbidden"` (allowed to browse, not to read) or
145
+ `"unreachable"` (our side failed) — they are different answers.
146
+
147
+ Call the generated `createYouscribeLicense` function, or invoke its TanStack
148
+ **mutation**, from the reader-opening event. `openNativeAuth` and
149
+ `openNativeReader` still work as in 0.13.0 but are deprecated. Never invoke
150
+ any of them on mount,
145
151
  prefetch, login, onboarding, catalogue navigation, or for every household profile.
146
152
  One activated profile keeps the same provider identity across retries and releases;
147
153
  staging and production use different identities. No token should be persisted.
@@ -564,3 +570,11 @@ import { apiVersion, backendRevision, schemaSha256 } from '@learnorama/mozaik-sd
564
570
  Generated HTTP TypeScript types describe the API contract; they do not validate
565
571
  response bodies at runtime. The package contains the client and type declarations, not application
566
572
  credentials or backend implementation code.
573
+
574
+ ### Frontend integration guides
575
+
576
+ The published package includes [cinema browsing, quotes and playback](CVS-PLAYBACK.md),
577
+ [press articles, topics and publisher codes](PRESS.md),
578
+ [customer administrator sign-in and permissions](CUSTOMER-ADMIN.md), and
579
+ [TV pairing](TV-LOGIN.md). Use the guide and generated operations from the same
580
+ published release; an uncommitted local guide does not establish staging availability.
package/TV-LOGIN.md ADDED
@@ -0,0 +1,137 @@
1
+ # Connexion de l’application TV
2
+
3
+ La TV affiche un QR code et un code court. L’utilisateur ouvre la page web
4
+ d’activation sur son téléphone ou ordinateur, se connecte puis **confirme dans
5
+ Keycloak**. La TV reçoit ensuite une session utilisable par le SDK.
6
+
7
+ Ce document concerne les nouvelles routes et le SDK publiés avec cette modification.
8
+ Ils seront disponibles après la release habituelle ; aucun déploiement manuel n’est requis.
9
+
10
+ ## Côté TV
11
+
12
+ ```ts
13
+ import { startTvLogin } from '@learnorama/mozaik-sdk/auth';
14
+ import { createClient } from '@learnorama/mozaik-sdk';
15
+ import type { SessionRead } from '@learnorama/mozaik-sdk/models';
16
+
17
+ const login = await startTvLogin({
18
+ baseUrl: 'https://api.stg.mozaik.co',
19
+ onStatus: status => console.log(status), // pending ou slow_down, sans jeton
20
+ });
21
+
22
+ // Afficher :
23
+ // <img src={login.qrCodeDataUrl} alt="Connecter cette TV" />
24
+ // login.userCode, login.verificationUri et un compte à rebours vers login.expiresAt.
25
+
26
+ let session: SessionRead;
27
+ try {
28
+ session = await login.waitForSession();
29
+ } finally {
30
+ login.cancel(); // aussi à appeler si l’utilisateur quitte l’écran
31
+ }
32
+ const api = createClient({
33
+ baseUrl: 'https://api.stg.mozaik.co',
34
+ getAccessToken: () => session.access_token,
35
+ });
36
+ // session.account est le compte connecté ; api appelle les routes métier.
37
+ ```
38
+
39
+ Le helper génère le PKCE S256, garde le vérificateur et le code privé en mémoire,
40
+ attend avant le premier appel et respecte les intervalles. Un `slow_down`
41
+ augmente durablement le délai. Les erreurs transitoires et `Retry-After` entraînent
42
+ un recul borné par l’expiration du code. Refus et expiration arrêtent la boucle.
43
+ Plusieurs appels à `waitForSession()` partagent une seule boucle.
44
+
45
+ Un nouveau QR nécessite un nouvel appel à `startTvLogin()`. Annuler l’ancien
46
+ avant de remplacer l’écran. `cancel()` arrête l’appairage ; il ne déconnecte pas
47
+ une session déjà remise à l’application.
48
+
49
+ Pour renouveler ou fermer la session, utiliser **le clientId renvoyé**, normalement
50
+ `mozaik-tv`, avec les méthodes existantes :
51
+
52
+ ```ts
53
+ import { refreshSession, deleteSession } from '@learnorama/mozaik-sdk/api/auth';
54
+
55
+ session = await refreshSession({
56
+ clientId: login.clientId,
57
+ refresh_token: session.refresh_token!,
58
+ }, { client: api });
59
+
60
+ await deleteSession({
61
+ clientId: login.clientId,
62
+ refresh_token: session.refresh_token!,
63
+ }, { client: api });
64
+ ```
65
+
66
+ Le store d’authentification TV doit sérialiser les renouvellements, appliquer les
67
+ durées `expires_in`/`refresh_expires_in`, et oublier ses jetons à la déconnexion.
68
+ S’il persiste des credentials, utiliser le stockage sécurisé de la plateforme.
69
+ Les règles de durée et de révocation SSO restent celles du realm Keycloak.
70
+
71
+ ## Page web à ajouter : `/activate`
72
+
73
+ Le QR mène à `FRONTEND_HOST/activate?user_code=ABCD-EFGH`.
74
+ La même page sans paramètre propose la saisie manuelle du code affiché sur la TV.
75
+
76
+ 1. Lire `user_code` ou laisser l’utilisateur le saisir.
77
+ 2. S’il n’est pas connecté dans ce navigateur, utiliser **le parcours de connexion
78
+ déjà présent dans l’application**. Conserver `/activate?user_code=...` comme
79
+ `returnTo`. Cela permet aussi aux utilisateurs SSO de choisir leur connexion
80
+ habituelle ; ne pas les envoyer directement sur un formulaire de mot de passe.
81
+ 3. Avec le client authentifié, appeler :
82
+
83
+ ```ts
84
+ import { prepareDeviceVerification } from '@learnorama/mozaik-sdk/api/auth-device';
85
+
86
+ const { verificationUrl } = await prepareDeviceVerification({
87
+ userCode: userCode.trim().toUpperCase(),
88
+ }, { client: auth.client });
89
+
90
+ window.location.assign(verificationUrl);
91
+ ```
92
+
93
+ Keycloak affiche la TV demandant l’accès et exige une confirmation, y compris
94
+ si le navigateur est déjà connecté ou a déjà approuvé une autre TV. L’utilisateur
95
+ peut refuser. Après confirmation, la page indique que l’on peut la fermer ; le
96
+ polling débloque la TV. Le front n’a pas d’endpoint d’« approbation automatique ».
97
+
98
+ Pour une connexion interne, le helper SSO existant est `prepareInternalSso` ;
99
+ pour un partenaire, `prepareProviderSso(alias)` ; pour un compte natif,
100
+ `prepareProviderSso('native')`. Réutiliser la sélection déjà faite par le front.
101
+ Un jeton mobile seul ne crée pas de cookie SSO dans le navigateur : le QR doit
102
+ passer par la page web d’activation et son parcours de connexion.
103
+
104
+ ## Contrat HTTP, également utilisable hors JavaScript
105
+
106
+ | Appel | Entrée | Résultat |
107
+ | --- | --- | --- |
108
+ | `POST /auth/device-authorizations` | `codeChallenge` : SHA-256/base64url du vérificateur | QR PNG en data URL, `userCode`, liens d’activation, `deviceCode` privé, `clientId`, `expiresIn`, `interval` |
109
+ | `POST /auth/device-authorizations/verification` | `userCode`, bearer du compte connecté | URL de confirmation Keycloak ; cet appel n’approuve pas la TV |
110
+ | `POST /auth/sessions/device` | `deviceCode`, `codeVerifier` | `pending`, `slow_down`, `denied`, `expired`, ou `authorized` avec `session` |
111
+ | `PUT /auth/sessions` | `clientId`, `refresh_token` | Session renouvelée |
112
+ | `DELETE /auth/sessions` | `clientId`, `refresh_token` | Déconnexion suivant les règles SSO existantes |
113
+
114
+ Le code dure cinq minutes. `deviceCode` et `codeVerifier` ne doivent jamais
115
+ apparaître dans un QR, une URL, une capture de diagnostic ou un log. Le code
116
+ court `userCode` ne permet pas d’obtenir les jetons. Sur `slow_down`, augmenter
117
+ le délai courant d’au moins cinq secondes ; sur `401`, démarrer un nouvel
118
+ appairage ; sur `429`/`503`, respecter `Retry-After` sans dépasser l’expiration.
119
+
120
+ ## Profils et sécurité
121
+
122
+ Le navigateur et les TV peuvent partager un `sid` de SSO Keycloak. Un nonce
123
+ signé par Keycloak et conservé au renouvellement distingue chaque appairage TV
124
+ dans le mécanisme existant des profils. Changer de profil sur une TV ne change
125
+ pas celui du téléphone ou d’une autre TV. Cela n’altère pas les règles globales
126
+ de révocation de la session SSO du broker.
127
+
128
+ Une session de support ou un profil dépendant ne peut pas lancer la vérification.
129
+ Le backend vérifie aussi le verrou enfant du navigateur avant d’enregistrer la
130
+ TV : appeler directement le broker ne donne pas accès à l’API avec un jeton TV
131
+ non enregistré. Toujours terminer via `POST /auth/sessions/device`.
132
+
133
+ La release inclut le client public `mozaik-tv`, le PKCE et son mapper de nonce
134
+ dans la configuration Keycloak/Helm. Aucun nouveau secret fournisseur n’est
135
+ nécessaire. Si `KEYCLOAK_ALLOWED_CLIENT_IDS` est surchargé explicitement dans un
136
+ environnement, y inclure `mozaik-tv`. `FRONTEND_HOST` doit désigner le front qui
137
+ sert `/activate`.
@@ -1,5 +1,5 @@
1
1
 
2
- import { Q as ServiceListRead, Vr as OnboardingRead, cg as AccountUpdateWrite, ll as FrontendCapabilitiesRead, ug as AccountDetailRead } from "../chunks/index.js";
2
+ import { Il as FrontendCapabilitiesRead, Q as ServiceListRead, si as OnboardingRead, x_ as AccountDetailRead, y_ as AccountUpdateWrite } from "../chunks/index.js";
3
3
  import { c as request } from "../chunks/client.js";
4
4
  //#region src/api/account/account.d.ts
5
5
  export declare const getDeleteAccountUrl: () => string;