Ory offers excellent documentation but needs more support tools and in-depth examples of using its libraries in TypeScript and NestJS projects. I decided to contribute to it by creating a set of libraries to interact with APIs, which will (hopefully) make integration into your NestJS project easier. This post presents the ideal use case to divulge my routines for creating libraries in NestJS/Nx!

Feel free to skip the journey and go straight to the code!

Note: This article is part of a series on integrating Ory in production with NestJS. If you are interested in the other articles, you can find them here.

Intermediate

NestJS Workshop: Building for Production

Mastering NestJS: From Basics to Advanced Application Design

The plan

I will follow the recipe from a previous blog post to create the libraries.

This workspace is composed of three public libraries (at least for the moment) and one private library:

  • kratos-client-wrapper is a set of NestJS modules that wraps @ory/client and, more particularly, the Frontend and Identity APIs, which are part of Ory Kratos

  • keto-client-wrapper is also a set of NestJS modules that wraps @ory/client's Permission and Relationship APIs, which are part of Ory Keto

  • base-client-wrapper is an internal NestJS module that provides a base class that the Ory client wrappers can extend (similar to the BaseApi class in @ory/client)

  • keto-relations-parser is a node library that allows manipulating relations tuples using Ory Permission Language notation. This library is an improved version of this existing lib.

Edit: Recently, I created three extra packages, keto-cli and kratos-cli, to interact with the Ory APIs from the command line and hydra-client-wrapper. They are not covered in this article.


Now, let’s focus on the Ory API integration. Ory already provides an auto-generated client based on their Open API specifications, which uses axios under the hood to send HTTP requests. How can we make this an even better experience for NestJS users? I would say:

  • Easily importable and configurable module and services

  • Mockable dependencies for testing

  • Module and services with clear boundaries and simple interfaces

  • Error handling that is consistent across all modules and services

  • A way to automatically retry requests in case of rate-limiting

  • NestJS Guards to check user sessions and permissions on endpoints

Module organization

Ory services usually have a public API and an admin API.

Ory Kratos' public API allows users to register accounts, log in, and check user sessions. On the other hand, the admin API enables the management of users (called identities in Ory), authentication methods, and sessions. Ory Keto's public API allows end users to check permissions while the admin API manages relationships between entities (called namespaces in Ory).

A logical path to split our modules is to follow this organization; it would result in the following:

Ory service

Library

Public API

Admin API

Ory Kratos

kratos-client-wrapper

OryFrontendModule

OryIdentitiesModule

Ory Keto

keto-client-wrapper

OryPermissionsModule

OryRelationshipsModule

As said previously, I wanted to easily mock dependencies for testing; moreover, to improve error response handling for calls to Ory APIs, providing a custom axios instance can be helpful.

Luckily, the BaseApi class from @ory/client, which all APIs extend, allows you to pass a custom axios instance in the constructor. One solution I thought of is injecting the HttpService from @nestjs/axios into the NestJS services and passing the HttpService.axiosRef to the BaseApi constructor. This Axios instance could have custom interceptors to wrap errors in a standard class and allow adding a retry strategy in case of rate-limiting. Since all services implement that logic, I might as well build a generic class that all others extend; sometimes, strong coupling is for a good cause.

At the end of this article, the dependency graph will look like this:

nx-graph

Note: This graph was generated using Nx Graph CLI.

base-client-wrapper implementation

The starting point is the generic module, OryBaseModule, with its OryBaseService provider, and the first step will be to install dependencies:

npm i @ory/client @nestjs/axios

Create interfaces

To configure and implement the retry logic, I needed to extend the AxiosRequestConfig interface, which axios does not support. I created an OryAxiosRequestConfig interface that extends it and an OryBaseModuleOptions class that implements the IOryBaseModuleOptions interface. The latter is used to configure the OryBaseModule and is provided by the library consumer, in this case, the kratos-client-wrapper and keto-client-wrapper modules.

To make the axios types aware of the new options, I extended the AxiosRequestConfig interface using TypeScript's declaration merging feature.

OryError

To improve error handling on the consumer side. I created an OryError class that extends the JS Error to wrap AxiosError and make them more readable. It allows consumers to check if an error is an OryError and to access the AxiosError instance.

OryBaseService

OryBaseService is the base class for all services interacting with Ory APIs. It requires injecting the HttpService, configuring the axios interceptor during module initialization, and providing the axios reference via a getter.

OryBaseModule

The OryBaseModule is a basic DynamicModule that follows the convention of using forRoot and forRootAsync static methods to configure the module. It ensures that the HttpService provider is available for the OryBaseService in two ways:

  • With the forRoot method, the options are immediately available, so I imported the HttpModule and provided the OryBaseService with the HttpService instance

  • With the forRootAsync method, the options are provided asynchronously, so instead of importing the HttpModule, I created a custom HttpService provider that receives an Axios instance to its constructor, configured with the OryBaseModuleOptions.

Tip: Have a look at the NestJS documentation to know more about dynamic modules.

kratos-client-wrapper implementation

For this package, I created two modules (see Module organization), both of which consume the OryBaseModule.

Create OryIdentities and OryFrontend interfaces

Interfaces for these modules (OryIdentitiesModuleOptions and OryFrontendModuleOptions) extend OryBaseInterface and enable configuring the base path and the access token (for the Admin API via Ory Network) to access the Ory APIs.

Create OryIdentitiesModule and OryFrontendModule

