cerbos-sdk-javascript icon indicating copy to clipboard operation
cerbos-sdk-javascript copied to clipboard

typesafe attributes in client calls

Open mdugue opened this issue 1 year ago • 2 comments

We recently started stepping into cerbos as an AuthZ solutions and are very happy so far with the onboarding experience and the clean yet powerfull modelling possibilities. Great job 💪

I was wondering if there is a way to provide type safety to the attributes that get provided to Principal and Resource in @cerbos/grpc or @cerbos/http? One example that came to my mind is the usage of the schemas.

If we provide a schema for a resource policy, this schema could also be used for generating stricter swagger / OpenAPI SDKs. Are there any approaches / ideas / hints for achieving this or similar?

Thanks a lot in advance!

mdugue avatar May 30 '23 09:05 mdugue

Hi @mdugue! Being able to generate "smarter" clients from your actual policies and schemas is definitely a use case we are interested in supporting, but isn't likely to be implemented imminently.

For now, the approach I would take would be to generate TypeScript types from your principal and resource schemas (using e.g. json-schema-to-typescript), and create a wrapper around the Cerbos GRPC/HTTP client that uses the generate types to define type-safe versions of the API methods you want to use.

haines avatar May 30 '23 09:05 haines

Hey @haines, thanks a lot for your reply. Meanwhile we have started exploring this field and created an interim solution. Instead of creating a wrapper we opted for generating TypeScript Declaration files, that allows using the original cerbos API.

It's basically a codegen CLI npx @estino/cerbos-ts-codegen that outputs sth like:

// cerbos.d.ts
import type {
	Principal,
	IsAllowedRequest,
} from "@cerbos/core/lib/types/external"

declare module "@cerbos/http" {
	interface HTTP {
		isAllowed(request: isAllowedParams): Promise<boolean>
	}
	export interface ContractPrincipal {
		department: "tech" | "legal"
		organisation: string
		[k: string]: unknown
	}
	export interface Contract {
		organisation: string
		[k: string]: unknown
	}

	type isAllowedParamsContract = {
		principal: Principal & {
			attributes?: ContractPrincipal
		}
		resource: { kind: "contract"; attributes: Contract }
		action: "view" | "edit"
	}
	type isAllowedParams = IsAllowedRequest & isAllowedParamsContract
}

You can find more details on https://github.com/optiscaners/cerbos-ts-codegen What do you think about this approach, is it worth digging deeper here or do you see any architectonical disadvantages?

mdugue avatar Jun 14 '23 06:06 mdugue