Reference
API
Node.js

Node.js Client API

💡

Before going through this guide, make sure you follow the Oso Cloud Quickstart to get your Oso Cloud API Key properly set in your environment.

First, install the oso-cloud package on NPM:

npm install oso-cloud

Instantiating an Oso Cloud client

The Oso Cloud library works by providing an Oso class which you configure with your Oso Cloud URL and API key:

const { Oso } = require("oso-cloud");
 
const oso = new Oso("https://cloud.osohq.com", YOUR_API_KEY);
 
// Later, in an async function:
await oso.tell("has_role", user, role, resource);
 
// Wherever authorization needs to be performed
if (await oso.authorize(user, action, resource)) {
  // Action is allowed
}

Passing application entities into the client

Under the hood, Oso Cloud represents an entity in your application as a combination of a type and an ID, which together uniquely identify the entity. The Node client represents these entities as objects with both type and id properties. For example:

const alice = {type: "User", id: "alice"};
const anvilsRepository = {type: "Repostory", id: "anvils"};

You will pass objects like these into nearly every function call you make to the Node client.

Management API

Add fact: oso.tell(name, ...args)

Adds a fact named name with the provided arguments. Example:

await oso.tell(
  "has_role", {type: "User", id: "bob"}, "owner", {type: "Organization", id: "acme"}
);

Add many facts: oso.bulkTell([...[name, ...args]])

Adds many facts at once. Example:

await oso.bulkTell([
    ["has_role", {type: "User", id: "bob"}, "owner", {type: "Organization", id: "acme"}],
    ["has_role", {type: "User", id: "bob"}, "maintainer", {type: "Repository", id: "anvils"}],
]);

Delete fact: oso.delete(name, ...args)

Deletes a fact. Does not throw an error when the fact is not found. Example:

await oso.delete(
  "has_role", {type: "User", id: "bob"}, "maintainer", {type: "Repository", id: "anvils"}
);

Delete many facts: oso.bulkDelete([...[name, ...args]])

Deletes many facts at once. Does not throw an error when some of the facts are not found. Example:

await oso.bulkDelete([
    ["has_role", {type: "User", id: "bob"}, "owner", {type: "Organization", id: "acme"}],
    ["has_role", {type: "User", id: "bob"}, "maintainer", {type: "Repository", id: "anvils"}],
]);

Transactionally delete and add facts: oso.bulk(delete, tell)

Deletes and adds many facts in one atomic transaction. The deletions are performed before the adds. null can be used as a wildcard in facts in delete. Does not throw an error when the facts to delete are not found. Example:

await oso.bulk(
  [
    // All `has_role` facts linking User:bob and Repository:anvils will be deleted.
    ["has_role", {type: "User", id: "bob"}, null, {type: "Repository", id: "anvils"}]
  ],
  [
    // This fact will be added.
    ["has_role", {type: "User", id: "bob"}, "maintainer", {type: "Repository", id: "anvils"}]
  ]
);

List facts: oso.get(name, ...args)

Lists facts that are stored in Oso Cloud. Can be used to check the existence of a particular fact, or used to fetch all facts that have a particular argument:

// Get one fact:
await oso.get(
  "has_role", {type: "User", id: "bob"}, "admin", {type: "Repository", id: "anvils"}
);
// => [
//      [
//        "has_role",
//        {type: "User", id: "bob"},
//        {type: "String", id: "admin"},
//        {type: "Repository", id: "anvils"}
//      ]
//    ]
 
// List all role-related facts on the `anvils` repo
await oso.get("has_role", null, null, {type: "Repository", id: "anvils"});
// => [
//      [
//        "has_role",
//        {type: "User", id: "bob"},
//        {type: "String", id: "admin"},
//        {type: "Repository", id: "anvils"}
//      ],
//      //...other has_role facts
//    ]

Note that null behaves like a wildcard: passing null, null, anvils means "find all facts where anvils is the third argument, regardless of other arguments".

Query for anything: oso.query(predicate, ...args)

Query Oso Cloud for any predicate and any combination of concrete and wildcard arguments. Unlike oso.get, which only lists facts you've added, you can use oso.query to list derived information about any rule in your policy. Example:

// Query for all the repos `User:bob` can `read`
await oso.query("allow", {type: "User", id: "bob"}, "read", {type: "Repository"});
// => [
//      ["allow", {type: "User", id: "bob"}, "read", {type: "Repository", id: "acme"}],
//      ["allow", {type: "User", id: "bob"}, "read", {type: "Repository", id: "anvils"}],
//    ]
 
// Query for all the objects `User:admin` can `read`
await oso.query("allow", {type: "User", id: "admin"}, "read", null);
// => [
//      // `User:admin` can `read` anything
//      ["allow", {type: "User", id: "admin"}, "read", null],
//    ]

