Keycloak: Role based client log-in access restriction for users - security

I am trying to achieve fairly simple usecase of role based client application (VueJS multi-page applications) control using the keycloak.
As shown in image, I have three different roles and three different clients in single realm.
The arrow in the image represents which role can access which client.
So my main objectives are,
User with role Viewer should only be able to log-in to the Viewer Application. If the same user tries to access the Operator Application or Admin application then keycloak should simply deny this user from doing so.
The same rules should follow for users with Admin and Operator role. Users of Admin role should be able to log-in to any of these application by keycloak.
To achieve this usecase I tried following ways,
First by appropriate role mapping to users and role creation in the clients. In this case, I create realm level roles and then client level roles, then assigned appropriate roles to the users created in the user section.
Enabling the Authorization. In the policies, I removed default policy that grant all users access to the client. And create a User policy and Client policy to restrict the access to client application
Also tried with Group based authorization policy. In this case, I created a group with client role and then assigned user to these groups. And enabled them from the Authorization group policy.
But, unfortunately none of this works. Meaning my user with Viewer role can log-in to my admin application. Which is just strange.

You can do this without extensions.
Copy the desired flow (e.g. the browser flow)
Create a new sub flow (e.g. for the browser forms) and call it Access By Role and select generic as type.
For the new sub flow ensure that CONDITIONAL is selected in the flow overview.
For the new sub flow add execution Condition - User Role, make it REQUIRED and configure it:
alias: admin-role-missing
role: admin (or whatever your role is)
negate: true
Add another execution: Deny Access and make it REQUIRED as well.
The final result should look similar to this:
This will deny access if the condition "admin-role-missing" is true.
You an also learn more from the docs: explicitly-deny-allow-access-in-conditional-flows
Also, don't forget to go to your client and select the flow in the authentication overrides.

The solution proposed by #Stuck is a great start, but it has a significant flaw: When the user has already authenticated, e.g. via the standard flow of another client that did not require the role, the password form flow will never be triggered. Consequently, the user will be logged in via the cookie flow without ever checking for the role.
In other words: If there are other clients (such as the account console) that do not require the role, anyone can bypass the role check.
To fix this there needs to be an additional flow layer that includes all authentication executions, that is followed by the authorization step (no matter what authentication flow was used). The final result will look like this:

I managed almost the same problem using KeyCloak extension SPI. After the deployment you will have additional configurable "execution" in authentication flows available, named "Validate User Role".
The auth flow then look's like :
This execution must be placed after the "Username Password Form" (or other form which authenticates user) or the authentication will fail.
The source code is here :
https://github.com/ValentinChirikov/kc_user_role_validate_extension

Finally handled this at the application level as it wasn't working from keycloak end.
After the login, check for the keycloak object, inspecting on the same we can find some of the useful properties set during the configuration mentioned in the question above. The overall code looks like below,
let appName = 'your_app';
keycloak.init({ onLoad: 'login-required' }).success(function () {
// Confirm the role & authentication of the user
if (keycloak.authenticated && keycloak.tokenParsed.resource_access &&
keycloak.tokenParsed.resource_access.hasOwnProperty(appName)) {
// Continue with the app execution...
} else {
// Logout user
keycloak.logout();
}
}).error(function () {
keycloak.logout();
});
This way I managed to route unauthorized user out of the application.
The solution isn't what's required in the question asked, but it works. Although I think this should be handled at the keycloak level itself.

For anyone looking to do this in Keycloak version 20, see the screenshot. This is based on answer by #heilerich but for version 20.
NOTE: Create a new flow instead of duplicating an existing flow as it will not work.

Related

Suggestion required on how to implement a better permission system

I have a user permission system in place where i have a set of permissions within the database, for example
id
Permission
1
POST:CreateBooking
2
GET:AllBookings
And i have another table (junction table) where i put dependent permissions such as
if i want to create a Booking, i need to fetch Package details and so POST:CreateBooking requires the user to also have GET:AllPackages permission.
There's a template system in place as well, where the users can group multiple permissions together and once that template is assigned to any employee, that employee will get THAT set of permissions and it's dependent permissions.
What my nodejs system does is that when user logs in, it fetches all permissions from DB and puts it in a redis set from where on each request, the permission is checked against user id.
Is there any tool from where i can do exactly this but in an intuitive and better way?
I tried keycloak but i don't know how to cover my needs mentioned above.
Thank you
if I'm understanding correctly and trying to generify your scenario, you have a classical situation where:
You have groups which can have multiple permissions assigned;
groups can be created dinamically;
each permission correspond to a specific functionality.
So, implementing the OIDC (Open Id Connect) protocol might fit you needs. As you suggested youself you might be interested in a OpenID provider (do not reinvent the wheel) keycloak is good, you can give a look also to Vault Hashicorp.
So assuming that your backend have an already existing framework to handle security and permissions (eg. Spring Security) you can produce JWT token with the OpenId provider and check throught PreAuthorize claims (permissions) inside the token.
At the end your security layer it's just an annotation you need to insert before your method controller or before you class controller.
Behind the scenes, instead, this is what will happen:
Your user connect to your app;
User insert username and password -> the Open Id provider gives you a JWT
Your Front End app everytime it make a REST req will send also the JWT
The back end controller method called it's under authorization
Given the public keys of the OpenId provider, the validity of the token it's enstablished
If the specific permission claim it's found inside the token, the request can be elaborated else a 403 Forbidden it's returned.
OIDC - as the conceptual model/backdrop to any tool of choice, is certainly a popular/good choice, but as long as you're willing to deal with an element of complexity - the understanding required to implement an OIDC arrangement (- think of it as a possible investment - effort up front with hopefully the rewards tricking-in over time); e.g. OIDC is an ideal choice for supporting SSO (Single Sign On), especially when it comes to supporting/allowing for authentication/login via providers such as Facebook, LinkedIn & Google, etc (- as well as more Corporate OPs (OIDC Providers) including AAD/Azure AD).
Try to first step-back, and consider the possible bigger/future picture, before selecting a tool based upon only your starting/current requirements.

