
Ask HN: What are the best practices for web authorization at the moment? - keth
Hi,<p>since authorization is important to me as a beginner, I want to ask what are the best practices for web applications at the moment if one needs more fine grained authorization rules than simple roles or permissions for a sideproject (which will probably never turn into something profitable, nor is it planned at the moment)?<p>Until now I&#x27;ve seen role based authorization which works for simple sites but can easily get out of hand for more complex websites (e.g checks like these: user.hasRole(&quot;Superadmin&quot;) || user.hasRole(&quot;Admin&quot;) || user.hasRole(&quot;YetAnotherRole&quot;) || ... ). If I handle it like in the example, I would need to recompile the application if I give roles access to methods for which they were previously not authorized (if it is not possible to just assign the user to a higher role).<p>In previous hobby projects I solved this with permission&#x2F;activity based authorization, where users are assigned roles, and permissions are assigned to roles. The checks within the application are against the permissions not the roles, and can be changed without recompiling the application. If a role needs new permissions just assign it in the backend, if a specific user needs new permissions it&#x27;s possible to create a new role with appropriate permissions etc.<p>But how do I handle more complex permissions like this? 
- Superadmin can delete everyone except himself.
- Admins can delete all users but not themself or other admins.
- Manager can only delete users that he manages but not himself.
======
olalonde
I smell over-engineering. It sounds like you would like to keep your
authorisation logic nicely decoupled from your code, stored away in a
database. As you are starting to notice, you can only get so far with that
approach and as you try to make your configuration more powerful to handle
more and more edge cases, you will inevitably end up with some sort of Turing
complete configuration system. My advice is to stick with something simple and
just hard code the edge cases in your code. It may feel dirty but at least you
won't end up with a monster like AWS IAM in your side project.

~~~
ironchef
And you basically laid out a recipe for incurring technical debt.

What's wrong with instead looking at some established best practices /
suggestions and using those for guidance. What if they said they were looking
to do this with encryption? Would you say "Just hash it with ROT-13 and put it
in the DB"? Of course not. Others have gone before us...why not learn from
where they've done well and not so well?

~~~
olalonde
What I am recommending _is_ an established best practice: YAGNI or perhaps
KISS. When I meant something simple, I didn't mean to throw away the whole
authorisation abstraction. I do consider things like role/permission based
authorisation relatively simple as long as the whole logic doesn't live in a
database a la [http://blog.bronto.com/wp-
content/uploads/2014/10/imagecache...](http://blog.bronto.com/wp-
content/uploads/2014/10/imagecache/blog-full-colum/magento-21.png).

~~~
ironchef
Ok. I guess the difference is I would never suggest RBAC to be "relatively
simple". There are so many considerations (are negative permissions required?
are there many to many required? separation of duties?) that without making
things explicit it's hard to know what is meant by "role/permission based
authorisation".

------
jph
I do authentication in terms of operations a.k.a. claims a.k.a. attributes,
such as "Can Create User".

These are simple, easy to understand, and provide good flexibility.

If you enjoy using roles that's fine too: for example you can create an
"Admin" role that grants permissions to do many operations. In other words the
Admin role has many claims.

In your authorization code, you check the operation, not the user nor the
role.

In your database or ORM, you can create these tables: users, users-roles,
roles, roles-operations, operations.

