json-rest-api 1.0.22 → 1.0.23

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/CLAUDE.md +1 -2
  2. package/JSKIT_BACKEND_MIGRATION_TRACKER.txt +180 -0
  3. package/SINGLE_VALIDATION_CONTRACT_TODO.txt +172 -0
  4. package/docs/API.md +51 -14
  5. package/docs/COMPARISON.md +2 -2
  6. package/docs/GUIDE/GUIDE_1_Initial_Setup.md +28 -1
  7. package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +5 -1
  8. package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +3 -1
  9. package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +12 -60
  10. package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +2 -2
  11. package/docs/GUIDE/GUIDE_3_Field_Transformations.md +5 -1
  12. package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +2 -0
  13. package/docs/GUIDE/GUIDE_X_Autofiltering.md +355 -0
  14. package/docs/GUIDE/GUIDE_X_Fastify.md +78 -0
  15. package/docs/GUIDE/GUIDE_X_Knex_Schema_And_Migrations.md +250 -0
  16. package/docs/GUIDE/GUIDE_X_Query_Projections.md +159 -0
  17. package/docs/GUIDE/GUIDE_X_Writing_Plugins.md +199 -0
  18. package/docs/GUIDE/index.md +18 -9
  19. package/docs/QUICKSTART.md +5 -1
  20. package/docs/index.md +1 -1
  21. package/index.js +3 -2
  22. package/package.json +2 -2
  23. package/plugins/core/connectors/express-plugin.js +97 -184
  24. package/plugins/core/connectors/fastify-plugin.js +206 -0
  25. package/plugins/core/connectors/lib/connector-core.js +195 -0
  26. package/plugins/core/connectors/lib/transport-http-helpers.js +122 -0
  27. package/plugins/core/connectors/lib/transport-route-schemas.js +92 -0
  28. package/plugins/core/lib/dbIntrospection.js +1078 -0
  29. package/plugins/core/lib/dbTablesOperations.js +1104 -160
  30. package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +5 -2
  31. package/plugins/core/lib/querying/knex-process-includes.js +3 -4
  32. package/plugins/core/lib/querying/knex-query-helpers.js +17 -71
  33. package/plugins/core/lib/querying/knex-relationship-includes.js +466 -206
  34. package/plugins/core/lib/querying/query-builder-utils.js +19 -0
  35. package/plugins/core/lib/querying/query-field-sort-helpers.js +135 -0
  36. package/plugins/core/lib/querying/sort-helpers.js +33 -0
  37. package/plugins/core/lib/querying/storage-adapter-utils.js +51 -0
  38. package/plugins/core/lib/querying/url-helpers.js +8 -2
  39. package/plugins/core/lib/querying-writing/compile-schemas.js +12 -1
  40. package/plugins/core/lib/querying-writing/knex-field-helpers.js +83 -13
  41. package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +13 -12
  42. package/plugins/core/lib/querying-writing/query-field-helpers.js +88 -0
  43. package/plugins/core/lib/querying-writing/request-contracts.js +590 -0
  44. package/plugins/core/lib/storage/canonical-storage-mapping.js +113 -0
  45. package/plugins/core/lib/storage/storage-adapter.js +79 -2
  46. package/plugins/core/lib/storage/storage-mapping.js +204 -0
  47. package/plugins/core/rest-api-anyapi-knex-plugin.js +355 -228
  48. package/plugins/core/rest-api-autofilter-plugin.js +414 -0
  49. package/plugins/core/rest-api-knex-plugin.js +249 -211
  50. package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +10 -5
  51. package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +12 -6
  52. package/plugins/core/rest-api-plugin-methods/apply-query-filters.js +41 -0
  53. package/plugins/core/rest-api-plugin-methods/common.js +121 -83
  54. package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +6 -1
  55. package/plugins/core/rest-api-plugin-methods/get.js +23 -14
  56. package/plugins/core/rest-api-plugin-methods/patch.js +28 -12
  57. package/plugins/core/rest-api-plugin-methods/post.js +13 -8
  58. package/plugins/core/rest-api-plugin-methods/put.js +45 -21
  59. package/plugins/core/rest-api-plugin-methods/query.js +15 -53
  60. package/plugins/core/rest-api-plugin.js +2 -0
  61. package/plugins/core/rest-api-positioning-plugin.js +1 -1
  62. package/plugins/core/rest-api-query-projections-plugin.js +130 -0
  63. package/plugins/core/socketio-plugin.js +1 -3
  64. package/tests/autofilter.test.js +463 -0
  65. package/tests/custom-id-simplified-post.test.js +75 -0
  66. package/tests/custom-idproperty-relationships.test.js +33 -0
  67. package/tests/db-introspection.test.js +518 -0
  68. package/tests/db-table-operations.test.js +781 -0
  69. package/tests/fastify-plugin.test.js +141 -0
  70. package/tests/field-getters.test.js +1 -2
  71. package/tests/field-setters.test.js +0 -4
  72. package/tests/fixtures/api-configs.js +188 -219
  73. package/tests/helpers/fake-fastify.js +99 -0
  74. package/tests/http-connectors-parity.test.js +235 -0
  75. package/tests/projected-fields.test.js +319 -0
  76. package/tests/relationships.test.js +3 -3
  77. package/tests/searchschema-knex-behavior.test.js +187 -0
  78. package/tests/searchschema-merge.test.js +21 -69
  79. package/tests/storage-mapping.test.js +210 -0
  80. package/audit/00-charter.md +0 -103
  81. package/audit/01-scope-map.md +0 -84
  82. package/audit/02-coverage-matrix.csv +0 -174
  83. package/audit/03-invariants.md +0 -123
  84. package/audit/04-standards-rubric.md +0 -56
  85. package/audit/05-competitor-rubric.md +0 -77
  86. package/audit/06-findings.md +0 -160
  87. package/audit/07-risk-register.csv +0 -10
  88. package/audit/08-remediation-roadmap.md +0 -177
  89. package/audit/09-signoff-check.md +0 -23
  90. package/audit/10-final-synthesis.md +0 -29
  91. package/audit/evidence/code/all-files-json-rest-api.txt +0 -168
  92. package/audit/evidence/code/all-files-json-rest-schema.txt +0 -12
  93. package/audit/evidence/code/boundary-leak-checks.txt +0 -72
  94. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__bulk-operations-plugin.js.txt +0 -480
  95. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__express-plugin.js.txt +0 -532
  96. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__busboy-detector.js.txt +0 -175
  97. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__formidable-detector.js.txt +0 -138
  98. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__request-helpers.js.txt +0 -222
  99. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__file-handling-plugin.js.txt +0 -342
  100. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__dbTablesOperations.js.txt +0 -304
  101. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__fractional-positioning.js.txt +0 -188
  102. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__compile-schemas.js.txt +0 -336
  103. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__connectors-query-parser.js.txt +0 -165
  104. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__database-capabilities.js.txt +0 -265
  105. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__database-value-normalizers.js.txt +0 -369
  106. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__default-data-helpers.js.txt +0 -72
  107. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__field-utils.js.txt +0 -139
  108. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-constants.js.txt +0 -45
  109. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-field-helpers.js.txt +0 -396
  110. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-json-api-transformers.js.txt +0 -162
  111. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__payload-validators.js.txt +0 -1314
  112. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__schema-helpers.js.txt +0 -488
  113. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__scope-validations.js.txt +0 -484
  114. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__simplified-helpers.js.txt +0 -857
  115. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-cross-table-search.js.txt +0 -565
  116. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-json-api-transformers-querying.js.txt +0 -485
  117. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-pagination-helpers.js.txt +0 -560
  118. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-process-includes.js.txt +0 -167
  119. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-query-helpers-base.js.txt +0 -109
  120. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-query-helpers.js.txt +0 -1064
  121. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-relationship-includes.js.txt +0 -1983
  122. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-window-queries.js.txt +0 -328
  123. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__url-helpers.js.txt +0 -119
  124. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__storage__storage-adapter.js.txt +0 -160
  125. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__writing__many-to-many-manipulations.js.txt +0 -300
  126. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__writing__relationship-processor.js.txt +0 -245
  127. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-access.js.txt +0 -459
  128. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-cors-plugin.js.txt +0 -237
  129. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-knex-plugin.js.txt +0 -1135
  130. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-multihome-plugin.js.txt +0 -314
  131. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__compile-resource-schemas.js.txt +0 -7
  132. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__register-relationship-routes.js.txt +0 -71
  133. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__register-scope-routes.js.txt +0 -102
  134. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__turn-scope-init-into-vars.js.txt +0 -49
  135. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__validate-include-configurations.js.txt +0 -39
  136. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__add-route.js.txt +0 -23
  137. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__check-permissions.js.txt +0 -13
  138. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__common.js.txt +0 -639
  139. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__delete-relationship.js.txt +0 -117
  140. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__delete.js.txt +0 -104
  141. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__enrich-attributes.js.txt +0 -199
  142. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get-related.js.txt +0 -353
  143. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get-relationship.js.txt +0 -61
  144. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get.js.txt +0 -184
  145. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__patch-relationship.js.txt +0 -55
  146. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__patch.js.txt +0 -191
  147. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__post-relationship.js.txt +0 -110
  148. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__post.js.txt +0 -168
  149. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__put.js.txt +0 -253
  150. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__query.js.txt +0 -207
  151. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__release.js.txt +0 -12
  152. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin.js.txt +0 -165
  153. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-positioning-plugin.js.txt +0 -668
  154. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__socketio-plugin.js.txt +0 -621
  155. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__storage__local-storage.js.txt +0 -218
  156. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__storage__s3-storage.js.txt +0 -124
  157. package/audit/evidence/code/critical-pass1/json-rest-schema__src__CorePlugin.js.txt +0 -269
  158. package/audit/evidence/code/critical-pass1/json-rest-schema__src__Schema.js.txt +0 -242
  159. package/audit/evidence/code/critical-pass1/json-rest-schema__src__index.js.txt +0 -77
  160. package/audit/evidence/code/critical-pass1-risk-keywords.txt +0 -238
  161. package/audit/evidence/code/critical-pass1-summary.csv +0 -67
  162. package/audit/evidence/code/critical-pass2-summary.csv +0 -67
  163. package/audit/evidence/code/early-silent-zones-no-test-token.txt +0 -57
  164. package/audit/evidence/code/error-model-signals.txt +0 -98
  165. package/audit/evidence/code/git-files-json-rest-api.txt +0 -171
  166. package/audit/evidence/code/git-files-json-rest-schema.txt +0 -27
  167. package/audit/evidence/code/hook-flow-signals.txt +0 -282
  168. package/audit/evidence/code/idproperty-signals.txt +0 -157
  169. package/audit/evidence/code/import-cycles-non-anyapi.txt +0 -3
  170. package/audit/evidence/code/import-hotspots-non-anyapi.txt +0 -96
  171. package/audit/evidence/code/imports-json-rest-api.txt +0 -354
  172. package/audit/evidence/code/imports-json-rest-schema.txt +0 -15
  173. package/audit/evidence/code/in-scope-files-json-rest-api.txt +0 -160
  174. package/audit/evidence/code/in-scope-files-json-rest-schema.txt +0 -13
  175. package/audit/evidence/code/non-anyapi-core-files.txt +0 -101
  176. package/audit/evidence/code/permission-signals.txt +0 -193
  177. package/audit/evidence/code/query-include-signals.txt +0 -687
  178. package/audit/evidence/code/transaction-signals.txt +0 -28
  179. package/audit/evidence/code/validation-signals.txt +0 -650
  180. package/audit/evidence/code/write-path-signals.txt +0 -61
  181. package/audit/evidence/docs/docs-claims-signals.txt +0 -1075
  182. package/audit/evidence/docs/schema-docs-claims-signals.txt +0 -179
  183. package/audit/evidence/external/corpus-freeze.md +0 -21
  184. package/audit/evidence/external/npm-competitor-metadata.txt +0 -70
  185. package/audit/evidence/external/npm-download-signals.txt +0 -23
  186. package/audit/evidence/external/npm-jsonapi-server.txt +0 -8
  187. package/audit/evidence/external/npm-modern-jsonapi-metadata.txt +0 -38
  188. package/audit/evidence/external/readme-fortune-signals.txt +0 -26
  189. package/audit/evidence/external/readme-fortune.md +0 -165
  190. package/audit/evidence/external/readme-json-api-serializer-signals.txt +0 -80
  191. package/audit/evidence/external/readme-json-api-serializer.md +0 -690
  192. package/audit/evidence/external/readme-jsonapi-serde-server-signals.txt +0 -0
  193. package/audit/evidence/external/readme-jsonapi-serde-server.md +0 -22
  194. package/audit/evidence/external/readme-jsonapi-serializer-signals.txt +0 -63
  195. package/audit/evidence/external/readme-jsonapi-serializer.md +0 -315
  196. package/audit/evidence/external/readme-jsonapi-server-signals.txt +0 -19
  197. package/audit/evidence/external/readme-jsonapi-server.md +0 -104
  198. package/audit/evidence/external/readme-nestjs-json-api-signals.txt +0 -4
  199. package/audit/evidence/external/readme-nestjs-json-api.md +0 -94
  200. package/audit/evidence/external/readme-yaser-json-api-nestjs-signals.txt +0 -47
  201. package/audit/evidence/external/readme-yaser-json-api-nestjs.md +0 -447
  202. package/audit/evidence/external/standard-jsonapi-snippets.txt +0 -84
  203. package/audit/evidence/external/standard-rfc-snippets.txt +0 -168
  204. package/audit/evidence/external/standards-sources.txt +0 -6
  205. package/audit/evidence/runtime/baseline-commits.txt +0 -2
  206. package/audit/evidence/runtime/db-adapter-assumptions.txt +0 -3
  207. package/audit/evidence/runtime/environment.txt +0 -4
  208. package/audit/evidence/runtime/invariant-runtime-mapping.md +0 -38
  209. package/audit/evidence/runtime/probe-patch-relationship-error.exitcode +0 -1
  210. package/audit/evidence/runtime/probe-patch-relationship-error.log +0 -76
  211. package/audit/evidence/runtime/probe-patch-relationship-error.mjs +0 -67
  212. package/audit/evidence/runtime/probe-patch-relationship-referenceerror.exitcode +0 -1
  213. package/audit/evidence/runtime/probe-patch-relationship-referenceerror.log +0 -62
  214. package/audit/evidence/runtime/probe-patch-relationship-referenceerror.mjs +0 -47
  215. package/audit/evidence/runtime/test-exitcodes.txt +0 -4
  216. package/audit/evidence/runtime/test-summary.md +0 -107
  217. package/audit/evidence/runtime/trace-anchors.txt +0 -61
  218. package/audit/evidence/tests/access-multitenancy-test-signals.txt +0 -125
  219. package/audit/evidence/tests/failing-tests-summary.txt +0 -25
  220. package/audit/evidence/tests/npm-test-json-rest-api.exitcode +0 -1
  221. package/audit/evidence/tests/npm-test-json-rest-api.log +0 -5885
  222. package/audit/evidence/tests/npm-test-json-rest-schema.exitcode +0 -1
  223. package/audit/evidence/tests/npm-test-json-rest-schema.log +0 -356
  224. package/audit/evidence/tests/query-include-test-signals.txt +0 -948
  225. package/audit/evidence/tests/relationship-test-signals.txt +0 -223
  226. package/audit/evidence/tests/skipped-tests.txt +0 -10
  227. package/audit/evidence/tests/test-suite-list-non-anyapi.txt +0 -30
  228. package/audit/evidence/tests/test-suite-list.txt +0 -32
  229. package/audit/subsystems/docs-drift.md +0 -49
  230. package/audit/subsystems/pass2-cross-review.md +0 -131
  231. package/audit/subsystems/plugins-access.md +0 -51
  232. package/audit/subsystems/plugins-bulk.md +0 -50
  233. package/audit/subsystems/plugins-cors.md +0 -48
  234. package/audit/subsystems/plugins-file.md +0 -53
  235. package/audit/subsystems/plugins-multihome.md +0 -50
  236. package/audit/subsystems/plugins-positioning.md +0 -54
  237. package/audit/subsystems/plugins-socketio.md +0 -50
  238. package/audit/subsystems/query-engine.md +0 -61
  239. package/audit/subsystems/relationships.md +0 -55
  240. package/audit/subsystems/rest-orchestration.md +0 -66
  241. package/audit/subsystems/schema-core.md +0 -64
  242. package/audit/subsystems/storage-knex.md +0 -57
  243. package/audit/subsystems/tests.md +0 -48
  244. package/audit/subsystems/transport-express.md +0 -55
  245. package/audit/traces/trace-read-get.md +0 -22
  246. package/audit/traces/trace-read-query.md +0 -25
  247. package/audit/traces/trace-relationship-endpoints.md +0 -31
  248. package/audit/traces/trace-write-delete.md +0 -17
  249. package/audit/traces/trace-write-patch.md +0 -26
  250. package/audit/traces/trace-write-post.md +0 -22
  251. package/audit/traces/trace-write-put.md +0 -23
  252. package/docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md +0 -707
  253. package/docs/GUIDE/GUIDE_X_Multihome.md +0 -666
  254. package/plugins/core/lib/querying-writing/payload-validators.js +0 -1314
  255. package/plugins/core/rest-api-access.js +0 -459
  256. package/plugins/core/rest-api-multihome-plugin.js +0 -314
  257. package/tests/multihome.test.js +0 -553
  258. package/tests/multihome.test.js.bak +0 -550
  259. package/tests/ownership-optout.test.js +0 -76
  260. package/tests/rest-api-access.test.js +0 -206
