Comments (11)
the SCIM endpoint are indeed not respecting the UMA protocol. A failed authentication attempt is simply responded with 401 unauthenticated or if successfully authenticated with a 403 forbidden if the necessary roles for accessing the resources are missing.
What is it you want to achieve by using the UMA protocol on the SCIM endpoints? I have considered the SCIM endpoints to be in an administrational function only to be used by very few authorized parties. What is your use-case?
from scim-for-keycloak.
OK, thanks for your answer.
The need is only to make SCIM to work correctly in my app, replacing an existing Gluu server with Keycloak.
The use-case is that the webapp allows users with enough privileges to list and change some user attributes through SCIM.
This boils down to calling getUser() et editUser() from this library. This library is not really flexible: it expects to be able to authenticate through UMA.
Do you know any good SCIM library for NodeJS that will work with your plugin?
from scim-for-keycloak.
unfortunately I cannot give you a good advice here, since I am using my own implementation of a SCIM client in my JS applications that is very simplified though.
import {useUserLogin} from "../provider/login-provider";
import {Optional} from "./utils";
export function useScimClient()
{
const auth = useUserLogin();
function createResource(resourcePath, resource, onSuccess, onError)
{
auth.accessToken.then(accessToken =>
{
fetch(resourcePath, {
method: "POST",
headers: {
'Content-Type': 'application/scim+json',
'Authorization': 'Bearer ' + accessToken
},
body: JSON.stringify(resource)
}).then(response =>
{
let tmpResponse = {
success: response.status === 201,
status: response.status,
resource: response.json()
};
if (tmpResponse.success)
{
new Optional(onSuccess).ifPresent(func => tmpResponse.resource.then(json => func(json)));
}
else
{
new Optional(onError).ifPresent(func => tmpResponse.resource.then(json => func(json)));
}
return tmpResponse;
});
});
}
function getResource(resourcePath, id, params, onSuccess, onError)
{
auth.accessToken.then(accessToken =>
{
let searchParams = new Optional(params).map(parameters => "?" + new URLSearchParams(parameters).toString())
.orElse("");
let url = resourcePath + new Optional(id).map(val => "/" + encodeURIComponent(val)).orElse("") + searchParams;
fetch(url, {
method: "GET",
headers: {
'Authorization': 'Bearer ' + accessToken
}
}).then(response =>
{
let tmpResponse = {
success: response.status === 200,
status: response.status,
resource: response.json()
};
if (tmpResponse.success)
{
new Optional(onSuccess).ifPresent(func => tmpResponse.resource.then(json => func(json)));
}
else
{
new Optional(onError).ifPresent(func => tmpResponse.resource.then(json => func(json)));
}
return tmpResponse;
});
});
}
async function listResources({
resourcePath,
startIndex,
count,
filter,
sortBy,
sortOrder,
attributes,
excludedAttributes,
onSuccess,
onError
} = {})
{
auth.accessToken.then(accessToken =>
{
let startIndexParam = new Optional(startIndex).map(val => "startIndex=" + val).orElse(null);
let countParam = new Optional(count).map(val => "count=" + val).orElse(null);
let filterParam = new Optional(filter).map(val => "filter=" + encodeURI(val)).orElse(null);
let sortByParam = new Optional(sortBy).map(val => "sortBy=" + encodeURI(val)).orElse(null);
let sortOrderParam = new Optional(sortOrder).map(val => "sortOrder=" + val).orElse(null);
let attributesParam = new Optional(attributes).map(val => "attributes=" + encodeURI(val)).orElse(null);
let excludedAttributesParam = new Optional(excludedAttributes).map(
val => "excludedAttributes=" + encodeURI(val)).orElse(null);
let query = Array.of(startIndexParam, countParam, filterParam, sortByParam, sortOrderParam, attributesParam,
excludedAttributesParam)
.filter(val => val != null)
.join("&");
query = new Optional(query).filter(val => val.length > 0).map(val => "?" + val).orElse("");
let requestUrl = resourcePath + query;
fetch(requestUrl, {
method: "GET",
headers: {
'Authorization': 'Bearer ' + accessToken
}
}).then(response =>
{
let tmpResponse = {
success: response.status === 200,
status: response.status,
resource: response.json()
};
if (tmpResponse.success)
{
new Optional(onSuccess).ifPresent(func => tmpResponse.resource.then(json => func(json)));
}
else
{
new Optional(onError).ifPresent(func => tmpResponse.resource.then(json => func(json)));
}
return tmpResponse;
});
});
}
function updateResource(resourcePath, id, resource, onSuccess, onError)
{
auth.accessToken.then(accessToken =>
{
fetch(resourcePath + "/" + encodeURIComponent(id), {
method: "PUT",
headers: {
'Content-Type': 'application/scim+json',
'Authorization': 'Bearer ' + accessToken
},
body: JSON.stringify(resource)
}).then(response =>
{
let tmpResponse = {
success: response.status === 200,
status: response.status,
resource: response.json()
};
if (tmpResponse.success)
{
new Optional(onSuccess).ifPresent(func => tmpResponse.resource.then(json => func(json)));
}
else
{
new Optional(onError).ifPresent(func => tmpResponse.resource.then(json => func(json)));
}
return tmpResponse;
});
});
}
function patchResource(resourcePath, id, patchBody, onSuccess, onError)
{
auth.accessToken.then(accessToken =>
{
fetch(resourcePath + "/" + encodeURIComponent(id), {
method: "PATCH",
headers: {
'Content-Type': 'application/scim+json',
'Authorization': 'Bearer ' + accessToken
},
body: JSON.stringify(patchBody)
}).then(response =>
{
let tmpResponse = {
success: response.status === 200,
status: response.status,
resource: response.json()
};
if (tmpResponse.success)
{
new Optional(onSuccess).ifPresent(func => tmpResponse.resource.then(json => func(json)));
}
else
{
new Optional(onError).ifPresent(func => tmpResponse.resource.then(json => func(json)));
}
return tmpResponse;
});
});
}
function deleteResource(resourcePath, id, requestBody, onSuccess, onError)
{
auth.accessToken.then(accessToken =>
{
fetch(resourcePath + "/" + encodeURIComponent(id), {
method: "DELETE",
headers: {
'Authorization': 'Bearer ' + accessToken
},
body: requestBody ? JSON.stringify(requestBody) : null
}).then(response =>
{
let tmpResponse = {
success: response.status === 204,
status: response.status,
resource: response.status === 204 ? undefined : response.json()
};
if (tmpResponse.success)
{
new Optional(onSuccess).ifPresent(func => func());
}
else
{
new Optional(onError).ifPresent(func => tmpResponse.resource.then(json => func(json)));
}
return tmpResponse;
});
});
}
return {
createResource: createResource,
getResource: getResource,
listResources: listResources,
updateResource: updateResource,
patchResource: patchResource,
deleteResource: deleteResource
};
}
from scim-for-keycloak.
Thanks, I will try to get it working with my app, and report back probably next week.
from scim-for-keycloak.
In the provided code, I guess that you're using the user access token to authenticate the requests.
- Does that mean that any authenticated user can do SCIM operations?
- On a more general perspective, how fine-grained are the authorization in the SCIM server? only client <-> realms? Can I ensure that 2 SCIM clients will only do SCIM operations on their users, and not the other users in the same realm?
from scim-for-keycloak.
- There is a yes and a no
- It is basically possible to grant every user access to the SCIM endpoints and I just noticed that I am not pointing out clearly enough that this is possible if the configuration is neglected.
- The details can be found here: https://scim-for-keycloak.de/documentation/administration/realm-management
- First things first. (section Enable Access to SCIM) Authentication on the SCIM endpoints are only authorized if authenticated by a client that was assigned in the "Service Provder -> Authorization" section.
- for the different resource-types (section: Resource Type Authorization) you can assign realmRoles that are necessary to get access to the different endpoints. A more detailed documentation can be found here: https://github.com/Captain-P-Goldfish/SCIM-SDK/wiki/Authentication-and-Authorization#authorization.
- The SCIM endpoints are not accessible if no Client in the authorization section was assigned (If the authentication is turned on). And users authenticating over other applications (other Clients) will not be granted access.
Thx. This question pointed out that the documentation on the website on this topic should be brought more to the foreground
Each realm has its own individual security configuration. So the configuration for realm A will have no effect for the configuration on realm B
from scim-for-keycloak.
I will try to create some graphics with enough explanations for the website
from scim-for-keycloak.
Thanks.
So, it seems that there is no way to restrict a SCIM client to manage only "its" users (the roles are bound to a Resource Type, not a resource). This implies that I need to make use of a different realm per business client (a client being in that case an OIDC client + potentially another OIDC client for AzureAD and SCIM if not using the local user DB).
This has some big impacts on our application, so I need to assess the solutions available for multi-tenancy.
from scim-for-keycloak.
Ah I see what you are getting at. This is indeed not possible with this solution. I once had a similiar problem where we wanted to share the same users for different tenants but with different access-rights on these tenants and its resources. The easiest way in my opinion is to use different realms and try to synchronize users between realms. It would probably also be possible to add an create the users in one realm and to add a delegation identity-provider from realm B to realm A so that the users from there are accepted too in realm B.
I actually never tried this its just a theory that might work :-)
from scim-for-keycloak.
Well, the problem for us is more that we wanted the authentication server to be the entry point for all users, and after authentication the user would be redirected to the correct applicative instance by the reverse-proxy. However Keycloak assumes that users will reach the correct realm login URL. So :
- Either we put all the users in the same realm, so the login URL is the same for everyone (but this is not compatible with different SCIM clients that will not be restricted to only their users). Maybe a plugin like Keycloak Organizations can help.
- Either we go multi-realm, by developping a custom component that will redirect to the correct realm, or using a plugin like Keycloak Multi-Tenancy.
from scim-for-keycloak.
FYI, we made our own implementation of NodeJS SCIM library (only the few functions needed). This work great!
Now we'll have to find a solution for the mutli-tenancy problem.
Anyway this has nothing to do with UMA compatibility, so you can close the issue if you want, as we do not need it anymore, or keep it open if you think this feature can be interesting for the project.
from scim-for-keycloak.
Related Issues (20)
- liquibase issues HOT 10
- Seed initial configuration in keycloak HOT 4
- Scim plugin behind reverse proxy that strips a path prefix makes the scim console unaccessible HOT 4
- I don't see any source for some of the classes in enterprise source zip HOT 3
- I can't seem to get authentication for scim working HOT 2
- You may want to look at integrating this for the scim user federation part
- Issue with Use with Microsoft Azure AD wiki page HOT 2
- Parallel PATCH calls result in optimistic locking exception HOT 15
- Token Expiry Handling between OKTA and Keycloak HOT 4
- ResourceTypes and Realm Assignment Tabs are empty - Remote SCIM Provider Configuration HOT 9
- Azure-Patch Operation not working HOT 4
- scim-for-keycloak plugin free version HOT 4
- Group Leave SCIM Notification is not being Received at the Sample Spring Boot Project HOT 2
- Issue with UPDATE_PASSWORD action when using scim-for-keycloak HOT 4
- SCIM Client with Zscaler Internet Access - 404: No user with the id HOT 14
- Cannot uninstall SCIM Plugin on Keycloak 24.0.3 HOT 4
- Issues with free version pre-enterprise HOT 12
- groups on users and members on groups doesn't appear to be exposed via the 2 endpoints. HOT 9
- Search by id seems to be not working HOT 5
- Something maybe missing from the scim endpoint implementation HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from scim-for-keycloak.