We have a detailed writeup here:
[https://github.com/SixArm/sixarm_ruby_rbac](https://github.com/SixArm/sixarm_ruby_rbac)

------
ejcx
I think most people answered your question without reading it. They must be
mind-readers.

In my opinion there's only one way to do this. Admins who are all powerful,
and non-admins. None of this complexity of super-admins, etc.

Admins have access to everything. Groups have access to things given to them.
Users in more than one group have access to the set of all things their groups
have access to. Users without a group are really just users in a group of 1.
They have access to things given to them.

People who ask for more complicated permissions than this are asking for foot
shooting abilities, where people have access to things they don't mean to.

The rest is up to UX. Notify other admins when one behaves badly. Confirm
admins really did mean to remove themselves from the admin group.

~~~
eterm
This really doesn't satisfy real world usage.

Let's say I'm on github and I have a number of repositories. I want admin
access over my area, the power to create and delete repositories. I do not
want other people to be able to create and delete repositories.

And then I lastly want to be able to have access control over some
repositories completely (private repositories).

How can you have authorisation that means that I get to sometimes create and
delete repositories, that other people can have some permissions for, without
having more than 2 levels of authorisation?

Just saying "groups" is just hand-waving the question away, all the complexity
of role-based vs attribute based authorisation is hidden in "use groups".

------
tboyd47
One thing you could do is to keep things as simple as possible inside the
application but code "confirmation pages" into every restricted flow. So the
confirmation page (or controller/Form object/Service/what have you) is what
contains the authorization logic, not the main action flow. So every time you
have a particularly sensitive action, you just give that action another step,
in which you store your messy authorization code.

A fun benefit to doing it this way is that you can 1) if they have permission,
you can make the extra step totally transparent to the user with redirects and
2) if they don't, it also gives you the chance to explain fully why they can't
do the thing they wanted to do in a whole nother page.

Try to avoid having a big authorization control center as long as possible.
Really when enterprise people request these things, they're trying to account
for very specific edge cases in their previous apps that they got burned on.
They don't really want to experiment and play with every possible combination
of roles and permissions.

If they absolutely insist on having an Excel-like spreadsheet of users and
permissions to play with, then just import an Excel spreadsheet and enforce
any sort of arbitrary format on them you want.

Just two cents, not bulletproof advice by any means.

------
chejazi
You can stuff the logic of "who can do what" somewhere else. eg:

var permissions = { "Superadmin": ["selfdestruct",...], "Admin": ["ban",...],
"Plebe": ["post",...] };

function roleCanDo(action, role) { return permissions[role].indexOf(action) >
-1; }

Your check would look like: if (roleCanDo("someaction", user.getRole()))

Sorry if JS isn't your thing.

~~~
striking
Sorry to nitpick, but indexOf is a bad idea. Using it is up to 1700x slower
(worst case) than using object indices[1]. It's probably an O(n) operation,
where indexing an Object is nearly always O(1).

1: [https://jsperf.com/indexof-vs-object-lookup/4](https://jsperf.com/indexof-
vs-object-lookup/4)

