Oso is a batteries-included library for building authorization in Node.
- It's embedded in your application—no extra processes to run.
- Oso uses your data models. Pass JavaScript objects to Oso, and your authorization policy can refer to properties on those objects. You can even call methods on JavaScript classes from Oso.
- Oso comes with built-in primitives for common authorization patterns like role-based access control (RBAC) and relationship-based access control (ReBAC).
- Oso provides APIs for yes/no authorization decisions, as well as authorization over list endpoints.
A single-line JavaScript authorization API.
The JavaScript authorization API is:
await oso.authorize(actor, action, resource);
Here's an example in an Express controller. This assumes you have an authentication scheme that can provide a getCurrentUser()
call:
await oso.authorize(User.getCurrentUser(), "read", Repository.getByName(name));
Declarative authorization models.
To model authorization with Oso, you write a policy in Polar, Oso’s declarative policy language.
For instance:
- Individual permissions take the form:
allow(actor, _action, _resource) if
actor.email = "alice@example.com";
- RBAC means grouping permissions into roles (e.g., User, Moderator, and Admin roles—or whichever roles your app needs), and assigning those roles to users. Oso supports role-based access control (RBAC) natively.
allow(actor, action, resource) if has_permission(actor, action, resource);
has_role(actor: User, role_name: String, post: Post) if
role in actor.roles and
role_name = role.name and
post = role.post;
actor User {}
resource Post {
permissions = ["read", "edit", "delete"];
roles = ["user", "moderator", "admin"];
"read" if "user";
"edit" if "moderator";
"delete" if "admin";
"user" if "moderator";
"moderator" if "admin";
}
For an implementation guide, read our guide on building role-based access control with Oso, or for background reading, see our technology-agnostic Authorization Academy guide to RBAC.
- Relationship-based authorization, or ReBAC, means organizing permissions based on relationships between resources. For instance, allowing only the user who created a post to edit it. Relationships include data ownership, parent-child relationships, groups, and hierarchies. Oso provides primitives for hierarchies, relationship-based access control (ReBAC) and attribute-based access control (ABAC).
allow(actor, action, resource) if has_permission(actor, action, resource);
resource Organization {
permissions = ["read", "add_member"];
roles = ["member", "owner"];
}
resource Repository {
permissions = ["read", "push"];
roles = ["contributor", "maintainer", "admin"];
relations = { parent: Organization };
"admin" if "owner" on "parent";
}
For a guide on parent-child implementing role relationships like the one shown above, see our documentation on granting a role on a child resource to a role on the parent. For a guide to building resource hierarchies (e.g., things that look like filesystems), take a look at our guide on building authorization for resource hierarchies. You can also read our technology-agnostic Authorization Academy guide to ReBAC.
Filter data at the database layer.
Your app needs to ask your authorization layer, "return all the posts that can this user can see." It's much more efficient to handle that at the database layer, instead of loading every database record and filtering in memory.
Here's that in the Node app again:
repos = await oso.authorizedResources(User.getCurrentUser(), "read", Repository)
Oso can take the logic in your policy and turn it into a query using your ORM. You define how queries are built, so you can adapt this to any ORM and any query language.
Test your authorization.
You can test the surface of your policy by testing the return value of your Oso.authorize
call. Any Node testing library will work—here's an example with Jest:
test("maintainers cannot delete repositories", async () => {
const repo = await Repository.getByName("gmail");
const user = User.getCurrentUser();
try {
await oso.authorize(user, "read", repo);
} catch (e) {
expect(e).toMatch(NotFoundError);
}
});
You can also query individual policy rules to rest them. This allows you to TDD your authorization.
Learn authorization concepts, architecture, and best practices.
We've written the Authorization Academy to help you get started with authorization. These guides aren't specific to Oso, and cover industry-standard authorization concepts. Learn:
- How to architect your app for authorization.
- Common authorization models like role-based access control (RBAC) and relationship-based access control (ReBAC) – like when to use them and how to implement them.
- Where to enforce authorization at various layers in your app.
How to get started
Add Oso to your app with npm i oso
, then start modeling your authorization logic. Oso is open source, and is on npm and GitHub.
Then join the community of hundreds of developers in the Oso Slack (including many JavaScript and TypeScript experts!) We'd love to talk about what you're working on and answer any questions you have.