@magek/mcp-server 0.0.8

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 (52) hide show
  1. package/README.md +42 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.js +25 -0
  4. package/dist/prompts/cqrs-flow.d.ts +15 -0
  5. package/dist/prompts/cqrs-flow.js +252 -0
  6. package/dist/prompts/troubleshooting.d.ts +15 -0
  7. package/dist/prompts/troubleshooting.js +239 -0
  8. package/dist/resources/cli-reference.d.ts +13 -0
  9. package/dist/resources/cli-reference.js +193 -0
  10. package/dist/resources/documentation.d.ts +18 -0
  11. package/dist/resources/documentation.js +62 -0
  12. package/dist/server.d.ts +5 -0
  13. package/dist/server.js +127 -0
  14. package/dist/utils/docs-loader.d.ts +19 -0
  15. package/dist/utils/docs-loader.js +111 -0
  16. package/docs/advanced/custom-templates.md +96 -0
  17. package/docs/advanced/data-migrations.md +181 -0
  18. package/docs/advanced/environment-configuration.md +74 -0
  19. package/docs/advanced/framework-packages.md +17 -0
  20. package/docs/advanced/health/sensor-health.md +389 -0
  21. package/docs/advanced/instrumentation.md +135 -0
  22. package/docs/advanced/register.md +119 -0
  23. package/docs/advanced/sensor.md +10 -0
  24. package/docs/advanced/testing.md +96 -0
  25. package/docs/advanced/touch-entities.md +45 -0
  26. package/docs/architecture/command.md +367 -0
  27. package/docs/architecture/entity.md +214 -0
  28. package/docs/architecture/event-driven.md +30 -0
  29. package/docs/architecture/event-handler.md +108 -0
  30. package/docs/architecture/event.md +145 -0
  31. package/docs/architecture/notifications.md +54 -0
  32. package/docs/architecture/queries.md +207 -0
  33. package/docs/architecture/read-model.md +507 -0
  34. package/docs/contributing.md +349 -0
  35. package/docs/docs-index.json +200 -0
  36. package/docs/features/error-handling.md +204 -0
  37. package/docs/features/event-stream.md +35 -0
  38. package/docs/features/logging.md +81 -0
  39. package/docs/features/schedule-actions.md +44 -0
  40. package/docs/getting-started/ai-coding-assistants.md +181 -0
  41. package/docs/getting-started/coding.md +543 -0
  42. package/docs/getting-started/installation.md +143 -0
  43. package/docs/graphql.md +1213 -0
  44. package/docs/index.md +62 -0
  45. package/docs/introduction.md +58 -0
  46. package/docs/magek-arch.png +0 -0
  47. package/docs/magek-cli.md +67 -0
  48. package/docs/magek-logo.svg +1 -0
  49. package/docs/security/authentication.md +189 -0
  50. package/docs/security/authorization.md +242 -0
  51. package/docs/security/security.md +16 -0
  52. package/package.json +46 -0