There is nothing new here. The setup is very similar to the OryBaseModule setup. The modules import the OryBaseModule and provide the OryIdentitiesService and OryFrontendService.

Create OryIdentitiesService and OryFrontendService

Both services will depend on OryBaseService and their corresponding module options; they will extend IdentityApi and FrontendApi from @ory/client.

Create an authentication guard

To make it easier to protect endpoints with Ory Kratos, I created a Guard that depends on OryFrontendService to fetch and validate the session from the cookie or session token. The Guard is a mixin allowing different configurations for each HTTP endpoint or microservice listener.

Add unit tests

I wanted to be sure that the Ory clients use the custom Axios instance with the options provided by the OryBaseModule, so I created a rather minimalist unit tests suite for both services, which mock the OryBaseService and, by extension, the Axios instance.

keto-relations-parser implementation

As said earlier, the original goal was to improve the original library that allows parsing relation tuples with the following changes:

  1. Replace Antlr4 based parser with a simpler and more efficient Regex parser (it still needs to be tested in some edge cases)

  2. Create a fluent API to construct a relation tuple object

  3. Provide a set of helpers to convert RelationTuple to Ory Keto API parameters

After a quick discussion with the author of the original library, it seemed like the Antlr4 parser was a bit overkill for the use case and that a more straightforward Regex match could be enough. The first task was to write a Regex that matched the Ory Permission Language notation. Here are the test cases I used to validate the Regex:

And the Regex is brought to you by OpenAI:

/^([^:]+)(?::([^#]+))?(?:#([^@]+)(?:@([^:]+)(?::([^#]+))?(?:#([^()]+(?:\([^()]+\))?)?)?)?)?$/

Please let me know if there is a better way to write this Regex!


Another discussion with some co-workers made me realize the importance of providing a fluent API to construct the relation tuple. Using the notation supplied by the Ory Permission Language, it was hard to verbalize the relationships. This results in the following API:

Finally, I provided a set of helpers to convert the RelationTuple to Ory Keto API parameters; this API seems to be a bit inconsistent in the way it expects the parameters, so I tried hiding it by making the RelationTuple interface the single model to interact with.

You can find those helpers here.

keto-client-wrapper implementation

The approach is the same as for the kratos-client-wrapper implementation, except that the modules are named OryPermissionsModule and OryRelationshipsModule.

Create an authorization guard

For this Guard case, things are more complicated than the OryAuthenticationGuard as multiple layers of abstraction are required to empower developers to define permissions on their endpoints. Hopefully, the expressiveness of the Ory Permission Language combined with the fluent API from keto-relations-parser allows us to check permissions elegantly.

Note: Want to know more about this notation and how it works? Check out the Zanzibar Academy.

Create OryPermissionChecks decorator

The relation tuple(s) must be dynamically constructed with the API endpoint parameters and passed to OryPermissionService to check permissions on Ory Keto. Ideally, the Guard should be able to handle multiple relation tuples and multiple checks, so the Guard should receive an array of relation tuples factories. Also, it would be ideal to define those factories declaratively, so I decided to use a custom decorator, OryPermissionChecks. The factories are resolved using Reflection and the Execution Context.

Note: The permissions to checks can be composed of multiple relation tuples and multiple conditions. The conditions can be nested and combined with AND or OR logical operators. This allows for a high level of expressiveness when defining permissions.

Create OryAuthorizationGuard

The Guard is a mixin that depends on:

  • OryPermissionChecks, this decorator enables to store the relation tuple(s) that represents the permission(s) required to access the given endpoint

  • OryPermissionService, check the permission(s) via Ory Keto HTTP API.

The most important part of the Guard is the evaluateConditions method, which resolves the relation tuple(s) and checks the permissions via the Ory Keto API. The method is recursive and can handle nested conditions.

Note:

The unit tests for the Guard here, can give you a better understanding of how it works.

Hopefully, this implementation could be improved when Keto supports checking multiple relation tuples in a single request, which is apparently a work in progress.

End2end testing

To ensure that the libraries work well together and that the Guard is successfully activated to protect endpoints, I created the following setup:

  • Custom Docker images for Kratos and Keto

  • A mocked application that uses the libraries

  • Some end-to-end tests using NestJS testing utilities and Jest

The setup refers to the keto-client-wrapper, but the same applies to the kratos-client-wrapper.

Build custom docker images

I created a Dockerfile for each service. The Dockerfile for Keto is simple; it copies the configuration and the namespaces files, and sets the health check and the command to run the service.

Note The health check will help us ensure the container is ready to accept requests before running tests

Then, we can add an Nx target to make publishing to GHCR easy:

Finally, the image is published with nx run keto-client-wrapper:docker-push.

Mock an application

Here is a simple Controller that interacts with the library and provides an endpoint that returns a 200 if the user has the required permissions to access the resource or a 403 if not.

Write e2e tests

In this test, I create a relation (owners) between a subject User and a resource Toy in Ory Keto and check if the user can access the resource via the endpoint. The Keto policies are defined in namespaces.ts.

Note: Before starting the tests and when running locally, Ory services are started with docker compose with the wait option to ensure services are up and running and stopped after the tests are done. In CI, the services are already running, so the tests can be run directly.

Conclusion

I hope this article will offer a better understanding of Ory APIs and concrete interactions with NestJS. The libraries are available on GitHub.

In this series's next article, I will show you how to integrate the libraries into a NestJS application and configure Ory Kratos and Ory Keto in a production environment. Stay tuned! 🚀