-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Endpoint authorization #10
base: main
Are you sure you want to change the base?
Conversation
|
Oops, I missed the CLA requirement. Working on that internally now. |
20221103-endpoint-authz.md
Outdated
- It requires setup outside of just writing a configuration file. | ||
- It couples the authorization system to other parts of OpenFGA. | ||
- There is a chicken-and-egg problem; you cannot protect the system prior to starting it up. | ||
- OpenFGA would be prescribing an authorization model that might not work for the operator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a note on this - the primary proposal mentioned herein is also very prescriptive (scopes/preshared keys per endpoint). A benefit of this alternative approach is that the model can be more fine grained and dynamic instead of statically prescribed. For example, with this approach the operators could potentially define their system access model using OpenFGA modeling.
@jbrooks2-godaddy thanks for the very detailed and thorough RFC submission 👏 ! This is awesome. We've discussed per-endpoint authorization internally on the team various times and don't yet have it planned in our immediate roadmap (sometime next year was the goal), but we do understand the significance of it and how beneficial it could be. I like the pragmatic approach to your proposal and I think this would be a reasonable approach to implementing it, however I do also believe that using OpenFGA within OpenFGA to enforce authorizations per-endpoint could be much more flexible and may cater to a larger audience of OpenFGA operators. For that reason I don't think we should discredit that as a viable option and it should be explored. |
keys: | ||
- cool-key-1 | ||
- cool-key-2 | ||
authz: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So that we're not mixing authn
with authz
I may recommend we introduce a separate block of config for the per-endpoint configuration. May I suggest an endpoints
config?
authn:
method: oidc
oidc:
issuer: ...
audience: ...
endpoints:
- openfga.v1.OpenFGAService/Write
scopes:
- openfga:write
- openfga.v1.OpenFGAService/Read
scopes:
- openfga:read
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it makes sense to split it out. I had it nested under the oidc
/preshared
block because the means of authorization differs between the two.
I suppose that the authorization block could be authn-type-agnostic, and we could enforce restrictions during config validation. For example, if you accidentally write a keys
block when the authn
method is oidc
, the config schema could allow it but we can catch that in validation:
authz:
global:
scopes:
- openfga:read
keys:
- cool-key-1 // specifying a key in addition to a scope would result in a config validation error
endpoints:
- openfga.v1.OpenFGAService/Write
scopes:
- openfga:write
Alternatively, if we want distinct schemas for each method type we could have blocks similar to authn
:
authz:
oidc:
global:
scopes:
- openfga:read
endpoints:
- openfga.v1.OpenFGAService/Write
scopes:
- openfga:write
preshared:
<empty here, but would be filled out when authn is preshared>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea of having distinct schemas for each authn method
authn:
method: oidc
oidc:
...
permissions:
- endpoint: openfga.v1.OpenFGAService/Write
oidc:
scopes:
- openfga:write
authn:
method: preshared
preshared:
...
permissions:
- endpoint: openfga.v1.OpenFGAService/Read
preshared:
keys:
- key1
It'd be nice if we could omit the usage of the term 'global' as well. It'd be nice to just specify a top-level requirement that is used unless the endpoint is specifically overridden. Will have to 🤔 about how that may best look?
Thanks for the feedback @jon-whit. I'll spend some time exploring an OpenFGA-driven solution in more detail. At a high level, though, do you think that a configuration-based approach like this could live alongside an OpenFGA-driven approach as two alternative options for adding authorization? |
I definitely think we could support both, though only one would be configured at any point in time. That being said, maintaining the code for two configuration streams for per-endpoint permissions in OpenFGA seems like it would be a lot of effort and introduces a larger API surface footprint as well (more things to try and maintain backwards and forwards compatibility with). So if we can make a choice on something that fits more usage patterns up front it'll reduce the maintainence footprint on the core OpenFGA maintenance team. |
* If the implementation is simple, it will not support all authorization use-cases (i.e. combinations of allow and deny). | ||
* If the implementation allows for more complex authorization schemes, it will be more difficult to configure and more prone to implementation bugs. | ||
|
||
# Alternatives |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to consider not to include subjects
?
Given you'll need a OIDC server, you can take advantage of the ability of the server to assign scopes per client_id.
I'm not familiar with how this works with other OIDC servers but I know Auth0 supports it. Do you know if other OIDC servers do not have this feature?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that would be reasonable! I included the subjects as an option as the code currently extracts both the subject and the scopes. I think that most operators would likely authorize based on scopes, I think we could leave the subjects
portion out unless someone asks for it.
@jon-whit in 2316dd5 I explored the possibilities of OpenFGA-driven authorization in more detail. It's a bit loose (schema is not the best, need to think through some implementation details) as I wanted to get some early feedback - if the direction makes sense I think I'll extract it out into a separate RFC dedicated to the approach. Let me know what you think! |
@jbrooks2-godaddy I'm curious if you've considered per-store authorization as part of this discovery as well? Your proposal introduces support for per-endpoint authorization, which will allow operators to restrict certain rights to the top-level endpoint. However, OpenFGA largely operates on the abstraction of an OpenFGA Store, which scopes/isolates models and tuples. OpenFGA Stores enable multi-tenancy effectively. Another very valid use case for authorization is to restrict who can perform certain actions (Check, Write, Expand, Read, etc...) on an individual store. Do you have any use cases for that in your workflows? If you were to extend this proposal to support per-store authorization do you have some design ideas for how that would be done? An approach I'd like to explore when I have a bit more time is bootstrapping OpenFGA with an internal store (call it
You'd run OpenFGA with something like
which would bootstrap the model above and add the following tuples to the bootstrapped
This would allow alice@example.com and bob@example.com to bootstrap OpenFGA Stores and start assigning permissions to other subjects in the system. Let's pretend that Alice then creates a new store (
Now if Peter goes to manage authorization models, write tuples, or read tuples for Another variation to the approach I mention above could include allowing the developer to specify a model that OpenFGA will use to enforce per-store authorization. To do this you'd have to implement an authorization model interface, but the implementation of that interface is up to you. For example, let's say that the OpenFGA server requires you to define a model but you can define the model however you like so long as it contains the following definitions
Essentially you just have to make sure that the following relationships type definitions are met but the expressions are up to you:
This approach would allow operators to customize what the relation rewrite rules (expressions) are defined as for their unique use case(s) but guarantees a stable interface that the OpenFGA server can still use. OpenFGA will still issue internal checks of the form Another option altogether here that I have been toying with the idea of is a pluggable module system. There are various behaviors of OpenFGA that we may want the community to be able to "plugin". For example, authentication and authorization are good candidates for this. One developer may have different authentication/authorization requirements for their deployment of OpenFGA. I think OpenFGA should have some baseline support for standardized authentication (like we do today with preshared keys and OIDC authn) and authorization mechanisms, but it would also be nice if developers could register their own plugins with the OpenFGA server at runtime and use the middleware(s) provided by those plugins to handle their custom use cases. There's another option here where we could expose an This would allow us to implement both your solution and my proposed solutions without necessarily introducing anything into the mainline of development in in the OpenFGA project (until we see a strong reason to do so). For example |
Lots of great ideas here @jon-whit! My proposal as written supports per-store authorization, but adding that precludes more fine-grained authorization within the store. That's a gap, some improvement is needed there. In order to support both the per-store authorization and more fine-grained dynamic authorization based on the request parameters as described I think that a multi-layered approach would be needed. Something like your bootstrapped approach where a subject is granted access to a store, and then further per-endpoint configs where a subject is restricted based on the type of request they are trying to make. Overall I'm less enthusiastic now about reaching that far into a request to authorize based on parameters like the object type... It feels a bit too complex. Both of your ideas are interesting, but I think that the plugin system is the most appealing. So far we've discussed:
I see some value in all of these approaches! An operator who is running OpenFGA as a pure platform provider will care a lot more about strong multi-tenancy than an operator running in-house trying to add some additional checks on internal clients. The plugin model would let all of these options co-exist while also making it easy for adopters to customize according to their needs. In addition to authN/authZ I think that the storage layer is a natural candidate for a plugin system. It's easy enough to implement the |
Howdy! Was anything further proposed or developed on this? I def love the idea of being able to "bootstrap" OpenFGA with a store and being able to provide initial tuples based on that. The idea to have default "Authorization Model", but be able to provide a custom one is very appealing, as well as the ability to potentially provide OIDC Claims as "contextual tuples" with a well defined mapping. This is sort how OPA handles it (https://www.openpolicyagent.org/docs/latest/security/#authentication-and-authorization) though obviously it has both more and less flexibility as a policy language to be able to do that. That said, a plugin enabling that behavior would also be a nice compromise. As an addendum to the above, has the possibility of multiple authenticators been spoken of? |
Hey @RichiCoder1, I unfortunately did not pursue the approaches outlined here any further - we ended up implementing authorization in another layer due to time constraints. No plans to dig in further now (I suppose I should close this PR?), but happy to help review any other proposed approaches |
Description
This RFC proposes a configuration schema for restricting access to authorized clients.
Rendered version
Click here to view the rendered markdown
References
Review Checklist
I have added documentation for new/changed functionality in this PR or in a PR to openfga.dev [Provide a link to any relevant PRs in the references section above](N/A at this time, this will come with the implementation)main
I have added tests to validate that the change in functionality is working as expectedN/A