@@ -1,707 +0,0 @@
1
- # Authentication and Permissions Guide
2
-
3
- This guide shows you how to add authentication to your REST API using the JWT Authentication Plugin. The plugin validates JWT tokens from any auth provider (Supabase, Auth0, Firebase, etc.) and enforces permissions on your resources.
4
-
5
- ## Table of Contents
6
- - [How It Works - The Architecture](#how-it-works---the-architecture)
7
- - [Quick Start - Get Running in 5 Minutes](#quick-start---get-running-in-5-minutes)
8
- - [Frontend: Getting Auth Tokens](#frontend-getting-auth-tokens)
9
- - [Supabase Setup](#supabase-setup)
10
- - [Auth0 Setup](#auth0-setup)
11
- - [Google OAuth Login](#google-oauth-login)
12
- - [Backend: Validating Tokens](#backend-validating-tokens)
13
- - [Using Built-in Checkers](#using-built-in-checkers)
14
- - [Progressive Examples](#progressive-examples)
15
- - [Advanced: Custom Authorization](#advanced-custom-authorization)
16
- - [Token Management](#token-management)
17
- - [Migration Guide](#migration-guide)
18
-
19
- ## How It Works - The Architecture
20
-
21
- The JWT plugin is a **backend/server-side** tool that validates tokens. Here's the complete flow:
22
-
23
- ```
24
- ┌─────────────┐ ┌─────────────┐ ┌──────────────┐
25
- │ Browser │ │ Supabase/ │ │ Your API │
26
- │ (Frontend) │ ───> │ Auth0 │ │ (Backend) │
27
- └─────────────┘ └─────────────┘ └──────────────┘
28
- │ │ │
29
- │ 1. Login with │ │
30
- │ email/Google │ │
31
- │ ─────────────────> │ │
32
- │ │ │
33
- │ 2. Get JWT token │ │
34
- │ <───────────────── │ │
35
- │ │ │
36
- │ 3. API request with Bearer token │
37
- │ ──────────────────────────────────────> │
38
- │ │
39
- │ 4. JWT Plugin validates
40
- │ token with Supabase
41
- │ │
42
- │ 5. Response (allowed or denied) │
43
- │ <────────────────────────────────────── │
44
- ```
45
-
46
- **Key Points:**
47
- - **Frontend**: Handles user login (email/password, Google, etc.) and gets JWT token
48
- - **Auth Provider**: Issues and manages JWT tokens (Supabase, Auth0, etc.)
49
- - **Backend (Your API)**: Uses JWT plugin to validate tokens and enforce permissions
50
- - **JWT Plugin**: Does NOT handle login/signup - only validates existing tokens
51
-
52
- ## Quick Start - Get Running in 5 Minutes
53
-
54
- ### Step 1: Install the Plugin
55
-
56
- ```bash
57
- npm install jose
58
- ```
59
-
60
- ### Step 2: Configure Your Backend API
61
-
62
- ```javascript
63
- import { Api } from 'hooked-api';
64
- import { RestApiPlugin, RestApiKnexPlugin } from 'json-rest-api';
65
- import { JwtAuthPlugin } from 'json-rest-api/plugins/core/jwt-auth-plugin.js';
66
-
67
- // Create your API
68
- const api = new Api();
69
- await api.use(RestApiPlugin);
70
- await api.use(RestApiKnexPlugin, { knex });
71
-
72
- // Add JWT authentication (example with Supabase)
73
- await api.use(JwtAuthPlugin, {
74
- jwksUrl: 'https://your-project.supabase.co/auth/v1/.well-known/jwks.json'
75
- });
76
-
77
- // Add a protected resource
78
- await api.addResource('posts', {
79
- schema: {
80
- id: { type: 'id' },
81
- title: { type: 'string', required: true },
82
- content: { type: 'text' },
83
- user_id: { type: 'string' } // Automatically set to current user
84
- },
85
-
86
- // Simple auth rules using built-in checkers
87
- auth: {
88
- query: ['public'], // Anyone can read posts
89
- post: ['authenticated'], // Must be logged in to create
90
- patch: ['owns'], // Can only edit your own posts
91
- delete: ['owns'] // Can only delete your own posts
92
- }
93
- });
94
- ```
95
-
96
- ### Step 3: Test It
97
-
98
- ```javascript
99
- // From your frontend (after user logs in)
100
- const response = await fetch('http://localhost:3000/api/posts', {
101
- method: 'POST',
102
- headers: {
103
- 'Authorization': `Bearer ${token}`, // Token from Supabase/Auth0
104
- 'Content-Type': 'application/json'
105
- },
106
- body: JSON.stringify({
107
- data: {
108
- type: 'posts',
109
- attributes: {
110
- title: 'My First Post',
111
- content: 'Hello world!'
112
- }
113
- }
114
- })
115
- });
116
- ```
117
-
118
- That's it! You now have authenticated API endpoints.
119
-
120
- ## Frontend: Getting Auth Tokens
121
-
122
- The JWT plugin validates tokens from any provider. Here's how to get tokens from popular services:
123
-
124
- ### Supabase Setup
125
-
126
- #### 1. Create a Supabase Project
127
- Go to [supabase.com](https://supabase.com) and create a new project. You'll get:
128
- - Project URL: `https://your-project.supabase.co`
129
- - Anon Key: `eyJhbGc...` (public key for frontend)
130
-
131
- #### 2. Frontend: Email/Password Login
132
-
133
- ```javascript
134
- // In your React/Vue/etc app
135
- import { createClient } from '@supabase/supabase-js'
136
-
137
- const supabase = createClient(
138
- 'https://your-project.supabase.co',
139
- 'your-anon-key'
140
- )
141
-
142
- // Sign up new user
143
- async function signUp(email, password) {
144
- const { data, error } = await supabase.auth.signUp({
145
- email,
146
- password
147
- })
148
- if (error) throw error
149
- return data.session.access_token // This is your JWT!
150
- }
151
-
152
- // Sign in existing user
153
- async function signIn(email, password) {
154
- const { data, error } = await supabase.auth.signInWithPassword({
155
- email,
156
- password
157
- })
158
- if (error) throw error
159
- return data.session.access_token // This is your JWT!
160
- }
161
-
162
- // Use the token for API calls
163
- async function createPost(token, title, content) {
164
- const response = await fetch('http://localhost:3000/api/posts', {
165
- method: 'POST',
166
- headers: {
167
- 'Authorization': `Bearer ${token}`,
168
- 'Content-Type': 'application/json'
169
- },
170
- body: JSON.stringify({
171
- data: {
172
- type: 'posts',
173
- attributes: { title, content }
174
- }
175
- })
176
- })
177
- return response.json()
178
- }
179
- ```
180
-
181
- #### 3. Backend Configuration
182
-
183
- ```javascript
184
- // Your API server
185
- await api.use(JwtAuthPlugin, {
186
- jwksUrl: 'https://your-project.supabase.co/auth/v1/.well-known/jwks.json'
187
- })
188
- ```
189
-
190
- ### Auth0 Setup
191
-
192
- #### 1. Create an Auth0 Application
193
- Go to [auth0.com](https://auth0.com) and create a new Single Page Application.
194
-
195
- #### 2. Frontend: Auth0 Login
196
-
197
- ```javascript
198
- // In your React/Vue/etc app
199
- import { createAuth0Client } from '@auth0/auth0-spa-js'
200
-
201
- const auth0 = await createAuth0Client({
202
- domain: 'your-domain.auth0.com',
203
- clientId: 'your-client-id',
204
- authorizationParams: {
205
- redirect_uri: window.location.origin,
206
- audience: 'https://your-api.com' // Your API identifier
207
- }
208
- })
209
-
210
- // Login
211
- async function login() {
212
- await auth0.loginWithRedirect()
213
- }
214
-
215
- // After redirect, get token
216
- async function getToken() {
217
- const token = await auth0.getAccessTokenSilently()
218
- return token // Your JWT!
219
- }
220
- ```
221
-
222
- #### 3. Backend Configuration
223
-
224
- ```javascript
225
- // Your API server
226
- await api.use(JwtAuthPlugin, {
227
- jwksUrl: 'https://your-domain.auth0.com/.well-known/jwks.json',
228
- audience: 'https://your-api.com',
229
- issuer: 'https://your-domain.auth0.com/'
230
- })
231
- ```
232
-
233
- ### Google OAuth Login
234
-
235
- Google login works through your auth provider (Supabase/Auth0), not directly. The user logs in with Google, but you still get a JWT from Supabase/Auth0.
236
-
237
- #### Supabase + Google
238
-
239
- ##### 1. Enable Google in Supabase Dashboard
240
- - Go to Authentication → Providers → Google
241
- - Add your Google Client ID and Secret (from Google Cloud Console)
242
-
243
- ##### 2. Frontend: Google Login Button
244
-
245
- ```javascript
246
- // React component example
247
- function GoogleLoginButton() {
248
- const handleGoogleLogin = async () => {
249
- const { data, error } = await supabase.auth.signInWithOAuth({
250
- provider: 'google',
251
- options: {
252
- redirectTo: 'http://localhost:3000/auth/callback'
253
- }
254
- })
255
- if (error) throw error
256
- // User will be redirected to Google, then back to your app
257
- }
258
-
259
- return (
260
- <button onClick={handleGoogleLogin}>
261
- Sign in with Google
262
- </button>
263
- )
264
- }
265
-
266
- // After redirect, get the session
267
- useEffect(() => {
268
- supabase.auth.getSession().then(({ data: { session } }) => {
269
- if (session) {
270
- const token = session.access_token // JWT from Supabase!
271
- // Use this token for API calls
272
- }
273
- })
274
- }, [])
275
- ```
276
-
277
- ##### 3. Backend: Same Configuration!
278
-
279
- ```javascript
280
- // No change needed - same as email/password
281
- await api.use(JwtAuthPlugin, {
282
- jwksUrl: 'https://your-project.supabase.co/auth/v1/.well-known/jwks.json'
283
- })
284
- ```
285
-
286
- The beauty is that your backend doesn't care HOW users logged in (email, Google, Facebook) - it just validates the JWT from Supabase.
287
-
288
- ## Backend: Validating Tokens
289
-
290
- ### Basic Configuration Options
291
-
292
- ```javascript
293
- await api.use(JwtAuthPlugin, {
294
- // Option 1: For external auth providers (Supabase, Auth0)
295
- jwksUrl: 'https://.../.well-known/jwks.json',
296
-
297
- // Option 2: For symmetric key (your own auth)
298
- secret: process.env.JWT_SECRET,
299
-
300
- // Option 3: For asymmetric key (your own auth)
301
- publicKey: '-----BEGIN PUBLIC KEY-----...',
302
-
303
- // Optional settings
304
- audience: 'your-api-audience', // Required for Auth0
305
- issuer: 'https://issuer.com', // Validate token issuer
306
- userIdField: 'sub', // Where to find user ID (default: 'sub')
307
- emailField: 'email', // Where to find email (default: 'email')
308
- ownershipField: 'user_id' // Field for ownership checks (default: 'user_id')
309
- })
310
- ```
311
-
312
- ### Understanding JWKS Configuration (Recommended)
313
-
314
- When you use `jwksUrl` (the recommended approach for Supabase/Auth0), here's what actually happens behind the scenes:
315
-
316
- #### What is JWKS?
317
- JWKS (JSON Web Key Set) is a URL that provides public keys for verifying JWT signatures. Instead of hardcoding keys, your API fetches them dynamically.
318
-
319
- #### The Automatic Process
320
- ```javascript
321
- await api.use(JwtAuthPlugin, {
322
- jwksUrl: 'https://your-project.supabase.co/auth/v1/.well-known/jwks.json'
323
- });
324
- ```
325
-
326
- This single line sets up:
327
-
328
- 1. **Automatic Key Fetching**: On first request, downloads Supabase's current public keys
329
- 2. **Smart Caching**: Caches keys for 10 minutes to avoid repeated downloads
330
- 3. **Key Rotation Handling**: When Supabase rotates keys, your API automatically gets the new ones
331
- 4. **Multiple Key Support**: Handles multiple keys during rotation periods
332
-
333
- #### What the JWKS URL Returns
334
- ```json
335
- {
336
- "keys": [
337
- {
338
- "kid": "abc123", // Key ID - matches 'kid' in JWT header
339
- "kty": "RSA", // Key type
340
- "use": "sig", // Usage: signature verification
341
- "n": "xGOr-H7A...", // RSA public key modulus
342
- "e": "AQAB" // RSA public key exponent
343
- },
344
- {
345
- "kid": "def456", // Another key (during rotation)
346
- "n": "yKPq-J8B...",
347
- "e": "AQAB"
348
- }
349
- ]
350
- }
351
- ```
352
-
353
- #### Performance Impact
354
- - **First request**: ~50-100ms (downloads keys from Supabase)
355
- - **Next 10 minutes**: ~1-2ms (uses cached keys)
356
- - **After cache expires**: Re-downloads automatically
357
- - **On verification failure**: Attempts to refresh keys
358
-
359
- #### Why This is Superior to Hardcoded Keys
360
- - **Zero Maintenance**: Keys update automatically when provider rotates them
361
- - **Always Secure**: You never handle or store private keys
362
- - **No Downtime**: Key rotation happens seamlessly
363
- - **No Code Changes**: Provider can change keys without you knowing
364
-
365
- ### What Happens During Validation
366
-
367
- 1. **Token Extraction**: Plugin extracts token from `Authorization: Bearer <token>` header
368
- 2. **Key Selection**: Matches token's `kid` (key ID) with cached JWKS keys
369
- 3. **Signature Verification**: Validates JWT signature using the matching public key
370
- 4. **Claims Validation**: Checks audience, issuer, expiration
371
- 5. **Context Population**: Sets `context.auth`:
372
- ```javascript
373
- context.auth = {
374
- userId: '123', // From 'sub' claim
375
- email: 'user@example.com', // From 'email' claim
376
- token: { /* full JWT */ }, // All token data
377
- tokenId: 'jti-123' // For revocation
378
- }
379
- ```
380
-
381
- ## Using Built-in Checkers
382
-
383
- The plugin provides only THREE built-in authorization checkers:
384
-
385
- ### 1. `public` - No Authentication Required
386
-
387
- ```javascript
388
- auth: {
389
- query: ['public'], // Anyone can read, even without token
390
- get: ['public']
391
- }
392
- ```
393
-
394
- ### 2. `authenticated` - Must Be Logged In
395
-
396
- ```javascript
397
- auth: {
398
- post: ['authenticated'], // Must have valid JWT token
399
- patch: ['authenticated']
400
- }
401
- ```
402
-
403
- ### 3. `owns` - Must Own the Resource
404
-
405
- ```javascript
406
- auth: {
407
- patch: ['owns'], // Can only edit records where user_id matches
408
- delete: ['owns'] // Can only delete your own records
409
- }
410
- ```
411
-
412
- The `owns` checker compares the `user_id` field in the database record with the current user's ID from the token.
413
-
414
- ## Progressive Examples
415
-
416
- Let's build up from simple to complex:
417
-
418
- ### Example 1: Public API (No Auth)
419
-
420
- ```javascript
421
- await api.addResource('articles', {
422
- schema: {
423
- title: { type: 'string', required: true },
424
- content: { type: 'text' }
425
- }
426
- // No auth property = completely open
427
- })
428
- ```
429
-
430
- ### Example 2: Read-Only Public, Write Requires Login
431
-
432
- ```javascript
433
- await api.addResource('articles', {
434
- schema: {
435
- title: { type: 'string', required: true },
436
- content: { type: 'text' },
437
- user_id: { type: 'string' }
438
- },
439
-
440
- auth: {
441
- query: ['public'], // Anyone can read
442
- get: ['public'],
443
- post: ['authenticated'], // Must be logged in to create
444
- patch: ['authenticated'],
445
- delete: ['authenticated']
446
- }
447
- })
448
- ```
449
-
450
- ### Example 3: Users Can Only Edit Their Own Content
451
-
452
- ```javascript
453
- await api.addResource('articles', {
454
- schema: {
455
- title: { type: 'string', required: true },
456
- content: { type: 'text' },
457
- user_id: { type: 'string' } // Auto-set on create
458
- },
459
-
460
- auth: {
461
- query: ['public'],
462
- get: ['public'],
463
- post: ['authenticated'],
464
- patch: ['owns'], // Only edit your own
465
- delete: ['owns'] // Only delete your own
466
- }
467
- })
468
-
469
- // Also enable auto-ownership
470
- await api.use(JwtAuthPlugin, {
471
- jwksUrl: '...',
472
- autoOwnership: {
473
- enabled: true, // Auto-set user_id on create
474
- filterByOwner: true // Users only see their own records in queries
475
- }
476
- })
477
- ```
478
-
479
- ### Example 4: Adding Admin Override (Custom Checker)
480
-
481
- ```javascript
482
- // Register a custom role checker
483
- api.helpers.auth.registerChecker('role', async (context, { param }) => {
484
- // In real app, query your users table
485
- const user = await api.resources.users.get(context.auth.userId)
486
- return user.role === param
487
- })
488
-
489
- await api.addResource('articles', {
490
- schema: { /* ... */ },
491
-
492
- auth: {
493
- query: ['public'],
494
- get: ['public'],
495
- post: ['authenticated'],
496
- patch: ['owns', 'role:admin'], // Owner OR admin
497
- delete: ['owns', 'role:admin'] // Owner OR admin
498
- }
499
- })
500
- ```
501
-
502
- ## Advanced: Custom Authorization
503
-
504
- The plugin is designed to be minimal. For complex authorization, you create custom checkers:
505
-
506
- ### Database-Driven Roles
507
-
508
- ```javascript
509
- // Store roles in your database
510
- await api.addResource('user_roles', {
511
- schema: {
512
- user_id: { type: 'string', required: true },
513
- role: { type: 'string', required: true }
514
- }
515
- })
516
-
517
- // Create role checker
518
- api.helpers.auth.registerChecker('role', async (context, { param }) => {
519
- const roles = await api.resources.user_roles.query({
520
- filter: { user_id: context.auth.userId }
521
- })
522
- return roles.some(r => r.role === param)
523
- })
524
-
525
- // Use it
526
- auth: {
527
- delete: ['role:admin'] // Must have admin role in database
528
- }
529
- ```
530
-
531
- ### Subscription Tiers
532
-
533
- ```javascript
534
- api.helpers.auth.registerChecker('plan', async (context, { param }) => {
535
- const user = await api.resources.users.get(context.auth.userId)
536
- const plans = ['free', 'basic', 'pro', 'enterprise']
537
- const requiredLevel = plans.indexOf(param)
538
- const userLevel = plans.indexOf(user.subscription_plan)
539
- return userLevel >= requiredLevel
540
- })
541
-
542
- // Premium features
543
- auth: {
544
- post: ['plan:pro'] // Need pro plan or higher
545
- }
546
- ```
547
-
548
- ### Team Membership
549
-
550
- ```javascript
551
- api.helpers.auth.registerChecker('team', async (context, { param }) => {
552
- const membership = await api.resources.team_members.query({
553
- filter: {
554
- user_id: context.auth.userId,
555
- team_id: param
556
- }
557
- })
558
- return membership.length > 0
559
- })
560
-
561
- // Team resources
562
- auth: {
563
- query: ['team:engineering'] // Must be on engineering team
564
- }
565
- ```
566
-
567
- ### Multi-Tenant Permissions
568
-
569
- ```javascript
570
- api.helpers.auth.registerChecker('tenant_role', async (context, { param }) => {
571
- const tenantId = context.request.headers['x-tenant-id']
572
- const membership = await api.resources.tenant_members.query({
573
- filter: {
574
- tenant_id: tenantId,
575
- user_id: context.auth.userId
576
- }
577
- })
578
- return membership[0]?.role === param
579
- })
580
-
581
- // Tenant-specific permissions
582
- auth: {
583
- delete: ['tenant_role:admin'] // Admin in current tenant
584
- }
585
- ```
586
-
587
- ## Token Management
588
-
589
- ### Accessing Token Data
590
-
591
- ```javascript
592
- // After validation, you have access to:
593
- context.auth = {
594
- userId: '123', // From 'sub' claim
595
- email: 'user@example.com', // From 'email' claim
596
- token: { // Full JWT payload
597
- sub: '123',
598
- email: 'user@example.com',
599
- iat: 1234567890,
600
- exp: 1234567890,
601
- // Any custom claims from your auth provider
602
- app_metadata: { /* ... */ },
603
- user_metadata: { /* ... */ }
604
- },
605
- tokenId: 'jti-123' // For revocation
606
- }
607
- ```
608
-
609
- ### Token Revocation / Logout
610
-
611
- ```javascript
612
- // Enable revocation
613
- await api.use(JwtAuthPlugin, {
614
- jwksUrl: '...',
615
- revocation: {
616
- enabled: true,
617
- storage: 'database' // Survives restarts
618
- },
619
- endpoints: {
620
- logout: '/auth/logout' // Add logout endpoint
621
- }
622
- })
623
-
624
- // Frontend logout
625
- async function logout(token) {
626
- // 1. Revoke token on your API
627
- await fetch('http://localhost:3000/auth/logout', {
628
- method: 'POST',
629
- headers: {
630
- 'Authorization': `Bearer ${token}`
631
- }
632
- })
633
-
634
- // 2. Clear Supabase session
635
- await supabase.auth.signOut()
636
- }
637
- ```
638
-
639
- ### Direct API Usage (No HTTP)
640
-
641
- For testing or server-side operations:
642
-
643
- ```javascript
644
- // Create auth context manually
645
- const authContext = {
646
- auth: {
647
- userId: 'user-123',
648
- email: 'user@example.com'
649
- }
650
- }
651
-
652
- // Use API methods directly
653
- const posts = await api.resources.posts.query({
654
- filter: { published: true }
655
- }, authContext) // Pass context as second parameter
656
-
657
- const newPost = await api.resources.posts.post({
658
- inputRecord: { /* ... */ }
659
- }, authContext)
660
- ```
661
-
662
- ## Common Issues and Solutions
663
-
664
- ### Issue: "Access denied" on all requests
665
- **Solution**: Check that your token is being sent correctly:
666
- ```javascript
667
- // Correct
668
- headers: { 'Authorization': `Bearer ${token}` }
669
-
670
- // Wrong
671
- headers: { 'Authorization': token } // Missing "Bearer "
672
- ```
673
-
674
- ### Issue: Token validation fails
675
- **Solution**: Ensure your JWKS URL matches your auth provider:
676
- ```javascript
677
- // Supabase: Check your project URL
678
- jwksUrl: 'https://YOUR-PROJECT.supabase.co/auth/v1/.well-known/jwks.json'
679
-
680
- // Auth0: Check your domain
681
- jwksUrl: 'https://YOUR-DOMAIN.auth0.com/.well-known/jwks.json'
682
- ```
683
-
684
- ### Issue: Google login token not working
685
- **Solution**: Remember that Google login still goes through your auth provider:
686
- - User logs in with Google → Supabase/Auth0 → Get Supabase/Auth0 JWT
687
- - Your API validates the Supabase/Auth0 JWT, not Google's token
688
-
689
- ### Issue: Ownership not working
690
- **Solution**: Ensure your schema has a `user_id` field:
691
- ```javascript
692
- schema: {
693
- user_id: { type: 'string' } // Required for 'owns' checker
694
- }
695
- ```
696
-
697
- ## Summary
698
-
699
- The JWT Authentication Plugin provides:
700
- 1. **Token validation** from any JWT provider (Supabase, Auth0, etc.)
701
- 2. **Three built-in checkers**: `public`, `authenticated`, `owns`
702
- 3. **Framework for custom authorization** via `checker:parameter` pattern
703
-
704
- Remember:
705
- - **Frontend**: Handles login (email, Google, etc.) and gets JWT
706
- - **Backend**: Validates JWT and enforces permissions
707
- - **Start simple**: Use built-in checkers, add custom logic as needed