Sam Scott: Authorization and SQLAlchemy - Montréal-Python 83 – Minimum Night
Watch on YouTubeVideo summary
Sam Scott, co-founder and CTO of OSO, introduces authorization as a critical component for securing applications, distinguishing it clearly from authentication. While authentication identifies who a user is, such as logging in with credentials on GitHub, authorization determines what that specific identity can do within an application. He illustrates this by showing how different permissions apply to various resources, like viewing teams in one organization versus being restricted in another. Scott argues that handling these permissions often leads to messy, unstructured code filled with complex if-else statements, which are difficult to maintain. To solve this, he advocates for a structured approach using roles, a concept abbreviated as RBAC or Base Access Control, where users are grouped into buckets like members or admins, each with predefined capabilities that simplify the logic of what actions are permitted.
To implement such a system effectively, Scott presents OSO, an open-source authorization library designed specifically for developers to integrate security directly into their applications. The core of OSO is a versatile library available in multiple programming languages including Python, Node.js, and Rust, which serves as the foundation for making authorization decisions. He highlights two additional key components: built-in functionality for managing roles out of the box and a declarative policy language called Polar. This language allows developers to define rules that express exactly who can perform what actions on specific application objects, such as allowing a user to read an organization if they are listed as a member. By separating these policies from the business logic, OSO enables a cleaner separation of concerns where security rules are defined independently and enforced programmatically.
The presentation culminates in a live demonstration integrating OSO with SQLAlchemy, a popular toolkit for handling SQL data models. Scott walks through building a simple API that initially allows unrestricted access before implementing authorization checks to restrict users based on their email domain. He then evolves this example by introducing the built-in role management features of OSO, which automatically handle database lookups to determine if a user holds a specific role within an organization. This approach eliminates the need for hard-coded conditions and allows policies to be written generically, such as granting read access to any user who possesses a valid role in an organization. Scott emphasizes that this integration is highly efficient because the enforcement happens at the database layer, ensuring robust security without bloating the application code with complex permission logic.
In conclusion, Scott positions OSO as a solution that bridges the gap between simple, easy-to-use frameworks and powerful, extensible systems. He addresses common questions regarding integration with JWT tokens containing scopes and compatibility with FastAPI, explaining how OSO can handle scoped tokens by combining user identity with token permissions in policy definitions. His argument is that while many frameworks excel at basic authorization, they often fail to support complex or evolving use cases without significant friction. OSO aims to provide the best of both worlds by offering immediate simplicity for standard roles while retaining the flexibility of its underlying policy language for advanced scenarios. Ultimately, he encourages developers to leverage these tools to build secure applications that are easy to maintain and capable of adapting to changing security requirements as their projects grow.
Read the full video transcript
okay so yeah i'm sam the co-founder and
cto at oso um i'm gonna talk about
authorization which is one of the main
things we're
solving here at also uh so i'm just
gonna give people a quick
outline of what is authentication and
authorization how to release your
application
uh i'm gonna talk through some very
common patterns around roles
and do a little live demo for this
instinct like you know so
i'm going to keep trying to go through
things reasonably fast
okay so authentication is this process
of identifying who you are
so you know you go to github for example
you log in with username and password
and you sign in as in my case samsung89
this is authentication github now knows
i'm sam um
and the next question is you know what
can sam do and so this is authorization
right so
suppose i try and visit these two urls
um you know one for the oso
org teams one for sql alchemy teams i
see these two different pages
and basically what's happening here is
that github has decided oh
samsung89 you're a member of the ursa
organization you can see the teams but
as it says here at the bottom
i'm not a member of any teams in this
equal alchemy organization so it's not
allowed
all right this process here is
authorization and this is what i'm going
to be talking about today
now authorization is kind of this
somewhat you know vague unstructured
problem you know who can do what in your
application and it's very easy to end up
with code that looks something like this
where you have a sort of kind of if else
statements you're checking a bunch of
different conditions because
often this is what permissions come down
to you know it's like who can do what
and
you define this by you know check
universe various properties
so what can we do better than this like
how can we how can we make our
application
structured nicer so it's easier to
maintain update change things like that
so roles is this very very common
approach to this now what roles does
is it takes your setup like this so
suppose you have
you know a bunch of users you have a
bunch of resources things that try and
access
and it helps you break this problem down
um but
you know i should say you often see this
abbreviated as our back which stands for
base access control
again so what this what roles do is they
take this problem and they basically say
all right well
most of these users you know maybe they
fall into this bucket we call them
members and there are only a few people
who are you know privileged users we'll
call them admins
and now instead of having to define what
everybody can do my app i just need to
define what these two people can do
members can read and write and admins
can additionally delete
kind of simple as that and again like
looking at this in like a realistic
example
um here is a kind of a slice from
git lab's permission system and so you
see along the top here they have
five different roles from the guest to
developer to owner
and there's like this table of all the
things they can do right so this is a
pretty pretty decent role system and you
know gitlab is this huge huge
application there's a ton of stuff you
can do from
maintaining source code all the way down
to like managing kubernetes and
deployment stuff like that
so there's a lot of things you can do in
it but they've managed to reduce it to
effectively these like five different
roles
and the nice thing about these roles is
they kind of
give you this intuitive sense of what
you should be able to do right like you
wouldn't expect that a guest
to your organization could come and
start deleting projects or things like
that
whereas the developer you know can
probably you know manage the code they
can make edits things like that
owners can you know do everything okay
so this is like a nice example of um you
know rolls down quite well because
there's like a relatively small number
of
of them um and they kind of relate to
intuitive concepts so
um what i want to talk about around oso
is how it kind of helps you to build
this kind of a system
so oso is an open source authorization
system and it's designed specifically
for developers building security into
their apps
um the kind of core of oso is a library
and this is a library for
making these authorization decisions so
once you're deciding who can do what in
your app and enforcing that and doing
and adding this to your application
and it's available through you know
standard package managers so in the case
of python
know pip install oso but it's also
available for a number of different
languages
the core of those is actually written in
rust and we make it available to
multiple different languages so node
java ruby
python go rust
second piece is we we made also sort of
batteries included
and what i mean by that is we've kind of
given you out of the box
functionality to manage things like
roles in your application and that's
what i'm going to be showing you in the
demo is how we've enabled this
in sql alchemy um to allow you to do
like a very rich like roles based access
control system
in your app with c glaring also
and the final piece of oso is
effectively how you configure it and you
do this through
the policy language called polar and it
looks like this at the top
so this allows you to express who can do
what in your app in terms of
um and by writing these rules so this
rule here for example says that
a user is allowed to read an
organization if the user is in the
organization
organization members and this policy
here it's written in the polar language
which is declarative policy language
but it's express over your application
objects so when i actually call this at
the bottom in the python library
for example i call the oso is allowed
function and i pass in my user object my
organization object
from my application data so this policy
at the top here is expressed over
attributes or methods or types things
like that from the application
so for those of you who are familiar
with sql alchemy um
you know it's this sort of it's this
large amazing toolkit for
you know handling all kinds of sql
inside your application so you can you
know managing everything from
you know writing sql queries and
expressions it's got like an rm model so
you can use python classes to represent
your data your data models migration
tools and kind of on and on
it's kind of this entire toolkit for
everything sql in your app
um oso kind of wants to be the same for
authorization and so the pieces i've
spoken to about today is is sort of just
the starting point
um and kind of in this way these two
provide this uh this like really great
match together
where they sort of both giving you a lot
of this out of the box
kind of experience and things you need
to think about for how to do
sql authorization and we'll see together
how those kind of pair up nicely
okay cool so i'm going to give a little
demo of the sql archiving oso
integration
what i'm going to start with i have a
very very simple github api built with
a combination of flask and sql alchemy
which i'll walk through
uh we're going to start off just by
adding some simple authorization with
oso so just want to kind of get the
basics in
and then show you what it would look
like using our built-in stuff for roles
um so we can kind of create something
similar to what i was speaking about
earlier
okay so here is my application let me
make this a bit bigger for you
uh here's my my data models so this is
where i'm using the sql alchemy
data models to define my data sets so
this is very very straightforward i have
a user class you know has an email
address for example
i have organizations i have repositories
right so this is my
my github very simple github data model
and your
repositories can belong to an
organization for example
then in my my root.pi files this is
where i'm you know my flask
uh api endpoints i mean i'm using uh i
have some
some simple routes to allow me to do
things like list all the repositories in
an organization
so for example if i
go and get the if so i'm going to hit
the api using curl
if i call this api as the
userportbeatles.com
and get the repost for organization one
uh you can see this the organization has
one repo called abbey road
whereas organization two has one repo
called
paperwork okay
so right now every user can do
everything so what if i actually wanted
to restrict what paul the beatles was
allowed to
see and say that he could only actually
see repositories from
organization one so what i want to do is
add in some authorization here in my
repos index
to check that you know the user can
actually read this this organization
so first of all i'm just going to get
the current app uh
helper from flask allows me to get
application state
and i'm going to get the i'm going to
get the oso object
off of my application state so where's
this coming from what is this
so the way i initialize oso is in this
in my flask initialization
um you see i have a method on my app
that i'm going to call on startup
which is just going to create a new oso
instance um
and do some do some relevant things like
load my policy files in
as i said that's how i configure my app
my my authorization is through policies
and finally i'm going to add ozone to
the application state okay
so that's where that's coming from i can
now get my oso instance and do
authorization
so i'm going to use the the flask the
flask also
helper method authorize which will take
in
the organization it's automatically
going to populate the current user
and but i'm going to say the action i
want to check is a read action
okay so once i've added this line i'm
now doing authorization as part of this
api endpoint if i now go and
try and access an organization i'm
getting 403 forbiddens
nothing is allowed anymore because um
basically
once i configure oso it's 9 by default
and my policy is currently completely
empty
i have no i've no policies i haven't
allowed anyone to do anything so
everything is
is not allowed let's change that let's
let's allow everything let's allow every
user to every action to every resource
um and i can do that by writing it this
way these are basically my my inputs my
input variables
and i can just i don't need to add a if
statement i can just say this is always
true
so now i've done that i am now able to
access everything again okay so we're
making some progress we have
authorization we've allowed everything
uh let's let's let's restrict that a
little bit more so
like i said earlier these inputs are
coming straight from the application so
this user is a user
the action we're checking is reeves and
this is actually an organization
and so we want to say maybe we just want
to say that the user's email address
must end with um
beatles.com let's say
and the organization name is
of organization number one is actually
the beatles
there we go so now we have a policy
saying that only users with the
beatles.com email address
can read the beatles organization
so i can still not access organization 2
but i can access organization number one
which is the beatles org
so cool i've got some i've got some
authorization going on
but clearly i don't have to do this for
every single user in my system every
single organization that joins
i want this logic to be like generic
over over organizations more generally
and so that's where roles come come back
into the pictures i basically want to
express
this the fact that users can have a
relationship with organizations
and that relationship is is effectively
their role
so let me reset this
okay so i just checked out a new branch
which has my roles implementation
and i'm actually going to reset that so
i can show you the
the diff there we go
okay so you can see the changes i've
made um
right here i need to make that a little
bit smaller
so in my in my initialization page um
i'm just importing this new built-in
rolls functionality and enabling that on
the oso instance
so that's all that happens and really
the magic that's happening here is i'm
importing this
resource role class helper from the sql
alchemy oso roles
package and what that's going to give me
is give me this
sort of helper method that allows me to
find a mix in to create a roles
relationship between users and
organizations
so here we can see it let me hide this
so here we see we're creating the mix in
by calling this ho this helper method
from oso
giving in the user of the user class the
organization class
and the list of roles we want to create
on that you know between those two
things
and now i have this mixed in i basically
can create a new sql alchemy model so
this is creating a new database model
um but basically pre-filling a lot of
information from this
this oso helper mix in and now that this
is in place i now have a data model
connecting users and organizations
and actually there's a bunch of helper
policies now already added to my app
so i can now start doing authorization
directly over users that have roles in
my app
so suppose i added some data just like
this
to say for example that john is an owner
of the beatles org
then in my my policy might look
something like this and let's go back to
the regular screen here we go so instead
of writing my allow rules like i did
before i can now write these
role allows which really have the same
purpose
but is effectively handling all the like
um
the the database lookup you know the is
is a person in a particular role or not
like does this exist in the database and
those kinds of things
i can just focus on writing permissions
in my uh
in my in my system so like this one says
that if you have any role
inside an organization then you have
read access to that organization
um so in my in my root now you in my
repos index root you can see that i've
updated the action to list repos
so i'm going to check whether the
current user is allowed to list repos
for this organization
and if i now come back
okay cool so you can see paul is now
still able to list
uh repost from organization one and not
from organization two
but whereas before we kind of like
hard-coded the beatles.com and the
and the old one thing this is now true
over um
all users and all members who have
who have roles so for example i have
sally monsters.com who
cannot read organization one but can
indeed read organization number two
because of the role they have and
um and yeah so this you know this is
really all that all that we need to
write in here is just
some simple policies about what and
roles do everything else is taken care
of in the database models
and i think the really nice thing about
this is um
just how few lines of code we needed to
get that role model set up this is now
extremely extensible it can support
everything from like inheritance
patterns hierarchies it can do
um you know things like you could even
have a role for a repository if you have
the role
in the parent organization things like
this
and with the oso sql alchemy integration
this is actually all enforced at the
database layer
itself so this is incredibly um
efficient because it's actually all
happening just over the database and not
like
in the in the python code
okay so those are the main things i
wanted to cover um this was a pretty
kind of quick intro to oso and the sql
alchemy stuff in particular
we have a lot more information available
on our website on our doc site
um or you can come speak to us directly
for example in our
in our slack channel we have for the
open source community
or see things on github and specifically
if this if this example is interesting
i'd recommend
on the docs page which you have that sql
alchemy
example is like a tutorial
okay thank you very much for listening
thank you so much that was super
interesting and thanks for sharing the
links to
where to reach you and get some more
information
on my side i didn't receive any
questions for your talk for now
so thanks all right
hey yeah thank you sam for
for your talk uh i i i'm
i don't know i don't have a lot of uh of
experience with uh authorization
and all of that thing but uh
uh can you give us a bit uh the com
comparison about uh also
and and maybe the other system that
already exists
why also it's uh like
more cool
[Laughter]
so i think the the thing that i think
really sets
oso apart is that we're sort of going
for the
we're trying to we're trying to kind of
have the best of both worlds in that it
is
very easy to do the sort of simple
things and
you know roles can get roads can get
complex but like when you start out and
you just need to say a user and an admin
you have two roles
maybe just hard code them it's fine it
can start out easily but like over time
it gets hard
um and so what we're really trying to do
is make the easy stuff easy by providing
that
out of the box two lines of code you're
good to go you have roles
but because it's you know backed by this
policy language
the policy language itself is like very
powerful um
and can be used for more things than
just authorization for example
this actually means it's like very
extensible so as your crimes change if
you have some
weird use case it's not quite a role
it's like somewhere between the two
it's fine the policy language will let
you write those
i think like a lot of frameworks they
maybe do the simple stuff very well but
they don't
let you extend and then you get into
this like very hard um
situation where you're trying to like
fight against
the system thank you sam
i actually got two questions as you were
answering this one
so the first one is how does it
incorporate with
jwt containing scopes and the second one
is how does it work with fast api
okay so the so on the one about jwts so
for people aren't familiar with is these
are sort of um
authentication credentials so you get
like a cryptographic token that maybe
can include
extra information for example a scope is
like a typical one that
you know the the github api contains for
example so you might create a api key
that's given a specific scope that says
you know this token allows the
the holder to read repositories that
kind of thing
so how that looks in oso is you would um
you basically use it as additional
information on the input user type
so maybe in this case the
uh you may need to look at a combination
of two things so the user might be
the person who originally created the
api token but maybe the token itself
is um kind of limited to only fewer
things right so i've created an api
token for github
um so it can do things as soundscape89
but i don't want to do everything i want
to just be able to read repositories so
it has that scope
so then your policy can contain both
like what can sound do what can the
token do
make sure that both are applicable
uh so hopefully that answers that
question on
uh fast api um it works pretty well with
with fast api i think there's like a lot
um because fast api has like a very
strong preference towards
a strong typing i think that works very
well with the
kind of types that you can use inside a
inside the the polar policy
um and we do have a
we have someone who who actually from
our community who wrote a fast api
example app with with oso and it it fit
really nicely
it was a good combination don't have the
link to hands
um but it's if you
you could either join our slack or email
me and i can i can share it with you
okay