Note that null behaves like a wildcard. Passing "allow", null, null, anvils means "find anyone who can do anything to anvils". null also behaves like a wildcard in return values from oso.query. Additionally, if you want to query over all instances of a particular type, pass an object with a type property but no id property. For example, "allow", bob, "read", {type: "Repository"} will query for all the objects of type "Repository" that bob can read.

Learn more about how to query Oso Cloud.

Check API

Context facts

You may provide an array of context facts as an optional argument to any of the Check API methods. When Oso Cloud performs a check, it will consider these context facts in addition to any other facts you've previously added. Context facts are only used in the API call in which they're provided— they do not persist across requests. Learn more about context facts.

Check a permission: oso.authorize(actor, action, resource)

Determines whether or not an action is allowed, based on a combination of authorization data and policy logic. Example:

const alice = {type: "User", id: "alice"};
const anvilsRepository = {type: "Repository", id: "anvils"};
 
const authorized = await oso.authorize(alice, "read", anvilsRepository);
if (!authorized) {
  throw new Error("Action is not allowed");
}

You may provide an array of context facts as an optional fourth argument to this method. Example:

const issue = {type: "Issue", id: "anvils-1"};
 
const authorized = await oso.authorize(alice, "read", issue, [
  ["has_relation", issue, "parent", anvilsRepository] // a context fact
]);

Check authorized resources: oso.authorize_resources(actor, action, resources)

Returns a subset of resources on which an actor can perform a particular action. Ordering and duplicates, if any exist, are preserved. Example:

const alice = {type: "User", id: "alice"};
const anvilsRepository = {type: "Repository", id: "anvils"};
const acmeRepository = {type: "Repository", id: "acme"};
 
await oso.authorize_resources(alice, "read", [anvilsRepository, acmeRepository]);
// => [acmeRepository]

You may provide an array of context facts as an optional fourth argument to this method. Example:

const issueOnAcmeRepository = {type: "Issue", id: "acme-1"};
const issueOnAnvilsRepository = {type: "Issue", id: "anvils-2"};
 
await oso.authorize_resources(
  alice, "read", [issueOnAnvilsRepository, issueOnAcmeRepository],
  [ // context facts
    ["has_relation", issueOnAnvilsRepository, "parent", anvilsRepository]
    ["has_relation", issueOnAcmeRepository, "parent", acmeRepository]
  ]
);
// => [issueOnAcmeRepository]

List authorized resources: oso.list(actor, action, resourceType)

Fetches a list of resource IDs on which an actor can perform a particular action. Example:

const alice = {type: "User", id: "alice"};
 
const repositoryIds = await oso.list(alice, "read", "Repository");
// => ["acme"]

You may provide an array of context facts as an optional fourth argument to this method. Example:

const anvilsRepository = {type: "Repository", id: "anvils"};
const acmeRepository = {type: "Repository", id: "acme"};
 
const issueOnAcmeRepository = {type: "Issue", id: "acme-1"};
const issueOnAnvilsRepository = {type: "Issue", id: "anvils-2"};
 
const repositoryIds = await oso.list(
  alice, "read", "Issue",
  [ // context facts
    ["has_relation", issueOnAnvilsRepository, "parent", anvilsRepository]
    ["has_relation", issueOnAcmeRepository, "parent", acmeRepository]
  ]
);
// => ["acme-1"]

List authorized actions: oso.actions(actor, resource)

Fetches a list of actions which an actor can perform on a particular resource. Example:

const alice = {type: "User", id: "alice"};
const acmeRepository = {type: "Repository", id: "acme"};
 
await oso.actions(user, acmeRepository);
// => ["read"]

You may provide an array of context facts as an optional third argument to this method. Example:

const issueOnAcmeRepository = {type: "Issue", id: "acme-1"};
 
await oso.actions(
  alice, issueOnAcmeRepository,
  [
    ["has_relation", issueOnAcmeRepository, "parent", acmeRepository] // a context fact
  ]
);
// => ["read"]

Policy API

Update the active policy: oso.policy(policy)

Updates the policy in Oso Cloud. The string passed into this method should be written in Polar. Example:

await oso.policy("actor User {}");

Debugging

Request logs can be found in the Oso Cloud dashboard, but sometimes it's useful to print timings from the client so you can diagnose network problems. There are two environment variables you can set.

OSO_DEBUG_PRINT will log debug messages to stdout. OSO_DEBUG_FILE will append debug messages to the file you specify. eg OSO_DEBUG_FILE=oso_debug.txt

In both cases the messages will look like this. They show the route, the response code, the total request time and how much time was spent in Oso Cloud processing the request vs. network transport.

[oso] /facts 200 total: 4ms, server: 3ms network: 1ms
[oso] /policy 200 total: 9ms, server: 3ms network: 6ms
...

Talk to an Oso Engineer

Our team is happy to help you get started with Oso Cloud. If you'd like to learn more about using Oso Cloud in your app or have any questions about this guide, schedule a 1x1 with an Oso engineer.

Get started with Oso Cloud →

Last updated on September 27, 2022