how to handle different rank authorization in a REST api

I am designing a REST API in which there are several user types:
disabled user
standard user
support user
admin
root
for each user, there are certain properties assigned to them in a relational database.
For example files, messages, payments, ...
Let's say I want users with higher ranks to be able to handle data related to lower ranks (e.g. an admin can modify a standard user's properties)
How can I implement it in a way that I make sure the authorization functionality is separated from the process (CRUD process).
I want something like this:
api.Get("/users/:id", authorization, processHandler)
I am using the echo framework and Golang, but I don't think that really matters. I am looking for a general solution independent of language.
Maybe you have Roles and Permissions problems, that problem has a solution when you control the access level by JWT token (for example).
You have a table with enabled system permissions and your users have associated or assigned permissions, this "association" can be managed through the Roles and Permissions manager (CRUD). So when you need to associate permissions to a user, you have to create a relationship between the permissions and the user. Then, send the new "state" in the token (JWT).
So finally you have a "middleware" on your routes like this
api.Get ("/ users /: id", authorization, processHandler)
And your authorization role has the responsibility to check if your user has the permissions to use the endpoint.

AAD B2C - Is a user authorized to change their own custom attributes?

I want to use custom attributes in AAD B2C as a shortcut for authorization. I would love to set values on users that I can use in my apis to know what they have access to do.
I see that I can create a User flow for a user to edit their own attributes, but that is the opposite of what I want.
If I don't create a user flow to edit the attribute, can I be confident that the user can't edit it on their own through Microsoft Graph or something like that? I tried doing it through graph but I can't tell if I did something wrong or if the user is not allowed to change it.
It is better not to depend on User attributes for authorization. As user profile information (user attributes)can be managed by the user self or organization level.
I don't think users can adjust their own attributes if they don't have the graph permissions in Azure. When signing in your users within your application you can allow them to access certain scopes. As long as they cant access these scopes they cannot perform any actions on the graph API. Updating user details would in this case require 'User.ReadWrite' scope assigned.

Microsoft Graph and accepted by admin required

I need to have the following workflow:
Third-party company registers new admin user for Office365 (I can't control this process).
Then pass credentials of this user to us and we configuring it (add domains, additional users etc).
I need to automate this process and this should be done by background task. So, after registration we catch this event and add message to queue and then our Azure Functions add domains, register new users etc.
But for calling Microsoft Graph admin have to accept permissions manually (go to web page and accept). Without this action token is not valid. And it breaks our automate process :(
Any way to accept it without going to web page and logging by admin for accepting?
From how you describe this process it seems like what you really need is to create an application that uses Application Only permissions that your customer grants consent too. There should be no need for them to manually create an admin user and give you credentials for this user.
And to specifically answer your question, I do not believe there is anyway to automate the user consent process via an API call. That would defeat the purpose of user consent.

What's a good way to have ordinary-user segregation but admin-user access to the same pages?

I'm using ASP.NET MVC 5 and ASP.NET Identity. I have a site with multiple users, who should only see their own data. The ordinary users access the site via URLs such as "/orders", "/orders/edit/1" etc.
I also have some "admin" users, who should be able to access all the same stuff that ordinary users see, except that they can view the data for all users. What I want to do is allow them to "impersonate" a user, and see what that user sees. So, they might access the site via URLs such as "/user-foo/orders", "/user-foo/orders/edit/1", etc.
Currently, my controllers have two variants of each action: one with a user id parameter (for admin users) and one without (for ordinary users). In the latter case, the id of the logged-in user is used. Both of these then call some shared code to render the view.
However, when rendering the view, I need to ensure that any embedded links (e.g. to an order detail page) use the correct routing form (with/without user id). That means I need to constantly check whether the user is an admin, etc. Is there a better way to do this?
I would use the claims that are available in ASP.NET Identity. Just add a claim for the impersonated user ID that will only be used if the user is an administrator. The roles are probably already in the claims. You do not need the action that passes the ID, instead add some logic that looks at the claims to see if the person is an administrator. If they are an administrator and there is a claim containing the impersonated ID then use it instead of the logged in users ID.
Here is an article that shows how to use claims with ASP.NET Identity. This shows how to set the claims during the log-in process. If you need to add a claim after the log-in process just use the SignIn method again, like this.
var AuthenticationManager = System.Web.HttpContext.Current.GetOwinContext().Authentication;
var prinicpal = (ClaimsPrincipal)Thread.CurrentPrincipal;
prinicipal.AddClaim(new Claim(MyClaimTypes.ImpersonatedUserId, impersonatedUserId));
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = persistCookie }, principal);
I normally provide the same structure for both, but with an Authorize attribute with a role specified if i am blocking access by a certain role type. Alternatively you can check the controllers User property to find out the logged on user and perform your own logic to determine what data that user has access to view.

Resources