~~~
ar0b
Just poking around, could you explain why [https://jsperf.com/indexof-vs-
object-lookup/32](https://jsperf.com/indexof-vs-object-lookup/32) indexOf is
the fastest in this revision?

~~~
striking
That revision tests the best case (looking for string "a") while the revision
I picked tests the worst case (looking for string "xxx", which doesn't exist).
I picked it just because it came up first in Google search. Testing in my
browser (Safari 9.0.1, Mac OS X 10.10.5) still gives a 54x improvement to
Object indexing, though.

Perhaps getting the first value in an array is faster than indexing an Object
in your browser.

The point is, if the thing doesn't exist, this part of the program will be
thousands of times slower, and will get slower as the list of roles grows.
Meanwhile, the hash map will not.

~~~
ar0b
Thanks for the explanation!

------
opmac
Attribute/Claims based access control seem to be the most recommended these
days. Legacy apps will tend to stick with the role based approach.

~~~
keth
Do you know of more involved examples that use claim based authorization? So
far I have only seen simple use cases that at most involve if a user works for
a certain company, or that a user can only delete his own postings (userID ==
authorID), which I can easily handle with permissions. But I have not seen
slightly more complex ones yet like the example in the OP or really complex
ones. But will definetly look into it, the .net implementation seems fairly
easy to understand (after skimming through some files).

------
Osiris
In my application I do this using tables in the database. The database has a
roles table, a permissions table, a roles_permissions table and a roles_users
table. When a user it looked up, a JOIN is done to pull in all the permissions
assigned to all the roles, then there's a configuration that specifies with
permission is required to access each API call.

This allows us the flexibility of adding additional roles and permission
combinations without making any code changes.

~~~
Shorel
I do exactly the same.

I'm also adding a role editor, where an user with admin role can add or remove
particular permissions to each role.

------
lucaspiller
Where I'm currently working we have quite complicated roles which are based
upon the position a member of staff holds. Positions may change, and the
permissions follows the position not an individual user. We have many
applications, and each is free to implement their own roles but we have an
open source gem which forms the basis of it:

[https://github.com/ifad/eaco](https://github.com/ifad/eaco)

At an application level there is a simple DSL that lets us define abilities on
a resource (e.g. a document can be viewed, edited, approved and deleted) and
roles (e.g. editor, reviewer, auditor), and to link those two.

On the actor (a user / member of staff) we define a set of designators such as
what department they belong to, what position they hold in that department,
what projects they are responsible for, etc.

When we then create resources, we link the actor and the designator which is
ACL definition. For example, to say everyone that is a manager of department 5
can review a given document we do this:

    
    
       document.grant :reviewer, :department_manager, 5
    

Permissions can be added and removed to the resource's ACL on the fly - so for
example we could grant temporary access to a member of staff to review a
document, then revoke their access once they've given the ok.

It's easy to check if a user can perform a given action on a single resource,
but we also want to see what actions a user can perform against a collection
of resources, for example to present a list of Documents they have access to.
The ACL is stored using PostgreSQLs JSONB columns, which can be efficiently
queried against:

    
    
        { "department_manager:5" => :reviewer, "department:3" => :editor }
    
        SELECT * FROM documents WHERE documents.acl ?| array['department_manager:5']

~~~
bshimmin
As an unashamed Italophile, I very much liked the "DOC" at the bottom of the
README in the repo!

------
swalsh
This is not a programming problem, this is a Roles design problem. Admin is
not a role, its a type of user. You might call it a container if you want...
but roles are more fine grained, and should be a hierarchy.

You can put a hierarchy of Roles in a database, but the actual role check can
(and I think should) be hard-coded.

In your API you might have a method "public void NewProduct()" That individual
method should have the ProductWrite permission (role), and you can give that
permission to higher level "containers" or "roles". So Admin has ProductWrite,
and ContentManager might also have ProductWrite. In your code it's just one
check has("ProductWrite").

Does that make sense?

~~~
evtothedev
Insisting on it being a hierarchy is an easy way to get into trouble.
Eventually, you'll want to give someone "just one more permission"

It's more flexible to have a composition of roles, such as "UserCreator +
FinanceReportRunner" or something like that.

------
thruflo22
Pyramid has a very flexible security system
[http://docs.pylonsproject.org/projects/pyramid/en/latest/nar...](http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/security.html)

You can define access control policies on a resource by resource basis. These
can, where necessary, allow or deny permissions to individuals or roles based
on the resource and request data.

There's a nice demo / walk through of capabilities here
[https://github.com/mmerickel/pyramid_auth_demo](https://github.com/mmerickel/pyramid_auth_demo)

------
ShirsenduK
Cancancan is one best tools to do this in Ruby. Maybe its DSL-like syntax will
give you better ideas.

[https://github.com/CanCanCommunity/cancancan](https://github.com/CanCanCommunity/cancancan)

~~~
keth
Oh, cancan is alive? Cool. I already looked at the old CanCan repo and it's
the best looking library for inspiration I've found so far. Maybe I should dig
deeper to see how more complex authorization rules are handled.

~~~
ShirsenduK
Yeah cancan was revived by Cancancan :)

------
agentgt
You can model most security requirements with either decision matrixes and/or
decision trees. These matrices and/or trees can be retrieved or calculated
dynamically avoiding you from having code like: user.hasRole("Superadmin") ||
snip...

The two primary models are Access Control Lists or Role based security. You
can google lots of info on these two security models.

In the past for enterprise/b2b I usually build ACL security with a Role based
security on top (that is the RBAC underneath is ACLs). Obviously for consumer
based products this is massive overkill.

------
zekenie
I'm working on an open source MEAN stack permissions framework that does some
of what you're talking about. Its not fully baked yet, but feel free to give
it a look: [https://github.com/zekenie/dmv](https://github.com/zekenie/dmv)

It let's you say, "Moderators can delete posts", "Users can create posts", etc

------
arihant
One way you can do that is by architecting something like chmod. Basically a
point based system. 7 gives all, 1 gives read. You can then have your actual
implementation compiled away, but can change points for a user as and when you
like. The code that checks won't need to be recompiled.

------
tony-allan
The last part of your question looks more like business rules for the
management of users.

A complex scheme such as AWS IAM policies increases the chance of definition
errors.

The business rules don't seem like they would change much over time so coding
them in your user management module might be a good compromise.

~~~
keth
Yes, I'm not always sure about what is what. The line between business rules
and authorization rules seems to be thin/blurred at times to me.

------
LukeB42
As one of the contributors to Microauth[1] I have to say file-based auth using
a dedicated auth server behind the scenes.

1\.
[https://github.com/LukeB42/Microauth](https://github.com/LukeB42/Microauth)

------
ynniv
Users have roles, roles provide permissions, some permissions are
parameterized:
[http://shiro.apache.org/permissions.html](http://shiro.apache.org/permissions.html)

------
Dowwie
You implemented what is known as a flat Role Based Access Control (RBAC)
model. It is the simplest form of RBAC, which gets much more complex and
powerful from there!

------
HugoDF
Can't you do some simple inheritance stuff instead? Like User has_one Admin
has_one SuperAdmin. Then join across your models.

------
bearfrieze
Defining authorisation rules as part of your application sounds fine to me.
Aim for making compilation and deployment of your application trivial.

Sounds like you want a linear hierarchy of roles. Attaching a number to each
role and making rules based on that number might be all you need. For example:

    
    
      Superadmin : 100
      Admin      : 90
      Manager    : 80
    
      if (a.number > b.number) a can delete b

~~~
Practicality
I had a hybrid system at one point that included this "access level" in
addition to the roles system. So a person could have multiple roles, but only
one access level. The access level was only for admin things like this, so
most people were just "users."

It works fine, but you need to document what is going on, or at least put some
good comments in. You are definitely going to confuse future developers
(including yourself) as to why you sometimes check one set of constraints and
sometimes another.

I got rid of this once the system got more heavily used though, as it's just
too much overhead to keep track of two systems. So if you really do plan on
staying small it's fine, but if you think this might grow, just stick to
roles.

------
JoachimSchipper
If you're really looking to overengineer this, look at SAML. ;-)

------
rdegges
The way we allow developers to model this sort of thing out at Stormpath
([https://stormpath.com/](https://stormpath.com/)) is via Applications,
Organizations, Directories, Groups, Accounts, and CustomData.

Those are the top-level resources we make available for 'modeling'
authorization stuff.

Here's how it works (you can replicate this sort of setup in your own systems
if you want):

A Directory is a 'bucket' of Accounts, that is unique by email / username.
This way, you can segment users into Directories however you wish. If you were
running 3 sites with entirely separate user bases, for instance, you might
have 3 separate Directories, one for each underlying website. This way each
site has its own unique group of users.

An Application is a collection of mapped Resources. So, let's say you have an
Application for each website you run. You could choose to:

* Map a single Directory to each Application. This way, when a user authenticates against your Application, their credentials will be checked against a single Directory of Accounts.

* Map multiple Directories to each Application. This way, when a user authenticates against your Application, their credentials will be checked against multiple Directories of Accounts.

* Map other resources Directly (Groups / Organizations).

You've also got the Organization Resource, which is basically a 'Tenant' \--
used for designing multi-tenant systems. Each Organization can have multiple
Directories / Groups mapped to it directly, and Authentication / Authorization
checks can be done against the Organization endpoint directly to control
behaviors.

Then, you've got the Group Resource. This basically is a label that you can
use to form Many-to-Many relationships between Accounts and Directories.

So, let's say you have a Directory with all your website users inside of it.
You could create the Groups:

* Admins

* Super-Admins

* Developers

And assign Accounts to those Groups as needed. When a user is retrieved, you
can then pull from their Groups, or vice-versa, to control Group-level
information.

Finally -- you've got this big blob of JSON we call 'CustomData'. This is a
Resource that is attached to _every other Resource in Stormpath_.

In CustomData, you can store things like claims / permissions. For instance,
if you have an Account object, you might store some permissions like so in
your Account's CustomData store:

{ "can_read": true, "can_write": false, "can_delete": false }

By doing a union of CustomData, Groups, Accounts, Directories, and
Organizations (each of which have their own CustomData), you can essentially
model out very complicated User Authorization patterns across your code base
in a pretty dynamic way.

If you'd like to chat about this at all, or would like to just email me some
suggestions or whatever, I'm totally open to feedback! randall@stormapth.com

~~~
welanes
CustomData in Stormpath is so damn useful.

Looks like you have a typo in your email. Guessing should be:

> randall@stormpath.com

------
levelist_com
Over the years I have tried many implementations, first using a binary system

0 - guest(0) 1 - user(1) 10 - moderator(2) ... 10000000000 - admin (1024) ...
1000000000000000 - superadmin (32768)

basically, a new role had the highest order bit set to 1, then the rest zeros.
To add a user to moderator and user (11), or admin & moderator (10000000010),
for example, you would do something like this. At the method level I could
either do bitwise operations to check for certain bits being set to 1 or I
could just check that the decimal was >= a specific decimal value (but this
could allow users I may not want having access). The limitations being many,
not least of which: what should I set superadmin/superuser to b/c this
determines the max amount of future roles that can be added without going
through the code and refactoring.

I then moved to a system of roles and user permissions, meaning I had
permissions at the group/role level and permissions at the user level. I would
merge these permissions together when user is authenticated. At the user level
I could remove a role from a user belonging to a group by adding the same
permission as found in the group but with an integer -1, or i could add a
permission to a specific user with an integer value of 1. I was still checking
for role and permission at the user level. This was better in the sense that I
could add as many roles as I wanted, as many permissions as I wanted. Merging
permissions was a bit of a pain but once it was all worked out it worked
pretty good. I was still checking for roles/group membership and permissions
at the method level and well this causes a lot refactoring when you want to
change what permissions and groups can execute a particular method. I had
always felt this was a pain and this system was a bit of overkill but it did
exactly what was needed at the time.

I then came across an old article, pretty sure it was this one -
[https://lostechies.com/derickbailey/2011/05/24/dont-do-
role-...](https://lostechies.com/derickbailey/2011/05/24/dont-do-role-based-
authorization-checks-do-activity-based-checks/) \- and it led me to the path,
right or wrong, that I do now. I create a table for permissions, users,
groups. I then create a pivot/join table between groups and permissions, and a
pivot/join table between users and permissions. This is so I can give group
level and user level permissions. an example permission might look like this:
user.create, user.delete, user.update, user.block, user.suspend, etc.. At a
user group level a user may have all those permissions but user.suspend, but I
may want a specific user to have user.suspend capabilities but not the other
capabilities that come at the next level, say moderator, so I keep the user in
the user group but give them user.suspend permissions at the user perm level.

Now when those two permission groups are merged (group, users) this makes up
the permissions available to a given user.

At the method level I just check for a specific permission ... so at the
method to create a user, I check for the permission user.create. At a user
update method I may check for user.update permission and maybe the user id
(dont want a user updating someone else's profile). The point is that by
looking for a specific permission rather than group(s)/role(s) I cut down the
amount of refactoring I need to do. Every scenario I've outlined has pros and
cons. For instance, what happens if I have a user that can update anyone's
profile but i'snt a superadmin or admin?? Maybe create a new permission that
give global.user.update and check for both of those. Who knows?!

Hope this gives you some ideas.

------
jlhim
Claims based authentication and authorization is all the rage.

~~~
keth
Oh, the system that will be used in .net mvc? So far I've only seen that
permissions and roles can be modeled with claims, but could that not lead to
the same problem like with roles (e.g user.hasClaim("foo") ||
user.hasClaim("bar") || ...)?

~~~
talles
For ASP.NET MVC I use to roll my own AuthorizeAttribute (just inherit it and
overload OnAuthorization). I avoid dealing with IIdentity and IPrincipal, I
find it sorta messy.

Looks like things are different on vNext though:
[http://docs.asp.net/en/latest/security/authorization/index.h...](http://docs.asp.net/en/latest/security/authorization/index.html)

------
hellbanner
Definitely checkout [https://en.wikipedia.org/wiki/Two-
factor_authentication](https://en.wikipedia.org/wiki/Two-
factor_authentication) .

If a password is compromised, data is protected.

~~~
eterm
That is authentication, which is orthogonal to authorisation.

It's very common to see a system with a reasonable authentication system which
is yet compromised by having little to no authorisation.

(A classic example of this is where after logging in as a normal user, you can
still hit the /admin/ endpoint.)

