Enforce Authorization in your Application

Install the client SDK

Follow the installation instruction (opens in a new tab) to install the Oso Cloud client SDK for your language.

Add enforcement

Most authorization comes down to "can this user perform this action on this resource?" We call authorization checks like these enforcement.

The Oso API for enforcement is authorize(user, action, resource) -- authorize whether this user can perform this action on this resource, and return true/false. This is what you call in your application to do enforcement.

The user and resource arguments are represented using a generic object with a type and an ID. This way, Oso can reference these objects against the data that you've stored.

For example, suppose we have a controller method to read a repository. We'll typically build the user object from authentication information, and extract the repository ID from the request path parameters.

And finally, we can check the user is authorized to perform the read action on the repository.


// get global oso instance
import { oso } from "../app";
router.get("/repos/:repoId", async (req, res) => {
const user = { type: "User", id: req.user.id };
const repo = { type: "Repository", id: req.params.repoId };
if (!(await oso.authorize(user, "read", repo))) {
return res.status(403).send("Unauthorized");
}
// fetch repository from database, etc.
});

Add enforcement using local data

Oso Cloud's authorize_local API can resolve authorization requests by combining data in Oso Cloud with data from your application database.

If your users have access to millions of resources, it may be cumbersome to store all of those IDs directly in Oso Cloud. Consider building an "Issues" microservice for GitCloud. The policy says that users can access issues that belong to repositories they have access to. If repositories have lots of issues, it could be slow or brittle to store all repository-to-issue relationships in Oso Cloud. Instead, you can tell Oso to look in your own database for information about which issue belongs to which repository. This way, Oso Cloud can determine whether the current user has access to a given issue without having to replicate all of the issue-repository relationships to Oso Cloud.

Configuration

You'll use a YAML file to configure how Oso Distributed Authorization looks for authorization data in your database:


facts:
has_relation(Issue:_, String:parent, Repository:_):
query: SELECT id, repository FROM issues
sql_types:
Issue: UUID
Repository: UUID

This example tells Oso that has_relation facts that associate issues with their parent repositories are resolved by running a query (SELECT id, repository FROM issues) against your application's database, rather than by looking them up directly in Oso Cloud.

The sql_types section is optional, but strongly recommended. It maps resources in Oso Cloud (e.g. Issue, Repository) to their data types in your application database. This allows Oso List Filtering to more effectively use indexes, improving query performance.

Usage

When you initialize the Oso Cloud client, provide the YAML configuration:


oso = OsoCloud::new(
...
data_bindings: "path/to/data_bindings.yaml"
)

Returning to the repository example, first use authorize_localto tell Oso Cloud to partially evaluate whether the user is authorized to perform the read action on the repository.

Then, use the resulting query to finish the evaluation using local data.


# get global oso instance
require 'app/oso'
get '/repos/:repoId' do
user = { "type" => "User", "id" => request.user.id }
repo = { "type" => "Repository", "id" => params[:repoId] }
query = Oso.authorize_local(user, "read", repo)
if !Issue.connection.select_value(query)
raise Sinatra::PermissionDenied
end
# fetch repository from database, etc.
end

Policy Tests & Enforcement

The authorize API is intentionally designed to mirror the allow API for policy tests.

When writing a policy test, the test assertions are exactly what you would be passing in via the authorize API.


test "repo members can read their repositories" {
setup {
has_role(User{"alice"}, "member", Repository{"repo-1"});
}
assert allow(User{"alice"}, "read", Repository{"repo-1"});
}


@app.route("/repos/<str:repoId>")
def get_repo(repoId):
user = { "type": "User", "id": "alice"}
repo = { "type": "Repository", "id": "repo-1" }
if not oso.authorize(user, "read", repo):
raise PermissionDenied

Talk to an Oso Engineer

If you'd like to learm more about how to use local data in enforcement or have any questions about this guide, schedule a 1x1 with an Oso engineer. We're happy to help.

Get started with Oso Cloud →