Skip to main content
Oso Cloud is authorization as a service. While similar in name, authentication and authorization serve two different purposes:
  • Authentication asserts who a user is.
  • Authorization asserts what they can do.
This guide describes how to integrate Oso Cloud with your authentication provider.

What is authentication?

Authentication confirms the identity of a user. There are a variety of methods available for confirming a user’s identity, from a knowledge factor like username and password combinations to more secure flows that include possession factors or social logins and Single Sign-On (SSO) providers. Oso Cloud is not an authentication provider, but it does integrate easily with authentication providers.

How Oso Cloud uses identities

Oso Cloud assumes that you are an authority for authenticating users and that you store identity data, either in a data store or a hosted service. In Oso Cloud, users are identified by a type and ID. If your application uses universally unique identifiers (UUIDs), a user in Oso Cloud might look like User{"70f19088-7e97-4ca9-be0a-4deeaeedb7a7"}. For example, this is how you could tell Oso Cloud that user 70f19... has the "admin" role on a particular organization:
const user = { type: "User", id: "70f19088-7e97-4ca9-be0a-4deeaeedb7a7" };
const organization = {
  type: "Organization",
  id: "00e54c4c-9bd3-4fc6-99d4-8d433e65d84f",
};

// User 70f19... has the "admin" role on Organization 00e54...
await oso.bulk([[], [["has_role", user, "admin", organization]]]);
That same user object can then be used to make an authorization decision.
const allowed = await oso.authorize(user, "delete", organization);

if (allowed) {
  // Delete the organization.
} else {
  // Return an error.
}

Choosing a user identifier

A good user identifier is:
  • unique per user
  • consistent for the same user over time
  • accessible from any service in your system with minimal overhead
Examples:
  • The sub property in the response from an authentication service or in a JSON Web Token (JWT).
  • The uuid or id field on the users table in a relational datastore.

Hands-on example

In this example, users can update projects by making PUT requests to the /api/project/[projectId] endpoint. Here we use the front end framework NextJS, Oso Cloud for authorization, and Stytch for authentication (though any authentication service will work). When a user logs in, Stytch stores a random string as a session token. You authenticate the user’s identity by validating the session token. The decoded session object contains a user_id for the user, which we use to identify the user to Oso Cloud:
export async function updateProject(req: NextApiRequest, res: NextApiResponse<Response | Error>) {
  // Read the Stytch session token from the cookies.
  const token = cookies.get('stytch_session');

  // Authenticate the session token. Throws an error if the token is invalid.
  const resp = await loadStytch().sessions.authenticate({ session_token: token });

  // Retrieve the user ID from the decoded session.
  const userID = resp.session.user_id;
With the user_id from the session, we ask Oso Cloud if the user can update the project:
  // Retrieve the project ID from the request.
  const { projectId } = req.query;

  const user = { type: "User", id: userID };
  const project = { type: "Project", id: projectId };

  if (await oso.authorize(user, "update", project)) {
    // Update project.
  } else {
    // Return an error.
  }
}

Do I need to sync all my users to Oso Cloud?

You do not need to sync users to Oso Cloud. Instead, you sync your users’ authorization data like roles and permissions. Oso Cloud uses those roles and attributes to make authorization decisions. Since Oso Cloud is deny by default, if you have not synced any roles for a particular user, those authorization decisions will be denied. For more information on adding facts to Oso Cloud, review the facts section of our documentation.

What about scopes?

Some authentication services support scopes for users. Scopes are similar to feature flags or global roles. They provide some data that you can use to determine what a user can see or do. But they can only encode information about the user and not about the resources in your application. For example, a user might have the delete_repository scope that allows them to delete any repository. However if you want to restrict users to only delete repositories they own, a scope is insufficient. Scopes might suffice if you only require coarse, global access control, but they’re an incomplete solution that doesn’t support more fine grained authorization.