Python Authorization Library

Oso is a batteries-included library for building authorization in Python.

  • It's embedded in your application—no extra processes to run.
  • Oso uses your data models. Pass Python objects to Oso, and your authorization policy can refer to Python properties and methods on those objects.
  • 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.
  • Native support for Django, Flask, and SQLAlchemy.

A single-line Python authorization API.

The Python authorization API is:

oso.authorize(actor, action, resource)

Here's an example in a Flask controller, using the Flask-Login package for user management:

oso.authorize(flask_login.current_user(), "read", Posts.get_by_name("python-auth"))

Express your authorization model.

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 Python app again:

posts = oso.authorized_resources(User.get_current_user(), "read", Post)

Oso's libraries for SQLAlchemy and Django support this without any extra configuration, or you can write your own database integration. Our guide to data filtering in Python helps get you started.

Test your authorization.

You can test the surface of your policy by testing the return value of your Oso.authorize call. Here's an example with Pytest:

def test_maintainers_cannot_delete_repos():
    repo = Repository.get_by_name("gmail")
    user = User([Role(name="maintainer", repository=repo)])
    with pytest.raises(ForbiddenError):
        oso.authorize(user, "delete", repo)

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 pip install oso, then start modeling your authorization logic.

Then join the community of hundreds of developers in the Oso Slack (including many Pythonistas!) We'd love to talk about what you're working on and answer any questions you have.

Get started with Oso

Want help thinking through authorization in your app?

Explore our docs

Access guides, example apps, and authorization best practies.
Read Oso docs

Connect on Slack

Get help from our team, and talk with hundreds of like-minded developers.
Join the Slack

Meet with us 1x1

Schedule time with an Oso engineer to help model your permissions.
Schedule your 1x1