I want to create a fairly simple role-based access control system using Keycloak's authorizaion system. The system Keycloak is replacing allows us to create a "user", who is a member of one or more "groups". In this legacy system, a user is given "permission" to access each of about 250 "capabilities" either through group membership (where groups are assigned permissions) or a direct grant of a permission to the user.
I would like to map the legacy system to keycloak authorizations.
It should be simple for me to map each "capability" in the existing system to a keycloak resource and a set of keycloak scopes. For example, a "viewAccount" capability would obviously map to an "account" resource and a "view" scope; and "viewTransaction" maps to a "transaction" resource… but is it best practice to create just one "view" scope, and use it across multiple resources (account, transaction, etc)? Or should I create a "viewAccount" scope, a "viewTransaction" scope, etc?
Similarly, I'm a little confused about permissions. For each practical combination of resource and scope, is it usual practice to create a permission? If there are multiple permissions matching a given resource/scope, what does Keycloak do? I'm guessing that the intention of Keycloak is to allow me to configure a matrix of permissions against resources and scopes, so for example I could have permission to access "accounts" and permission for "view" scope, so therefore I would have permission to view accounts?
I ask because the result of all this seems to be that my old "viewAccount" capability ends up creating an "Account" resource, with "View" scope, and a "viewAccount" permission, which seems to get me back where I was. Which is fine, if it's correct.
Finally, obviously I need a set of policies that determine if viewAccount should be applied. But am I right that this means I need a policy for each of the legacy groups that a user could belong to? For example, if I have a "helpdesk" role, then I need a "helpdesk membership" policy, which I could then add to the "viewAccount" permission. Is this correct?
Thanks,
Mark
Best Answer
I know I'm 2+ years late but I figure I'd share what I know and hopefully alleviate some pain for future readers. Full transparency- I am by no means a Keycloak/OAuth/OIDC expert and what I know is mostly from reading the docs, books, good ol' YouTube and playing around with the tool.
This post will be comprised of two parts:
Keycloak 8.0.0
.Part I
Some terminology before we get started:
Resource-Based
permissions, you apply it directly to your resourceScoped-Based
permission, you apply it to your scope(s) or scope(s) and resource.Scopes represent a set of rights at a protected resource. In your case, you have 2 resources:
account
andtransaction
, so I would lean towards the second approach.In the long run, having a global
view
scope associated with all your resources (e.g.account
,transaction
,customer
,settlement
...) makes authorization difficult to both manage and adapt to security requirement changes.Here are a few examples that you can check out to get a feel for design
Do note though - I am not claiming that you shouldn't share scopes across resources. Matter of fact,
Keycloak
allows this for resources with the sametype
. You could for instance need bothviewAccount
andviewTransaction
scope to read a transaction under a given account (after all you might need access to the account to view transactions). Your requirements and standards will heavily influence your design.Apologies, I don't fully understand the question so I'll be a bit broad. In order to grant/deny access to a
resource
, you need to:scope
orresource
(or both)for policy enforcement to take effect. See Authorization Process.
How you go about setting all this up is entirely up to you. You could for instance:
Define individual policies, and tie each policy under the appropriate permission.
Better yet, define individual policies, then group all your related policies under an
aggregated
policy (a policy of policies) and then associate that aggregated policy with thescope-based
permission. You could have thatscoped-based
permission apply to both the resource and all its associated scope.Or, you could further break apart your permissions by leveraging the two separate types. You could create permissions solely for your resources via the
resource-based
permission type, and separately associate other permissions solely with a scope via thescope-based
permission type.You have options.
This depends on
Decision Strategy
Decision Strategy
Logic
value.The
Logic
value is similar with Java's!
operator. It can either bePositive
orNegative
. When theLogic
isPositive
, the policy's final evaluation remains unchanged. When itsNegative
, the final result is negated (e.g. if a policy evaluates to false and itsLogic
isNegative
, then it will betrue
). To keep things simple, let's assume that theLogic
is always set toPositive
.The
Decision Strategy
is what we really want to tackle. TheDecision Strategy
can either beUnanimous
orAffirmative
. From the docs,Let's use an example to better understand the above. Suppose you have a resource with 2 permissions and someone is trying to access that resource (remember, the
Logic
isPositive
for all policies). Now:Permission One
has aDecision Strategy
set toAffirmative
. It also has 3 policies where they each evaluate to:true
false
false
Since one of the policies is set to
true
,Permission One
is set totrue
(Affirmative - only 1 needs to betrue
).Permission Two
has aDecision Strategy
set toUnanimous
with 2 policies:true
false
In this case
Permission Two
isfalse
since one policy is false (Unanimous - they all need to betrue
).Decision Strategy
is set toAffirmative
, access to that resource would be granted becausePermission One
istrue
. If on the other hand, the resource server'sDecision Strategy
is set toUnanimous
, access would be denied.See:
We'll keep revisiting this. I explain how to set the resource sever's
Decision Strategy
in Part II.The short answer is yes. Now, let's expand on this a bit :)
If you have the following scenario:
Decision Strategy
set toUnanimous
orAffirmative
account/{id}
resource istrue
view
scope istrue
You will be granted access to view the account.
true
+true
is equal totrue
under theAffirmative
orUnanimous
Decision Strategy
.Now if you have this
Decision Strategy
set toAffirmative
account/{id}
resource istrue
view
scope isfalse
You will also be granted access to view the account.
true
+false
istrue
under theAffirmative
strategy.The point here is that access to a given resource also depends on your setup so be careful as you may not want the second scenario.
I'm not sure how Keycloak behaved 2 years ago, but you can specify a Group-Based policy and simply add all your groups under that policy. You certainly do not need to create one policy per group.
Pretty much. There are many ways you can set this up. For instance, you can:
/account/{id}
) and associate it with theaccount:view
scope.helpdesk
role under that policyScope-Based
permission calledviewAccount
and tie it withscope
,resource
andpolicy
We'll set up something similar in Part II.
Part II
Keycloak has a neat little tool which allows you test all your policies. Better yet, you actually do not need to spin up another application server and deploy a separate app for this to work.
Here's the scenario that we'll set up:
stackoverflow-demo
bank-api
client under that realm/account/{id}
for that clientaccount/{id}
will have theaccount:view
scopebob
under the new realmbank_teller
,account_owner
anduser
bob
with any roles. This is not needed right now.Role-Based
policies:bank_teller
andaccount_owner
have access to the/account/{id}
resourceaccount_owner
has access to theaccount:view
scopeuser
does not have access to the resource or scopeEvaluate
tool to see how access can be granted or denied.Do forgive me, this example is unrealistic but I'm not familiar with the banking sector :)
Keycloak setup
Download and run Keycloak
Create initial admin user
http://localhost:8080/auth
Administration Console
linkVisit Getting Started for more information. For our purposes, the above is enough.
Setting up the stage
Create a new realm
master
realm and click on theAdd Realm
button.stackoverflow-demo
as the name.Create
.stackoverflow-demo
instead of themaster
realm.See Creating a New Realm
Create a new user
Users
link on the leftAdd User
buttonusername
(e.g.bob
)User Enabled
is turned onSave
See Creating a New User
Create new roles
Roles
linkAdd Role
bank_teller
,account_owner
anduser
Again, do not associate your user with the roles. For our purposes, this is not needed.
See Roles
Create a client
Clients
linkCreate
bank-api
for theClient ID
Root URL
enterhttp://127.0.0.1:8080/bank-api
Save
Client Protocol
isopenid-connect
Access Type
toconfidential
Authorization Enabled
toOn
Save
. A newAuthorization
tab should appear at the top.Authorization
tab and thenSettings
Decision Strategy
is set toUnanimous
Decision Strategy
See:
Create Custom Scopes
Authorization
tabAuthorization Scopes
>Create
to bring upAdd Scope
pageaccount:view
in the name and hit enter.Create "View Account Resource"
Authorization
link aboveResources
Create
View Account Resource
for both theName
andDisplay name
account/{id}
for theURI
account:view
in theScopes
textboxSave
See Creating Resources
Create your policies
Authorization
tab, click onPolicies
Role
from the theCreate Policy
dropdownName
section, typeOnly Bank Teller and Account Owner Policy
Realm Roles
select both thebank_teller
andaccount_owner
roleLogic
is set toPositive
Save
Policies
linkRole
again from theCreate Policy
dropdown.Only Account Owner Policy
for theName
Realm Roles
selectaccount_owner
Logic
is set toPositive
Save
Policies
link at the top, you should now see your newly created policies.See Role-Based Policy
Do note that Keycloak has much more powerful policies. See Managing Policies
Create Resource-Based Permission
Authorization
tab, click onPermissions
Resource-Based
View Account Resource Permission
for theName
Resources
typeView Account Resource Permission
Apply Policy
selectOnly Bank Teller and Account Owner Policy
Decision Strategy
is set toUnanimous
Save
See Create Resource-Based Permissions
Phew...
Evaluating the Resource-Based permission
Authorization
tab, selectEvaluate
User
enterbob
Roles
selectuser
Resources
selectView Account Resource
and clickAdd
View Account Resource with scopes [account:view]
to see the results and you should seeDENY
.Only Bank Teller and Account Owner Policy
. Let's test this to make sure this is true!Back
link right above the evaluation resultaccount_owner
and click onEvaluate
. You should now see the result asPERMIT
. Same deal if you go back and change the role tobank_teller
See Evaluating and Testing Policies
Create Scope-Based Permission
Permissions
sectionScope-Based
this time under theCreate Permission
dropdown.Name
, enterView Account Scope Permission
Scopes
, enteraccount:view
Apply Policy
, enterOnly Account Owner Policy
Decision Strategy
is set toUnanimous
Save
See Creating Scope-Based Permissions
Second test run
Evaluating our new changes
Authorization
sectionEvaluate
bob
bank_teller
View Account Resource
and clickAdd
Evaluate
and we should getDENY
.bank_teller
has access to theresource
but not thescope
. Here one permission evaluates to true, and the other to false. Given that the resource server'sDecision Strategy
is set toUnanimous
, the final decision isDENY
.Settings
under theAuthorization
tab, and change theDecision Strategy
toAffirmative
and go back to steps 1-6 again. This time, the final result should bePERMIT
(one permission is true, so final decision is true).Decision Strategy
back toUnanimous
. Again, go back to steps 1 through 6 but this time, set the role asaccount_owner
. This time, the final result is againPERMIT
which makes sense, given that theaccount_owner
has access to both theresource
andscope
.Neat :) Hope this helps.