package/docs/index.md ADDED
@@ -0,0 +1,62 @@
1
+ ---
2
+ title: "Documentation"
3
+ children:
4
+ - ./introduction.md
5
+ - ./getting-started/installation.md
6
+ - ./getting-started/coding.md
7
+ - ./architecture/event-driven.md
8
+ - ./architecture/command.md
9
+ - ./architecture/event.md
10
+ - ./architecture/event-handler.md
11
+ - ./architecture/entity.md
12
+ - ./architecture/read-model.md
13
+ - ./architecture/notifications.md
14
+ - ./architecture/queries.md
15
+ - ./features/event-stream.md
16
+ - ./features/schedule-actions.md
17
+ - ./features/logging.md
18
+ - ./features/error-handling.md
19
+ - ./security/security.md
20
+ - ./security/authentication.md
21
+ - ./security/authorization.md
22
+ - ./magek-cli.md
23
+ - ./graphql.md
24
+ - ./advanced/custom-templates.md
25
+ - ./advanced/data-migrations.md
26
+ - ./advanced/environment-configuration.md
27
+ - ./advanced/framework-packages.md
28
+ - ./advanced/instrumentation.md
29
+ - ./advanced/register.md
30
+ - ./advanced/sensor.md
31
+ - ./advanced/testing.md
32
+ - ./advanced/touch-entities.md
33
+ - ./advanced/health/sensor-health.md
34
+ - ./contributing.md
35
+ ---
36
+
37
+ # Magek Framework Documentation
38
+
39
+ Welcome to the Magek documentation! Use the navigation on the left to browse guides and API reference.
40
+
41
+ ## Quick Links
42
+
43
+ - **[Introduction](./introduction.md)** - Learn what Magek is
44
+ - **[Installation](./getting-started/installation.md)** - Get started quickly
45
+ - **[Coding Tutorial](./getting-started/coding.md)** - Build your first app
46
+
47
+ ## Sections
48
+
49
+ ### Getting Started
50
+ Set up your environment and build your first Magek application.
51
+
52
+ ### Architecture
53
+ Understand Commands, Events, Entities, Read Models, and other core concepts.
54
+
55
+ ### Features
56
+ Learn about Event Streams, Scheduled Actions, Logging, and Error Handling.
57
+
58
+ ### Security
59
+ Implement Authentication and Authorization in your applications.
60
+
61
+ ### Advanced Topics
62
+ Dive deeper into Providers, Migrations, Testing, and more.
@@ -0,0 +1,58 @@
1
+ ---
2
+ title: "Introduction"
3
+ group: "Guides"
4
+ ---
5
+
6
+ # Introduction
7
+
8
+ > _Progress isn't made by early risers. It's made by lazy men trying to find easier ways to do something._ — [Robert A. Heinlein](https://en.wikipedia.org/wiki/Robert_A._Heinlein)
9
+
10
+ ## What is Magek?
11
+
12
+ Magek is the fastest way to create a scalable application. It is a new kind of framework to build scalable and reliable systems easier, reimagining the software development experience to maximize your team's speed and reduce friction on every level.
13
+
14
+ Magek follows an Event-Driven and a Domain-Driven Design approach in which you define your application in terms that are understandable by anyone in your company. From a bird’s eye view, your project is organized into:
15
+
16
+ - **Commands**: Define what a user can request from the system (i.e: Add an item to the cart)
17
+ - **Queries**: Define what a user can get from the system (i.e: Get cart items)
18
+ - **Events**: Simple records of facts (i.e: User X added item Y to the cart Z)
19
+ - **Entities**: Data about the things that the people in your company talk about (i.e: Orders, Customers, etc.)
20
+ - **Handlers**: Code that processes commands, reacts to events to trigger other actions, or update the entities after new events happen.
21
+
22
+ Events are the cornerstone of a Magek application, and that’s why we say that Magek is an event-driven framework. Events bring us many of the differentiating characteristics of Magek:
23
+
24
+ - **Real-time**: Events can trigger other actions when they’re created, and updates can be pushed to connected clients without extra requests.
25
+ - **High data resiliency**: Events are stored by default in an append-only database, so the data is never lost and it’s possible to recover any previous state of the system.
26
+ - **Scalable by nature**: Dependencies only happen at data level, so Magek apps can ingest more data without waiting for other operatons to complete. Low coupling also makes it easier to evolve the code without affecting other parts of the system.
27
+ - **Asynchronous**: Your users won't need to wait for your system to process the whole operation before continuing using it.
28
+
29
+ Before Magek, building an event-driven system with the mentioned characteristics required huge investments in hiring engineers with the needed expertise. Magek packs this expertise, acquired from real-case scenarios in high-scale companies, into a very simple tool that handles the hard parts for you!
30
+
31
+ We have redesigned the whole developer experience from scratch, taking advantage of the advanced TypeScript type system to go from project generation to a production-ready application which provides a real-time GraphQL API that can ingest thousands of concurrent users in a matter of minutes.
32
+
33
+ Magek's ultimate goal is making developer's lives easier, fulfilling the dream of writing code in a domain-driven way that eases communications for the whole team, without caring about how anything else is done at the infrastructure level!
34
+
35
+ ## Magek Principles
36
+
37
+ Magek enhances developers' productivity by focusing only on business logic. Write your code, provide your credentials and let Magek do the rest. Magek takes a holistic and highly-opinionated approach at many levels:
38
+
39
+ - **Focus on business value**: The only code that makes sense is the code that makes your application different from any other.
40
+ - **Convention over configuration**: All the supporting code and configuration that is similar in all applications should be out of programmers’ sight.
41
+ - **Simple deployment**: Run your application as a standalone server or export handlers for serverless deployment.
42
+ - **Scale smoothly**: The code you write to handle your first 100 users will still work to handle your first million. You won't need to rewrite your application when it succeeds.
43
+ - **Event-source and CQRS**: Our world is event-driven, businesses are event-driven, and modern software maps better to reality when it’s event-driven.
44
+ - **Principle of Abstraction**: Building an application is hard enough to have to deal with recurring low-level details like SQL, API design, or authentication mechanisms, so we tend to build more semantic abstractions on top of them.
45
+ - **Real-time first**: Client applications must be able to react to events happening in the backend and notice data changes.
46
+
47
+ ## Why use Magek
48
+
49
+ What does _Magek_ boost? Your team's productivity. Not just because it helps you work faster, but because it makes you worry about fewer buttons and switches. We aim to solve major productivity sinks for developers like designing the right infrastructure, writing APIs or dealing with ORMs.
50
+
51
+ Magek will fit like a glove in applications that are naturally event-driven like commerce applications (retail, e-commerce, omnichannel applications, warehouse management, etc.), business applications or communication systems, but it's a general-purpose framework that has several advantages over other solutions:
52
+
53
+ - **Faster time-to-market**: Magek can run your application from minute one, without complicated configurations. In addition to that, it features a set of code generators to help developers build the project scaffolding faster and focus on actual business code in a matter of seconds instead of dealing with complicated framework folklore.
54
+ - **Write less code**: Magek conventions and abstractions require less code to implement the same features. This not only speeds up development but combined with clear architecture guidelines also makes Magek projects easier to understand, iterate, and maintain.
55
+ - **Benefit from Typescript's advantages**: Typescript's type system provides an important security layer that helps developers make sure the code they write is the code they meant to write, making Magek apps more reliable and less error-prone.
56
+ - **All the advantages of Microservices, none of its cons**: Microservices are a great way to deal with code complexity, at least on paper. Services are isolated and can scale independently, and different teams can work independently, but that usually comes with a con: interfaces between services introduce huge challenges like delays, hard to solve cyclic dependencies, or deployment errors. In Magek, every handler function works independently, there are no direct dependencies between them, all communication happens asynchronously via events, and everything is compiled and type-checked atomically to avoid issues.
57
+ - **Event-sourcing by default**: Magek keeps all incremental data changes as events, indefinitely. This means that any previous state of the system can be recreated and replayed at any moment, enabling a whole world of possibilities for troubleshooting and auditing, syncing environments or performing tests and simulations.
58
+ - **Magek makes it easy to build enterprise-grade applications**: Implementing an event-sourcing system from scratch is a challenging exercise that usually requires highly specialized experts. There are some technical challenges like eventual consistency, message ordering, and snapshot building. Magek takes care of all of that and more for you, lowering the curve for people that are starting and making expert lives easier.
Binary file
@@ -0,0 +1,67 @@
1
+ ---
2
+ title: "Magek CLI"
3
+ group: "Guides"
4
+ ---
5
+
6
+ # Magek CLI
7
+
8
+ The Magek CLI is a command line interface that helps you develop your Magek applications. It is built with Node.js and published to NPM through the package `@magek/cli`. It's automatically included as a dependency in every Magek project - no global installation required!
9
+
10
+ ## No Installation Required
11
+
12
+ When you create a Magek project using `npm create magek@latest`, the CLI is automatically included as a dependency. Simply use `npx magek` to run any CLI command within your project directory.
13
+
14
+ ```bash
15
+ # Create a new project (CLI automatically included)
16
+ npm create magek@latest my-project
17
+
18
+ # Navigate to your project and start using the CLI
19
+ cd my-project
20
+ npx magek --help
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ Once you're in your Magek project directory, you can use the `npx magek` command to see the help message.
26
+
27
+ > **Tip:** You can also run `npx magek --help` to get the same output.
28
+
29
+ ## Command Overview
30
+
31
+ | Command | Description |
32
+ |---------|-------------|
33
+ | [`new:command`](/architecture/command#creating-a-command) | Creates a new command in the project |
34
+ | [`new:entity`](/architecture/entity#creating-an-entity) | Creates a new entity in the project |
35
+ | [`new:event`](/architecture/event#creating-an-event) | Creates a new event in the project |
36
+ | [`new:event-handler`](/architecture/event-handler#creating-an-event-handler) | Creates a new event handler in the project |
37
+ | [`new:read-model`](/architecture/read-model#creating-a-read-model) | Creates a new read model in the project |
38
+ | [`new:scheduled-command`](/features/schedule-actions#creating-a-scheduled-command) | Creates a new scheduled command in the project |
39
+
40
+ > **Tip:** To create a new Magek project, use the modern npm create pattern:
41
+ >
42
+ > ```bash
43
+ > npm create magek@latest my-project
44
+ > ```
45
+ >
46
+ > All CLI commands should be run with `npx magek` within your project directory.
47
+
48
+ ```bash
49
+ npm create magek@latest my-project
50
+ ```
51
+
52
+ ## Adapter CLI plugins
53
+
54
+ Magek CLI automatically discovers adapter packages named `@magek/adapter-*` that export a `magekCli` object. Any commands listed there are registered at startup and appear in `npx magek --help`.
55
+
56
+ ```ts
57
+ export const magekCli = {
58
+ commands: {
59
+ 'migrate:status': MigrateStatusCommand,
60
+ 'migrate:apply': MigrateApplyCommand,
61
+ },
62
+ }
63
+ ```
64
+
65
+ > **Note:** Plugin discovery executes code from installed adapter packages. Only install trusted adapters in your project.
66
+
67
+ See the [Getting Started guide](/getting-started/coding#1-create-the-project) for details.
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 2892 715" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><rect id="Artboard2" x="0" y="0" width="2891.53" height="714.98" style="fill:none;"/><clipPath id="_clip1"><rect x="0" y="0" width="2891.53" height="714.98"/></clipPath><g clip-path="url(#_clip1)"><g><path d="M1215.33,192.315c40.311,-0 72.818,12.388 97.518,37.166c24.701,24.777 37.051,59.366 37.051,103.767l0,193.264l-82.994,-0l-0,-181.965c-0,-25.769 -6.521,-45.491 -19.563,-59.168c-13.042,-13.678 -30.827,-20.516 -53.354,-20.516c-22.527,-0 -40.41,6.838 -53.65,20.516c-13.239,13.677 -19.859,33.399 -19.859,59.168l-0,181.965l-82.994,-0l-0,-181.965c-0,-25.769 -6.521,-45.491 -19.563,-59.168c-13.042,-13.678 -30.827,-20.516 -53.354,-20.516c-22.922,-0 -41.003,6.838 -54.243,20.516c-13.239,13.677 -19.859,33.399 -19.859,59.168l-0,181.965l-82.995,-0l0,-329.44l82.995,0l-0,39.842c10.67,-13.875 24.404,-24.777 41.201,-32.706c16.796,-7.929 35.272,-11.893 55.428,-11.893c25.689,-0 48.611,5.451 68.767,16.353c20.156,10.902 35.767,26.462 46.832,46.68c10.671,-19.029 26.183,-34.291 46.537,-45.788c20.353,-11.497 42.386,-17.245 66.099,-17.245Z" style="fill:#6545f5;fill-rule:nonzero;"/><path d="M1382.5,360.603c0,-33.301 6.62,-62.836 19.86,-88.604c13.239,-25.769 31.221,-45.591 53.946,-59.466c22.725,-13.875 48.117,-20.813 76.177,-20.813c24.503,0 45.943,4.956 64.321,14.867c18.377,9.91 33.099,22.398 44.165,37.463l-0,-46.978l83.587,0l-0,329.44l-83.587,-0l-0,-48.167c-10.671,15.461 -25.393,28.246 -44.165,38.355c-18.773,10.109 -40.411,15.164 -64.914,15.164c-27.665,-0 -52.859,-7.136 -75.584,-21.408c-22.725,-14.272 -40.707,-34.391 -53.946,-60.358c-13.24,-25.966 -19.86,-55.798 -19.86,-89.495Zm258.469,1.189c-0,-20.218 -3.952,-37.563 -11.857,-52.033c-7.904,-14.469 -18.575,-25.57 -32.012,-33.3c-13.437,-7.731 -27.862,-11.596 -43.275,-11.596c-15.414,-0 -29.641,3.766 -42.683,11.298c-13.042,7.533 -23.614,18.534 -31.716,33.004c-8.102,14.47 -12.153,31.616 -12.153,51.438c0,19.822 4.051,37.166 12.153,52.032c8.102,14.867 18.773,26.264 32.012,34.193c13.24,7.929 27.369,11.893 42.387,11.893c15.413,0 29.838,-3.865 43.275,-11.596c13.437,-7.73 24.108,-18.831 32.012,-33.301c7.905,-14.47 11.857,-31.814 11.857,-52.032Z" style="fill:#6545f5;fill-rule:nonzero;"/><path d="M1909.52,191.72c24.503,0 46.042,4.856 64.617,14.569c18.575,9.713 33.198,22.3 43.869,37.761l-0,-46.978l83.587,0l-0,331.819c-0,30.525 -6.126,57.78 -18.377,81.765c-12.252,23.984 -30.629,43.013 -55.132,57.087c-24.504,14.073 -54.144,21.11 -88.923,21.11c-46.635,0 -84.872,-10.902 -114.71,-32.706c-29.839,-21.804 -45.734,-48.537 -49.686,-86.198l82.401,-0c4.348,15.064 12.734,24.056 27.159,32.976c14.426,8.92 31.914,13.38 52.465,13.38c24.108,0 43.671,-7.235 58.689,-21.705c15.018,-14.47 22.527,-36.373 22.527,-65.709l-0,-51.141c-10.671,15.461 -25.393,28.345 -44.165,38.653c-18.773,10.307 -40.213,15.461 -64.321,15.461c-27.665,-0 -52.958,-7.136 -75.881,-21.408c-22.922,-14.272 -41.003,-34.391 -54.242,-60.358c-13.24,-25.966 -19.86,-55.798 -19.86,-89.495c0,-33.301 6.62,-62.836 19.86,-88.604c13.239,-25.769 31.221,-45.591 53.946,-59.466c22.725,-13.875 48.117,-20.813 76.177,-20.813Zm108.486,170.072c-0,-20.218 -3.952,-37.563 -11.857,-52.033c-7.904,-14.469 -18.575,-25.57 -32.012,-33.3c-13.437,-7.731 -27.862,-11.596 -43.276,-11.596c-15.413,-0 -29.64,3.766 -42.682,11.298c-13.042,7.533 -23.614,18.534 -31.716,33.004c-8.102,14.47 -12.153,31.616 -12.153,51.438c0,19.822 4.051,37.166 12.153,52.032c8.102,14.867 18.772,26.264 32.012,34.193c13.24,7.929 27.368,11.893 42.386,11.893c15.414,0 29.839,-3.865 43.276,-11.596c13.437,-7.73 24.108,-18.831 32.012,-33.301c7.905,-14.47 11.857,-31.814 11.857,-52.032Z" style="fill:#6545f5;fill-rule:nonzero;"/><path d="M2463.21,354.656c-0,11.893 -0.791,22.597 -2.372,32.112l-240.091,-0c1.976,23.786 10.276,42.418 24.899,55.897c14.622,13.479 32.605,20.219 53.946,20.219c30.826,-0 52.761,-13.281 65.803,-39.842l89.515,-0c-9.485,31.715 -27.665,57.78 -54.539,78.197c-26.874,20.417 -59.875,30.625 -99.001,30.625c-31.617,-0 -59.973,-7.037 -85.069,-21.111c-25.096,-14.073 -44.659,-33.994 -58.689,-59.763c-14.03,-25.768 -21.045,-55.501 -21.045,-89.198c0,-34.094 6.916,-64.025 20.749,-89.793c13.832,-25.769 33.198,-45.591 58.096,-59.466c24.898,-13.875 53.551,-20.813 85.958,-20.813c31.222,0 59.183,6.74 83.884,20.218c24.701,13.479 43.869,32.607 57.503,57.385c13.635,24.777 20.453,53.222 20.453,85.333Zm-85.959,-23.786c-0.395,-21.408 -8.102,-38.554 -23.12,-51.438c-15.018,-12.884 -33.395,-19.326 -55.132,-19.326c-20.551,-0 -37.841,6.244 -51.871,18.731c-14.03,12.488 -22.626,29.832 -25.788,52.033l155.911,-0Z" style="fill:#6545f5;fill-rule:nonzero;"/><path d="M2693.22,526.512l-111.45,-140.339l0,140.339l-82.994,-0l-0,-454.046l82.994,-0l0,264.35l110.264,-139.744l107.893,0l-144.648,165.315l145.834,164.125l-107.893,-0Z" style="fill:#6545f5;fill-rule:nonzero;"/></g><path d="M145.419,374.47l0,57.359c0,5.629 2.237,11.027 6.217,15.008c3.98,3.98 9.379,6.216 15.008,6.216l57.359,0l-57.359,0c-5.629,0 -11.028,2.236 -15.008,6.217c-3.98,3.98 -6.217,9.379 -6.217,15.008l0,57.359l0,-57.359c0,-5.629 -2.236,-11.028 -6.216,-15.008c-3.98,-3.981 -9.379,-6.217 -15.008,-6.217l-57.359,0l57.359,0c5.629,0 11.028,-2.236 15.008,-6.216c3.98,-3.981 6.216,-9.379 6.216,-15.008l0,-57.359Z" style="fill:url(#_Linear2);"/><path d="M496.134,490.373l-0,60.715c-0,5.958 2.367,11.673 6.58,15.886c4.213,4.213 9.928,6.58 15.886,6.58l60.715,0l-60.715,0c-5.958,0 -11.673,2.367 -15.886,6.581c-4.213,4.213 -6.58,9.927 -6.58,15.886l-0,60.715l-0,-60.715c-0,-5.959 -2.367,-11.673 -6.581,-15.886c-4.213,-4.214 -9.927,-6.581 -15.886,-6.581l-60.715,0l60.715,0c5.959,0 11.673,-2.367 15.886,-6.58c4.214,-4.213 6.581,-9.928 6.581,-15.886l-0,-60.715Z" style="fill:url(#_Linear3);"/><path d="M214.308,-20.291l0,110.579c0,10.852 4.311,21.259 11.984,28.933c7.674,7.673 18.082,11.984 28.934,11.984l110.578,0l-110.578,0c-10.852,0 -21.26,4.311 -28.934,11.985c-7.673,7.673 -11.984,18.081 -11.984,28.933l0,110.578l0,-110.578c0,-10.852 -4.311,-21.26 -11.984,-28.933c-7.674,-7.674 -18.082,-11.985 -28.934,-11.985l-110.578,0l110.578,0c10.852,0 21.26,-4.311 28.934,-11.984c7.673,-7.674 11.984,-18.081 11.984,-28.933l0,-110.579Z" style="fill:url(#_Linear4);"/><path d="M537.95,34.078l0,68.103c0,6.684 2.655,13.093 7.381,17.819c4.726,4.726 11.136,7.381 17.82,7.381l68.103,0l-68.103,0c-6.684,0 -13.094,2.656 -17.82,7.382c-4.726,4.726 -7.381,11.135 -7.381,17.819l0,68.103l0,-68.103c0,-6.684 -2.655,-13.093 -7.381,-17.819c-4.726,-4.726 -11.136,-7.382 -17.819,-7.382l-68.103,0l68.103,0c6.683,0 13.093,-2.655 17.819,-7.381c4.726,-4.726 7.381,-11.135 7.381,-17.819l0,-68.103Z" style="fill:url(#_Linear5);"/><path d="M605.044,177.724l-0,54.867c-0,5.385 2.139,10.549 5.946,14.356c3.808,3.808 8.972,5.947 14.356,5.947l54.868,-0l-54.868,-0c-5.384,-0 -10.548,2.139 -14.356,5.946c-3.807,3.808 -5.946,8.972 -5.946,14.357l-0,54.867l-0,-54.867c-0,-5.385 -2.139,-10.549 -5.947,-14.357c-3.807,-3.807 -8.972,-5.946 -14.356,-5.946l-54.867,-0l54.867,-0c5.384,-0 10.549,-2.139 14.356,-5.947c3.808,-3.807 5.947,-8.971 5.947,-14.356l-0,-54.867Z" style="fill:url(#_Linear6);"/><path d="M363.922,83.347c2.368,-4.668 7.158,-7.609 12.392,-7.609c5.234,-0 10.024,2.941 12.392,7.609l50.897,100.324c3.157,6.223 10.004,9.66 16.881,8.474l86.27,-14.883c4.45,-0.768 8.995,0.675 12.188,3.867c3.193,3.193 4.635,7.739 3.868,12.188l-14.883,86.271c-1.187,6.877 2.25,13.723 8.474,16.881l100.324,50.897c4.668,2.368 7.609,7.158 7.609,12.392c-0,5.234 -2.941,10.024 -7.609,12.392l-100.324,50.897c-6.224,3.157 -9.661,10.004 -8.474,16.881l14.883,86.27c0.767,4.45 -0.675,8.995 -3.868,12.188c-3.193,3.193 -7.738,4.636 -12.188,3.868l-86.27,-14.883c-6.877,-1.187 -13.724,2.25 -16.881,8.474l-50.897,100.324c-2.368,4.668 -7.158,7.609 -12.392,7.609c-5.234,-0 -10.024,-2.941 -12.392,-7.609l-50.897,-100.324c-3.158,-6.224 -10.005,-9.661 -16.882,-8.474l-86.27,14.883c-4.45,0.768 -8.995,-0.675 -12.188,-3.868c-3.193,-3.193 -4.635,-7.738 -3.867,-12.188l14.883,-86.27c1.186,-6.877 -2.251,-13.724 -8.474,-16.881l-100.325,-50.897c-4.667,-2.368 -7.608,-7.158 -7.608,-12.392c-0,-5.234 2.941,-10.024 7.608,-12.392l100.325,-50.897c6.223,-3.158 9.66,-10.004 8.474,-16.881l-14.883,-86.271c-0.768,-4.449 0.674,-8.995 3.867,-12.188c3.193,-3.192 7.738,-4.635 12.188,-3.867l86.27,14.883c6.877,1.186 13.724,-2.251 16.882,-8.474l50.897,-100.324Zm12.392,124.6c-83.787,0 -151.811,68.024 -151.811,151.811c0,83.786 68.024,151.81 151.811,151.81c83.786,0 151.81,-68.024 151.81,-151.81c0,-83.787 -68.024,-151.811 -151.81,-151.811Zm-0,17.745c73.993,-0 134.066,60.073 134.066,134.066c-0,73.993 -60.073,134.066 -134.066,134.066c-73.994,0 -134.067,-60.073 -134.067,-134.066c0,-73.993 60.073,-134.066 134.067,-134.066Zm-0,53.107c-44.683,0 -80.959,36.277 -80.959,80.959c0,44.682 36.276,80.958 80.959,80.958c44.682,0 80.958,-36.276 80.958,-80.958c0,-44.682 -36.276,-80.959 -80.958,-80.959Zm-0,40.48c22.341,-0 40.479,18.138 40.479,40.479c-0,22.341 -18.138,40.479 -40.479,40.479c-22.341,0 -40.48,-18.138 -40.48,-40.479c0,-22.341 18.139,-40.479 40.48,-40.479Z" style="fill:url(#_Linear7);"/></g><defs><linearGradient id="_Linear2" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(235.239,454.298,-454.298,235.239,235.912,135.951)"><stop offset="0" style="stop-color:#fbd248;stop-opacity:1"/><stop offset="0.5" style="stop-color:#e95a85;stop-opacity:1"/><stop offset="0.72" style="stop-color:#b752b0;stop-opacity:1"/><stop offset="1" style="stop-color:#6545f5;stop-opacity:1"/></linearGradient><linearGradient id="_Linear3" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(235.239,454.298,-454.298,235.239,260.091,123.162)"><stop offset="0" style="stop-color:#fbd248;stop-opacity:1"/><stop offset="0.5" style="stop-color:#e95a85;stop-opacity:1"/><stop offset="0.72" style="stop-color:#b752b0;stop-opacity:1"/><stop offset="1" style="stop-color:#6545f5;stop-opacity:1"/></linearGradient><linearGradient id="_Linear4" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(286.237,539.459,-539.459,286.237,208.073,131.525)"><stop offset="0" style="stop-color:#fbd248;stop-opacity:1"/><stop offset="0.5" style="stop-color:#e95a85;stop-opacity:1"/><stop offset="0.72" style="stop-color:#b752b0;stop-opacity:1"/><stop offset="1" style="stop-color:#6545f5;stop-opacity:1"/></linearGradient><linearGradient id="_Linear5" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(235.239,454.298,-454.298,235.239,294.943,119.974)"><stop offset="0" style="stop-color:#fbd248;stop-opacity:1"/><stop offset="0.5" style="stop-color:#e95a85;stop-opacity:1"/><stop offset="0.72" style="stop-color:#b752b0;stop-opacity:1"/><stop offset="1" style="stop-color:#6545f5;stop-opacity:1"/></linearGradient><linearGradient id="_Linear6" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(235.239,454.298,-454.298,235.239,261.364,141.107)"><stop offset="0" style="stop-color:#fbd248;stop-opacity:1"/><stop offset="0.5" style="stop-color:#e95a85;stop-opacity:1"/><stop offset="0.72" style="stop-color:#b752b0;stop-opacity:1"/><stop offset="1" style="stop-color:#6545f5;stop-opacity:1"/></linearGradient><linearGradient id="_Linear7" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(235.239,454.298,-454.298,235.239,261.364,141.107)"><stop offset="0" style="stop-color:#fbd248;stop-opacity:1"/><stop offset="0.5" style="stop-color:#e95a85;stop-opacity:1"/><stop offset="0.72" style="stop-color:#b752b0;stop-opacity:1"/><stop offset="1" style="stop-color:#6545f5;stop-opacity:1"/></linearGradient></defs></svg>
@@ -0,0 +1,189 @@
1
+ ---
2
+ title: "Authentication"
3
+ group: "Security"
4
+ ---
5
+
6
+ # Authentication
7
+
8
+ Magek uses the OAuth 2.0 protocol to authenticate users. That means that it uses tokens to identify users and authorize them. These tokens are called _access tokens_ and are issued by an _authentication provider_. The most common authentication provider is [Auth0](https://auth0.com/), but you can use any other provider that supports OAuth 2.0.
9
+
10
+ ## Configuring the authentication provider
11
+
12
+ The first step to configure authentication in Magek is to configure the authentication provider. The provider must support OAuth 2.0 and must be able to issue _access tokens_. In order to validate incoming tokens and make sure that user requests come from trustable origins, you need to provide one or more `TokenVerifier` instances at config time for each of your environments.
13
+
14
+ The `TokenVerifier` class is a simple interface that you can implement to define your own token verifiers. Magek provides a `JwksUriTokenVerifier` class that you can use to configure a JWT token verifier. The `JwksUriTokenVerifier` constructor accepts the following parameters:
15
+
16
+ - `issuer`: The issuer of the tokens. This is a mandatory parameter. This is commonly found in the token payload under the `iss` key.
17
+ - `jwksUri`: The URL of the JSON Web Key Set (JWKS) that contains the public keys used to verify the tokens. This is a mandatory parameter. You can find more information about JWKS [here](https://auth0.com/docs/jwks).
18
+ - `rolesClaim`: The name of the claim that contains the user roles. This is an optional parameter. If not provided, the `roles` claim will be used. This is commonly found in the token payload under the `roles` key.
19
+
20
+ Here is an example of how to configure a `JwksUriTokenVerifier`:
21
+
22
+ ```typescript title="src/config/config.ts"
23
+
24
+ Magek.configure('production', (config: MagekConfig): void => {
25
+ config.appName = 'app-name'
26
+ config.runtime = ServerRuntime
27
+ config.eventStoreAdapter = eventStore
28
+ config.tokenVerifiers = [
29
+ new JwksUriTokenVerifier(
30
+ 'https://my-auth0-tenant.auth0.com/', // Issuer
31
+ 'https://my-auth0-tenant.auth0.com/.well-known/jwks.json', // JWKS URL
32
+ 'role' // Roles claim
33
+ ),
34
+ ])
35
+ })
36
+ ```
37
+
38
+ One common way to validate JWT tokens is by using a issuer-provided well-known URI on which you can find their [JSON Web Key](https://datatracker.ietf.org/doc/html/rfc7517) sets (JWKS). If you use this method, you only need to provide the issuer's name, the JWKS URI and, if you're using role-based authentication, an optional `rolesClaim` option that sets the claim from which Magek will read the role names.
39
+
40
+ ### JWKS URI glossary
41
+
42
+ Here you can find a list of the most common authentication providers and their corresponding issuer, JWKS URI and roles claim:
43
+
44
+ The issuer and JWKS URI may change depending on the region you're using. Please check the provider's documentation to find the correct values for your use case.
45
+
46
+ The following list is not exhaustive and the information may be deprecated. If you want to add a new provider, or update an existing one, please open a PR to have this content up to date.
47
+
48
+ | Provider | Issuer | JWKS URI |
49
+ | ----------- | ----------------------------------------------- | ----------------------------------------------------------- |
50
+ | Auth0 | `https://<your-tenant>.auth0.com/` | `https://<your-tenant>.auth0.com/.well-known/jwks.json` |
51
+ | Okta | `https://<your-tenant>.okta.com/oauth2/default` | `https://<your-tenant>.okta.com/oauth2/default/v1/keys` |
52
+ | Google | `https://accounts.google.com` | `https://www.googleapis.com/oauth2/v3/certs` |
53
+ | Firebase | `https://accounts.google.com` | `https://www.googleapis.com/oauth2/v3/certs` |
54
+
55
+ ## Public key based authentication
56
+
57
+ The `PublicKeyTokenVerifier` class uses the public key of the issuer to verify the token signature. This means that the issuer must provide a JWKS URI that can be used to verify the token signature. This is the most common way to verify tokens, but it's not the only one. If you want to use a different method, you can implement your own `TokenVerifier` class.
58
+
59
+ This is useful when the token issuer doesn't provide a JWKS URI, when you're implementing your own authentication mechanism or you're issuing self-signed tokens.
60
+
61
+ ```typescript title="src/config/config.ts"
62
+
63
+ function publicKeyResolver(): Promise<string> {
64
+ // Your implementation here
65
+ }
66
+
67
+ Magek.configure('production', (config: MagekConfig): void => {
68
+ config.appName = 'app-name'
69
+ config.runtime = ServerRuntime
70
+ config.eventStoreAdapter = eventStore
71
+ config.tokenVerifiers = [
72
+ new PublicKeyTokenVerifier(
73
+ 'issuer-name', // Issuer name
74
+ publicKeyResolver(), // Promise that resolves to the public key string
75
+ 'custom:roles' // Name of the claim to read the roles from (if you're using role-based authorization)
76
+ ),
77
+ ]
78
+ })
79
+ ```
80
+
81
+ Notice that the `publicKeyResolver` is a promise that resolves to a string, so it can be used to load the public key from a remote location too (i.e. get it from your KMS).
82
+
83
+ If you need to handle private keys in production, consider using a KMS [(Key Management System)](https://en.wikipedia.org/wiki/Key_management#Key_storage). These systems often provide API endpoints that let you encrypt/sign your JWT tokens without exposing the private keys. The public keys can be set in a `PublicKeyTokenVerifier` to automate verification.
84
+
85
+ ## Custom authentication
86
+
87
+ If you want to implement your own authentication mechanism, you can implement your own `TokenVerifier` class. This class must implement the following interface:
88
+
89
+ ```typescript
90
+ interface TokenVerifier {
91
+ /**
92
+ * Verify asd deserialize a stringified token with this token verifier.
93
+ * @param token The token to verify
94
+ */
95
+ verify(token: string): Promise<DecodedToken>
96
+
97
+ /**
98
+ * Build a valid `UserEnvelope` from a decoded token.
99
+ * @param decodedToken The decoded token
100
+ */
101
+ toUserEnvelope(decodedToken: DecodedToken): UserEnvelope
102
+ }
103
+ ```
104
+
105
+ Here is an example of how to implement a custom `TokenVerifier`:
106
+
107
+ ```typescript title="src/config/config.ts"
108
+
109
+ class CustomTokenVerifier implements TokenVerifier {
110
+ public async verify(token: string): Promise<DecodedToken> {
111
+ // Your custom token verification logic here
112
+ }
113
+
114
+ public toUserEnvelope(decodedToken: DecodedToken): UserEnvelope {
115
+ // Your custom logic to build a UserEnvelope from a decoded token here
116
+ }
117
+ }
118
+
119
+ Magek.configure('production', (config: MagekConfig): void => {
120
+ config.appName = 'app-name'
121
+ config.runtime = ServerRuntime
122
+ config.eventStoreAdapter = eventStore
123
+ config.tokenVerifiers = [new CustomTokenVerifier()]
124
+ })
125
+ ```
126
+
127
+ Some use cases for this could be to check that the token was generated specifically for your service by inspecting the `aud` claim, or check that the token has not been blacklisted or invalidated by your business logic (i.e. a user logs out before the token's expiration date and is included in an invalidated tokens list to make sure that an attacker that finds the token later can't use it to impersonate the legitimate owner).
128
+
129
+ ### Extend existing token verifiers
130
+
131
+ If you only need to perform extra validations on top of one of the existing `TokenVerifier`s, you can extend one of the default implementations:
132
+
133
+ ```typescript
134
+ export class CustomValidator extends PrivateKeyValidator {
135
+ public async verify(token: string): Promise<UserEnvelope> {
136
+ // Call to the PrivateKeyValidator verify method to check the signature
137
+ const userEnvelope = await super.verify(token)
138
+
139
+ // Do my extra validations here. Throwing an error will reject the token
140
+ await myExtraValidations(userEnvelope.claims, token)
141
+
142
+ return userEnvelope
143
+ }
144
+ }
145
+ ```
146
+
147
+ ### Advanced authentication
148
+
149
+ If you need to do more advanced checks, you can implement the whole verification algorithm yourself. For example, if you're using non-standard or legacy tokens. Magek exposes for convenience many of the utility functions that it uses in the default `TokenVerifier` implementations:
150
+
151
+ | Function | Description |
152
+ | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
153
+ | `getJwksClient` | Initializes a jwksRSA client that can be used to get the public key of a JWKS URI using the `getKeyWithClient` function. |
154
+ | `getKeyWithClient` | Initializes a function that can be used to get the public key from a JWKS URI with the signature required by the `verifyJWT` function. You can create a client using the `getJwksClient` function. |
155
+ | `verifyJWT` | Verifies a JWT token using a key or key resolver function and returns a Magek UserEnvelope. |
156
+
157
+ ```typescript
158
+ /**
159
+ * Initializes a jwksRSA client that can be used to get the public key of a JWKS URI using the
160
+ * `getKeyWithClient` function.
161
+ */
162
+ export function getJwksClient(jwksUri: string) {
163
+ ...
164
+ }
165
+
166
+ /**
167
+ * Initializes a function that can be used to get the public key from a JWKS URI with the signature
168
+ * required by the `verifyJWT` function. You can create a client using the `getJwksClient` function.
169
+ */
170
+ export function getKeyWithClient(
171
+ client: jwksRSA.JwksClient,
172
+ header: jwt.JwtHeader,
173
+ callback: jwt.SigningKeyCallback
174
+ ): void {
175
+ ...
176
+ }
177
+
178
+ /**
179
+ * Verifies a JWT token using a key or key resolver function and returns a Magek UserEnvelope.
180
+ */
181
+ export async function verifyJWT(
182
+ token: string,
183
+ issuer: string,
184
+ key: jwt.Secret | jwt.GetPublicKeyOrSecret,
185
+ rolesClaim?: string
186
+ ): Promise<UserEnvelope> {
187
+ ...
188
+ }
189
+ ```
@@ -0,0 +1,242 @@
1
+ ---
2
+ title: "Authorization"
3
+ group: "Security"
4
+ ---
5
+
6
+ # Authorization
7
+
8
+ Magek uses a whitelisting approach to authorize users to perform commands and read models. This means that you must explicitly specify which users are allowed to perform each action. In order to do that you must configure the `authorize` policy parameter on every Command or Read Model. This parameter accepts one of the following options:
9
+
10
+ - `'all'`: The command or read-model is explicitly public, any user can access it.
11
+ - `[Role1, Role2, ...]`: An array of authorized [Roles](#defining-roles), this means that only those authenticated users that have any of the roles listed there are authorized to execute the command.
12
+ - An authorizer function that matches the `CommandAuthorizer` interface for commands or the `ReadModelAuthorizer` interface for read models.
13
+
14
+ ## Making commands and read models public
15
+
16
+ Setting the option `authorize: 'all'` in a command or read model will make it publicly accessible to anyone that has access to the GraphQL endpoint. For example, the following command can be executed by anyone, even if they don't provide a valid JWT token:
17
+
18
+ ```typescript title="src/commands/create-comment.ts"
19
+ @Command({
20
+ authorize: 'all',
21
+ })
22
+ export class CreateComment {
23
+ ...
24
+ }
25
+ ```
26
+
27
+ > **Danger:** Think twice if you really need fully open GraphQL endpoints in your application, this might be useful during development, but we recommend to **avoid exposing your endpoints in this way in production**. Even for public APIs, it might be useful to issue API keys to avoid abuse. Magek is designed to scale to any given demand, but scaling also increases infrastructure costs! (See [Denial of wallet attacks](https://www.sciencedirect.com/science/article/pii/S221421262100079X))
28
+
29
+ ## Simple Role-based authorization
30
+
31
+ Magek provides a simple role-based authentication mechanism that will work in many standard scenarios. It is based on the concept of roles, which are just a set of permissions. For example, a `User` role might have the permission to `create` and `read` comments, while an `Admin` role might have the permission to `create`, `read`, and `delete` comments. You can define as many roles as you want, and then assign them to users.
32
+
33
+ ### Defining @Roles
34
+
35
+ As many other Magek artifacts, Magek Roles are defined as simple decorated classes. We recommend them to be defined in the `src/config/roles.ts` file, but it is not limited to that file. To define a role, you only need to decorate an empty class with the `@Role` decorator as follows:
36
+
37
+ ```typescript title="src/config/roles.ts"
38
+ @Role()
39
+ export class User {}
40
+
41
+ @Role()
42
+ export class Admin {}
43
+ ```
44
+
45
+ ### Protecting commands and read models with roles
46
+
47
+ Once you have defined your roles, you can use them to protect your commands and read models. For example, the following command can only be executed by users that have the role `Admin`:
48
+
49
+ ```typescript title="src/commands/create-comment.ts"
50
+ @Command({
51
+ authorize: [Admin],
52
+ })
53
+ export class CreateComment {
54
+ ...
55
+ }
56
+ ```
57
+
58
+ This command will not be available to users with the role `User`.
59
+
60
+ ### Associating users with roles
61
+
62
+ Magek will read the roles from the JWT token that you provide in the request. The token must include a claim with the key you specidied in the `rolesClaim` field. Magek will read such field and compare it with the declared ones in the `authorize` field of the protected command or read model.
63
+
64
+ For example, given the following setup:
65
+
66
+ ## Magek Config
67
+
68
+ ```typescript title="src/config/config.ts"
69
+
70
+ Magek.configure('production', (config: MagekConfig): void => {
71
+ config.appName = 'my-store'
72
+ config.runtime = ServerRuntime
73
+ config.eventStoreAdapter = eventStore
74
+ config.tokenVerifiers = [
75
+ new JwksUriTokenVerifier(
76
+ 'https://my-auth0-tenant.auth0.com/', // Issuer
77
+ 'https://my-auth0-tenant.auth0.com/.well-known/jwks.json', // JWKS URL
78
+ // highlight-next-line
79
+ 'firebase:groups' // <- roles are read from 'firebase:groups' claim from the token
80
+ ),
81
+ ]
82
+ })
83
+ ```
84
+
85
+ ## Decoded Token
86
+
87
+ ```json
88
+ {
89
+ // highlight-next-line
90
+ "firebase:groups": "User", // <- roles are read from 'firebase:groups' claim
91
+ "iss": "https://securetoken.google.com/demoapp",
92
+ "aud": "demoapp",
93
+ "auth_time": 1604676721,
94
+ "user_id": "xJY5Y6fTbVggNtDjaNh7cNSBd7q1",
95
+ "sub": "xJY5Y6fTbVggNtDjaNh7cNSBd7q1",
96
+ "iat": 1604676721,
97
+ "exp": 1604680321,
98
+ "phone_number": "+999999999",
99
+ "firebase": {}
100
+ }
101
+ ```
102
+
103
+ ## Magek Command
104
+
105
+ ```typescript title="src/commands/create-comment.ts"
106
+ @Command({
107
+ authorize: [Admin],
108
+ })
109
+ export class CreateComment {
110
+ ...
111
+ }
112
+ ```
113
+
114
+ ## Magek Roles
115
+
116
+ ```typescript title="src/config/roles.ts"
117
+ @Role()
118
+ export class User {}
119
+
120
+ @Role()
121
+ export class Admin {}
122
+ ```
123
+
124
+ Magek will check that the token contains the `firebase:groups` claim and that it contains the `Admin` role.
125
+ Also, if the token doesn't contain the `Admin` role, the command will not be executed. As you can see, the decoded token
126
+ has `User` as value of the `firebase:groups` claim, so the command will not be executed.
127
+
128
+ ## Custom authorization functions
129
+
130
+ Magek also allows you to implement your own authorization functions, in case the role-based authorization model doesn't work for your application. In order to
131
+ apply your own authorization functions, you need to provide them in the `authorize` field of the command or read model. As authorization functions are regular
132
+ JavaScript functions, you can easily reuse them in your project or even in other Magek projects as a library.
133
+
134
+ ### Command Authorizers
135
+
136
+ As mentioned, the `authorize` parameter of the `@Command` can receive a function. However, this function must match the `CommandAuthorizer` type. This function will receive two parameters and return a `Promise` that will resolve if the user is authorized to execute the command or reject if not:
137
+
138
+ ```typescript
139
+ export type CommandAuthorizer = (currentUser?: UserEnvelope, input?: CommandInput) => Promise<void>
140
+ ```
141
+
142
+ | Parameter | Type | Description |
143
+ | ----------- | -------------- | ----------------------------------------- |
144
+ | currentUser | `UserEnvelope` | User data decoded from the provided token |
145
+ | input | `CommandInput` | The input of the command |
146
+
147
+ For instance, if you want to restrict a command to users that have a permission named `Permission-To-Rock` in the `permissions` claim you can do this:
148
+
149
+ ```typescript
150
+
151
+ const CustomCommandAuthorizer: CommandAuthorizer = async (currentUser) => {
152
+ if (!currentUser.claims['permissions'].includes('Permission-To-Rock')) {
153
+ throw new Error(`User ${currentUser.username} should not be rocking!`) // <- This will reject the access to the command
154
+ }
155
+ }
156
+
157
+ @Command({
158
+ authorize: CustomCommandAuthorizer,
159
+ })
160
+ export class PerformIncredibleGuitarSolo {
161
+ ...
162
+ }
163
+ ```
164
+
165
+ ### Read Model Authorizers
166
+
167
+ As with commands, the `authorize` parameter of the `@ReadModel` decorator can also receive a function. However, this function must match the `ReadModelAuthorizer` type. This function will receive two parameters and return a `Promise` that will resolve if the user is authorized to execute the command or reject if not:
168
+
169
+ ```typescript
170
+ export type ReadModelAuthorizer<TReadModel extends ReadModelInterface> = (
171
+ currentUser?: UserEnvelope,
172
+ readModelRequestEnvelope?: ReadModelRequestEnvelope<TReadModel>
173
+ ) => Promise<void>
174
+ ```
175
+
176
+ | Parameter | Type | Description |
177
+ | ----------- | -------------- | ----------------------------------------- |
178
+ | currentUser | `UserEnvelope` | User data decoded from the provided token |
179
+ | input | `CommandInput` | The input of the command |
180
+
181
+ For instance, you may want to restrict access to a specific resource only to people that has been granted read permission:
182
+
183
+ ```typescript
184
+ const CustomReadModelAuthorizer: ReadModelAuthorizer = async (currentUser, readModelRequestEnvelope) => {
185
+ const userPermissions = Magek.entity(UserPermissions, currentUser.username)
186
+ if (!userPermissions || !userPermissions.accessTo[readModelRequestEnvelope.className].includes(readModelRequestEnvelope.key.id)) {
187
+ throw new Error(`User ${currentUser.username} should not be looking here`)
188
+ }
189
+ }
190
+
191
+ @ReadModel({
192
+ authorize: CustomReadModelAuthorizer
193
+ })
194
+ ```
195
+
196
+ ### Event Stream Authorizers
197
+
198
+ You can restrict the access to the [Event Stream](/features/event-stream) of an `Entity` by providing an `authorizeReadEvents` function in the `@Entity` decorator. This function is called every time an event stream is requested. The function must match the `EventStreamAuthorizer` type receives the current user and the event search request as parameters. The function must return a `Promise<void>`. If the promise is rejected, the request will be denied. If the promise is resolved successfully, the request will be allowed.
199
+
200
+ ```typescript
201
+ export type EventStreamAuthorizer = (
202
+ currentUser?: UserEnvelope,
203
+ eventSearchRequest?: EventSearchRequest
204
+ ) => Promise<void>
205
+ ```
206
+
207
+ For instance, you can restrict access to entities that the current user own.
208
+
209
+ ```typescript
210
+ const CustomEventAuthorizer: EventStreamAuthorizer = async (currentUser, eventSearchRequest) => {
211
+ const { entityID } = eventSearchRequest.parameters
212
+ if (!entityID) {
213
+ throw new Error(`${currentUser.username} cannot list carts`)
214
+ }
215
+ const cart = Magek.entity(Cart, entityID)
216
+ if (cart.ownerUserName !== currentUser.userName) {
217
+ throw new Error(`${currentUser.username} cannot see events in cart ${entityID}`)
218
+ }
219
+ }
220
+
221
+ @Entity({
222
+ authorizeReadEvents: CustomEventAuthorizer
223
+ })
224
+ export class Cart {
225
+ @field(type => UUID)
226
+ readonly id!: UUID
227
+
228
+ @field()
229
+ readonly ownerUserName!: string
230
+
231
+ @field()
232
+ readonly cartItems!: Array<CartItem>
233
+
234
+ @field()
235
+ public shippingAddress?: Address
236
+
237
+ @field()
238
+ public checks: number = 0
239
+
240
+ // ... reducers
241
+ }
242
+ ```