Request-level Enforcement

It’s likely that your app already has a way of preventing logged-out users from accessing certain endpoints. This is an example of “request-level” enforcement, and you can use Oso to express these types of rules.

Often this gets more complicated as time goes along. “Logged-in” vs “logged-out” is the first question you might want to ask, but perhaps users can’t perform certain actions until they’ve verified their email, or perhaps you allow users to create access tokens with particular scopes that limit which endpoints are accessible.

To implement “request-level” enforcement, you can use the authorize_request method provided by your Oso instance.

In your request middleware, after you’ve loaded the user, but not executed any endpoint-specific code, you should call Oso to determine whether the action is allowed:

def before_request(request)
  oso.authorize_request(current_user, request)
end

You can see this method only takes two arguments: user and request. The type of the request argument depends on your app. In most frameworks, there is a Request type, and we recommend using that so your policy can access fields such as its method (POST, GET, etc) and its path.

If you’d prefer, you can also pass a string as the request argument. The string could hold the request’s path or, if you’re using GraphQL, the string could be the name of the current GraphQL query or mutation.

Writing a request-level authorization policy means implementing an allow_request rule like this:

# Allow anyone to hit the login endpoint
allow_request(_, _: Request{path: "/login"});

# Only allow access to payments by users with verified emails
allow_request(user: User, request: Request) if
    request.path.start_with?("/payments") and
    user.verified_email;

Authorization Failure

What happens when the authorization fails? That is, what if there is not an allow_request rule that gives the user permission to perform the given request?

In that case, the authorize_request method raises a ForbiddenError, which your app should handle by returning a 403 error to the user and aborting the request.

Access token scopes

You can use a request-level authorization policy to protect your endpoints using access token scopes. This can be used to build features like GitHub’s OAuth Scopes.

allow_request(token: AccessToken, _: Request{method: "POST", path: "/repos"}) if
    "repos.create" in token.scopes;

allow_request(token: AccessToken, _: Request{method: "GET", path: "/repos"}) if
    "repos.list" in token.scopes;

To do this, you’d have to call authorize_request with the access token used to make the request:

oso.authorize_request(current_access_token, request)

Connect with us on Slack

If you have any questions, or just want to talk something through, jump into Slack. An Oso engineer or one of the thousands of developers in the growing community will be happy to help.