Grafana sso using auth.generic_oauth - single-sign-on

I have tried to implement sso in grafana using Oauth and ping id which is working as expected .
Access token provides the list of attributes, it shows all groups that i'm member of .so My workaround is to only members of the group mydomain_Monitoring_Portal can able to join sso using grafana
Used data-
{
"scope":[]
"client_id":"xxx-xxx-xxx-xxx-xxx",
" firstName": "myname",
"LastName":"lastname",
"emailAddress":"abc#gmail.com",
:memberOf":[
"CN=mydomain_Monitoring_Portal,OU=xyz,OU=SecurityGroup,DC=fiat,DC=com"
"CN=Monitoring,OU=abc,OU=Secret,DC=fiat,DC=com"
"CN=service,OU=def,OU=mount,DC=fiat,DC=com
],
"userType":"Employee",
"userId":"nb656",
"username":"n656",
"exp":167895258
},
scope : openid email profile
i had tried to implement group_attribute_path in grafana.ini file no luck. all members of the other group also able to join
groups_attribute_path = memberOf[?contains(#, 'mydomain_Monitoring_Portal') == `true`]
expecting is Only employees which is in group mydomain_Monitoring_Portal can able to join garfana using sso

Doc is your friend: https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/generic-oauth/#role-mapping
You need to configure role mapping role_attribute_path + role_attribute_strict = true which denies user access if no role or an invalid role is returned.

Related

Keycloak permission with multiple scopes where all must match

Is it possible in Keycloak to set up a permission with multiple Authorization scopes where all scopes must be true? Example: I have a resource workshop with Authorization scopes client_1, client_2, tool_1, tool_2. My user alice is allowed to use tool_1 in workshop for client_1 but not client_2 and the same for client_2 and tool_2.
I set up two scope based permissions one for client_1, tool_1 resource workshop and another for client_2, tool_2 resource workshop. The evaluation was done on keycloak Authorizations Evaluate site.
If I evaluate the setup in keycloak I get always a permit if any of the scopes or combination thereof is used. I would like to either get a deny if I request workshop for scopes client_1,tool_2 or get scope tool_1 returned, if I request workshop for scope client_1. Is this at all possible?
I'm aware that I could create combination roles/scopes, but that would be quit a lot... and setting up a new tool or client with all combination would be a nightmare... multiple scopes
would be a more elegant solution...
All scopes in this text mean Authorization scopes!

Keycloak ignores realmRoles when adding a user by rest api

When I am creating a new user by using Keycloak rest API, the application ignores the realmRoles property not assigning the role to the new user.
Here is an exemple
POST: https://localhost:8543/auth/admin/realms/quarkus/users
Body:
{
"username":"alexandre",
"enabled":true,
"emailVerified":true,
"firstName":"Alexandre",
"lastName":"Oliveira",
"email":"alexandreqogmailcom",
"credentials":[
{
"type":"password",
"value":"123456",
"temporary":false
}
],
"realmRoles":[
"user_esc"
],
"access":{
"mapRoles":true
}
Is there a way to resolve this problem or a work around ?
PS: I am using the keycloak version 12.0.1
If you are expecting that with the endpoint:
POST: https://localhost:8543/auth/admin/realms/quarkus/users
it will also create the realm roles, that will not happen, it will not create the Realm roles. To create the Realm roles you either use the Admin Console or you use the endpoint:
POST https://localhost:8543/auth/admin/realms/quarkus/roles
with the payload
{"name":"<ROLE_NAME>","description":"<DESCRIPTION>"}
if it is a non Composite Realm Role.
To assign the Realm Role to the user, after having create the user, call the endpoint:
POST: https://localhost:8543/auth/admin/realms/quarkus/users/<USER_ID>/role-mappings/realm
with the payload
[{"id":"<Role ID>","name":"<Role Name>"}]
The role ID you can get it from:
GET: https://localhost:8543/auth/admin/realms/quarkus/roles/<ROLE_NAME>
and the user ID from :
GET: https://localhost:8543/auth/admin/realms/quarkus/users/?username=<USERNAME>
I have upload the following bash scripts to automatize this process.

How can I restrict client access to only one group of users in keycloak?

I have a client in keycloak for my awx(ansible tower) webpage.
I need only the users from one specific keycloak group to be able to log in through this client.
How can I forbid all other users(except from one particular group) from using this keycloak client?
I found a solution which does not require the scripts extension or any changes on the flow.
The key for this solution are the Client Scopes. An application which wants to to authorize a user needs a scope like email or uid, right? What if you only pass them to an application if a user is in a specific group?
In the following, my client application name is App1.
Solution:
Go to your client roles (realm -> Clients -> click App1 -> Roles)
Click 'Add Role' -> enter Name (e.g. 'access') -> click 'Save'
Go to Client Scopes (realm -> Client Scopes)
Click on the scope which is needed by your client application (e.g. 'email')
Assign Client Role 'access' in 'Scope' Tab by choosing client application 'App1' in Drop Down 'Client Roles'
Now, you won't be able to log into your client application App1 anymore, as the role 'access' is not assigned to any user or group. You can try.
Let's create a new group and assign the role and a user to it.
Create Group (realm -> Groups -> Click 'New' -> enter Name 'App1 Users' -> Click Save)
In the Group, choose 'Role Mappings', choose 'App1' in Client Roles drop down, and assign the role 'access'
Assign User to 'App1 Users' (realm -> Users -> Click on User -> Groups -> Select 'App1 Users -> Click Join)
Voila, the chosen user can log into App1.
On Keycloak admin console, go to Clients menu, select your client. On the client configuration page, set Authorization Enabled: On, click Save. A new Authorization tab should appear, go to it, then to the Policies tab underneath, click Create Policy and select Group-based policy. There, you can restrict access to specific groups, assuming you have defined your groups via the Groups menu already.
--EDIT 2019-11-08--
As mentioned in comments, Client Protocol must be set to openid-connect and Access Type must be set to confidential, in order to make the Authorization Enabled option visible.
Follow-up to Allan's answer: His approach is working (for me ;-) ), though I had some struggle on how to deploy it. This is how I did it:
Bundle script in a JAR file as documented here, deploy it by copying to standalone/deployments/ (see manual link)
Enable scripts: Start Keycloak with -Dkeycloak.profile.feature.scripts=enabled
In your realm, create a new flow. Duplicate the Browser flow in a required subflow, and add the script authenticator as final (required) element:
Now add to all clients which should be restricted a client role feature:authenticate. Users which don't bear that role won't get access to the application.
If it can help, here is a script which helps implementing this behaviour for any client: if the client contains a given role (here it is called feature:authenticate), then the script checks whether the user has the role and shows an error page (a new template that needs to be deployed in the theme) if not.
AuthenticationFlowError = Java.type("org.keycloak.authentication.AuthenticationFlowError");
function authenticate(context) {
var MANDATORY_ROLE = 'feature:authenticate';
var username = user ? user.username : "anonymous";
var client = session.getContext().getClient();
LOG.debug("Checking access to authentication for client '" + client.getName() + "' through mandatory role '" + MANDATORY_ROLE + "' for user '" + username + "'");
var mandatoryRole = client.getRole(MANDATORY_ROLE);
if (mandatoryRole === null) {
LOG.debug("No mandatory role '" + MANDATORY_ROLE + "' for client '" + client.getName() + "'");
return context.success();
}
if (user.hasRole(mandatoryRole)) {
LOG.info("Successful authentication for user '" + username + "' with mandatory role '" + MANDATORY_ROLE + "' for client '" + client.getName() + "'");
return context.success();
}
LOG.info("Denied authentication for user '" + username + "' without mandatory role '" + MANDATORY_ROLE + "' for client '" + client.getName() + "'");
return denyAccess(context, mandatoryRole);
}
function denyAccess(context, mandatoryRole) {
var formBuilder = context.form();
var client = session.getContext().getClient();
var description = !mandatoryRole.getAttribute('deniedMessage').isEmpty() ? mandatoryRole.getAttribute('deniedMessage') : [''];
var form = formBuilder
.setAttribute('clientUrl', client.getRootUrl())
.setAttribute('clientName', client.getName())
.setAttribute('description', description[0])
.createForm('denied-auth.ftl');
return context.failure(AuthenticationFlowError.INVALID_USER, form);
}
I solved it like this:
Create a new role in Keycloak.
Assign this role to the group.
Create a new authentication script in Kycloak. Configure which role is allowed upon login (e.g. user.hasRole(realm.getRole("yourRoleName"))).
In the client's settings, under "Authentication Flow Overrides", choose the authentication script that was just created.
You can use this extension to restrict access to a specific group: https://github.com/thomasdarimont/keycloak-extension-playground/tree/master/auth-require-group-extension
according docu https://www.keycloak.org/docs/6.0/server_admin/#executions u have to active that feature to add some custom scripts with "add execution".
bin/standalone.sh|bat -Dkeycloak.profile.feature.scripts=enabled
#Allan solution with feature:authenticate looks good to me
I tried Allan's solution and it is working fine using Keycloak 11.0.3 but it has some cons mentioned below. Here is my solution for the authenticator script which does not grant access for users if they are not member at least one of the given groups. In such case a unique error message is shown.
AuthenticationFlowError = Java.type("org.keycloak.authentication.AuthenticationFlowError");
function authenticate(context) {
var allowed_groups = ['foo', 'bar'];
var username = user ? user.username : "anonymous";
var groups = user.getGroups();
var group_array = groups.toArray();
for (var i in group_array) {
var gn = group_array[i].getName();
if (allowed_groups.indexOf(gn) >= 0) {
LOG.info("Access granted for user '" + username + "' for being member of LDAP group '" + gn + "'");
return context.success();
}
}
LOG.info("Access denied for user '" + username + ". for not being member of any of the following LDAP groups: " + allowed_groups);
context.failure(AuthenticationFlowError.IDENTITY_PROVIDER_DISABLED, context.form().setError(
"User doesn't have the required LDAP group membership to view this page", null).createForm("error.ftl"));
return;
}
There are two minor user experience related cons with this solution worth mentioning:
When a not logged in user tries to connect to a client which access gets denied by the authenticator script the whole authentication flow is considered failure. This means the user doesn't get logged in into Keycloak despite the fact they provided the correct credentials
When a logged in user tries to connect to a client which access gets denied by the authenticator script the Keycloak login page is displayed (without showing any error message) which is deceptive as the user can have the false feeling they are not logged in
In addition if you maintain multiple clients and you need to have different groups (or roles) checked per client then you have to implement as many new authentication flows as many different checks you need. In short the solution works, but it has some disadvantages. I believe a simple feature such as restricting the access based on groups or roles is essential for an identity and access management system and should be supported natively!
2021 year - Keycloak 7.4.1.GA
I solved it like this for SAML2:
Add new Authentication flow (Just copy existing one)
Add execution "Group Access Observer" and set it as Required
Actions -> Config on Group Access Observer line
Fill group name
Go to your client and change Authentication flow to created now.
Best Regards
With Keycloak >= 13.x you may want to try the "Allow/Deny Access" authenticators with conditions. You can assign a role to a group and build the condition based on the role.
If that is not flexible enough, try out this library that I have build to solve exactly that issue.

Identity server3 : User Info endpoint returns null Claims

I am using Authorization Code flow and I am trying to get the user information using user info endpoint, but I am not getting claims. I've enabled IncludeInIdToken for some claims like name profile email and when decode Idtoken I was not able to see the above claims.
I've tried by debugging the method GetProfileDataAsync, here I found the "Name" property coming null, since I am using the AD group I need this Name property here.
I tried to save "Name" into global variable in LocalLoginAsync method and used the same in GetProfileDataAsync method. Now I have added claims to the TaskResult, but I am getting count '0' in the response
When I request for tokens by using code I am able to see the following result in Claims
and I am not able to see those claims in IdToken,
Next, time when request using user info endpoint by using access token I am not getting any Claim except "sub" or no Name property.
How can I get Claim with User Info endpoint response
I've tried by debugging the code and found the issue, since I am doing the mistake like
return Task.FormResult(identity.Claims.where(x=>context.RequestedClaimTypes.Contains(x.Types)));
instead of doing
context.IssuedClaims = identity.Claims.where(x=>context.RequestedClaimTypes.Contains(x.Types));
return Task.FromResult(0);
But still I am getting the username(Name) property null with user info endpoint..,
I am assuming that you are requesting the profile scope in your request. If this is true then username is not a valid scope and does not form part of the list of standard claims for this scope. you should rename your claim to either nickname or preferred_username

MembershipReboot with IdentityServer v3

I am having trouble extracting UserAccount properties from MembershipReboot in conjunction with Thinktecture IdentityServer. I have both up and running using the Sample repo here: https://github.com/identityserver/IdentityServer3.MembershipReboot
When I request the "openid profile" scope in an Implicit Grant Flow, I am missing a lot of the user account fields such as "given_name, middle_name", etc from the id_token and response from the userinfo endpoint. I understand this is because they need to be assigned in the GetClaimsFromAccount function.
I can see the requestedClaims come into the GetProfileDataAsync() function in the MembershipRebootUserService class and if I hover over the instance of TAccount in GetClaimsFromAccount I can see the Firstname, Lastname, etc properties appearing in the CustomUser dynamic proxy but I can't for the life of me work out how to access them and copy them into the claims collection?
More Info:
I suspect the issue is with this line:
claims.AddRange(userAccountService.MapClaims(account));
It looks like this should be converting the user account properties into claims but I dont get any back.
The way I understand it works is you add an option to your Scope object to return all of the claims for a user. IncludeAllClaimsForUser is the key property.
e.g.
new Scope
{
Enabled = true,
Name = "roles",
Type = ScopeType.Identity,
IncludeAllClaimsForUser = true,
Claims = new List<ScopeClaim>
{
new ScopeClaim("role")
}
}
My request includes the role property as well. This pulled back all the claims for the user from MR for me. My example is with Implicit flow btw.