Questions about Authentication and Authorization plugin usage / best practices


#1

I’m in the process of porting all my authentication and authorization code to the new middleware-based plugins. It’s a pretty sizable application, with lots of complexity in terms of who is allowed to do what. So far, I’m very much liking what it’s doing for the code structure, eliminating a lot of duplication, which should make things more maintainable for the long term. (Especially if/when new roles are introduced, changes should be restricted to the policies.)

I have, however, run into a few scenarios where I’m not sure what the best solution would be. I’ve posed a couple of question on the Slack channel, but they tend to disappear quickly in the flow of discussion there. I think they’re fairly opinion-based, so not appropriate for Stack Overflow. And while I think there may be enhancements to be made to the plugins, I’m not convinced that what I’m running into are truly “issues” to be posted on Github. So, here I am.

The first area where I’m struggling is with hierarchical data, where some people have access to edit lower levels but not higher levels, but where the higher levels may control aspects of what’s available. Say that A hasMany B, and X has authority over a particular B, but not the A that it belongsTo. In order to perform operation O, I need to authorize that against resource B, but it also needs to check whether that feature is entirely disabled for A. Options as I see them:

  1. Call $this->Authorize->authorize for both A and B. Seems like unnecessary duplication, plus I have to remember to do it everywhere that I’m checking permission for that operation. Having to remember to add code like this is a sure source of bugs.
  2. Call $this->Authorize->authorize only for B, and have it check A. But that means that either I have to have A as a property of B (sometimes this makes sense, but sometimes I’ve loaded all the Bs into A and am foreaching over them) or else have the policy load the A record (which would be extra queries when it’s probably already been loaded somewhere).
  3. Stop using policies to check whether features are enabled. But that means that I have to duplicate the feature check everywhere that I check authorization, which isn’t DRY. Admittedly, checking whether a feature is enabled has nothing to do with who is logged in, so an argument can be made against this technique, but then again the plugins have split authentication apart from authorization, and I feel like disabling features is a way to block anyone from being authorized to perform those actions.

Leaning towards option 2 here, unless people have good reasons not to, or suggestions for other ways to go about it.

Second thing is that I’ve run into a few situations where it seems very useful to be able to pass context data into an authorization check. Some of these I’ve thought more carefully about and realized that passing context would be a lazy solution, while a perfectly good no-context solution also exists. But in other cases I can’t for the life of me figure out how to handle a particular authorization without context. For example, when links are emailed to people with hash codes embedded in them, permitting the recipient to take that one action without logging in. In order to authorize that action, the policy needs the resource and either an identity or a code. I could set the code as a property on the resource, but that seems very hackish. Having an optional context array that could be passed into the authorize function would solve this. (And could be used to provide the A data in my option 2 above.)

So the question here is whether or not adding an optional context parameter to the API would be considered a good thing, or if it was already considered and rejected for some reason.

Last thing (at least for now…) is that I’ve worked up a simple “AuthorizationHelper” that my views can use, to do things like if ($this->Authorize->can('edit', $record)) { // echo edit link }. Is this something that others would find useful? If so, I could submit it to the plugin for the community to benefit from.

I know that’s a lot to digest. Thanks for making it to the end. I’d give you a leftover Halloween candy if you were here. :slight_smile:


#2

Just remembered another thing that I’m not clear on. The old Auth component had configuration for setting what flash message would be shown in case of authentication failures. It’s not at all clear to me how this would work in the new system. For now, I have made my own “unauthorized handler” that does this, but I’m at a bit of a loss about how to handle “unauthenticated” errors.

Is this all something that should be added into the existing plugins, or intended to be done externally?