I know that there is admin APIs to get the list of users which returns the user representation array.
GET /admin/realms/{realm}/groups/{id}/members
returns
https://www.keycloak.org/docs-api/2.5/rest-api/index.html#_userrepresentation
but is there a way to get users by custom attribute ?
This is not possible by default, but Keycloak offers the possibility to extend its functionalities via a system of Service Provider Interfaces which is very easy to implement.
Here is an example of new route that allows to search by custom attributes :
public class SearchByAttributeResourceProvider implements RealmResourceProvider {
private KeycloakSession session;
public SearchByAttributeResourceProvider(KeycloakSession session) {
this.session = session;
}
#Override
public Object getResource() {
return this;
}
#GET
#Path("search-by-stuff/{stuffValue}")
#Produces({MediaType.APPLICATION_JSON})
public List<UserRepresentation> getUsersByStuff(#PathParam("stuffValue") String stuffValue) {
return session
.users()
.searchForUserByUserAttribute("stuff", stuffValue, session.getContext().getRealm())
.stream()
.map(userModel -> ModelToRepresentation.toRepresentation(session, session.getContext().getRealm(), userModel))
.collect(toList());
}
#Override
public void close() {
}
}
You'll find more details here : https://www.keycloak.org/docs/latest/server_development/index.html#_extensions_rest
This is enabled out of the box from Keycloak version 15.1.0
Using GET /{realm}/users API, parameter q is introduced: A query to search for custom attributes, in the format 'key1:value2 key2:value2'
curl 'http://{{keycloak_url}}/auth/admin/realms/{{realm}}/users?q=phone:123456789'
You can also combine several attributes within this parameter using space ' ' delimiter
curl 'http://{{keycloak_url}}/auth/admin/realms/{{realm}}/users?q=phone:123456789 country:USA'
Docs: https://www.keycloak.org/docs-api/15.1/rest-api/index.html#_users_resource
With latest version of keycloak (18.01), we have api in
#GET
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
List<UserRepresentation> searchByAttributes(#QueryParam("q") String searchQuery);
The query param is of format 'key:value' . Using this we can get list of all users by custom attributes
Update: The /auth path was removed starting with Keycloak 17 Quarkus distribution. So you might need to remove the /auth from the endpoint calls presented on this answer.
Keycloak versions from 15.1.0 upwards
Although not mentioned on the release notes it is possible after Keycloak version 15.1.0 (as pointed out by #Darko) to search users by custom attributes, introduced with this commit. As one can now see on the GET /{realm}/users endpoint of the Keycloak Admin Rest API:
Form example:
curl 'https://${KEYCLOAL_HOST}/auth/admin/realms/${REALM_NAME}/users?q=employeeNumber:444555'
Keycloak versions before 15.1.0
For version before 15.1.0, out-of-the-box you can use the Keycloak Admin API endpoint:
GET /{realm}/users
one can read that :
Get users Returns a list of users, filtered according to query
parameters
those (optional) query parameters being:
briefRepresentation (boolean);
email (string);
first (string);
firstName (string);
lastName (string);
max (Maximum results size (defaults to 100)) (integer);
search (A String contained in username, first or last name, or email);
username (string).
As you can see you cannot search for custom attributes. A not so great solution is to get all the users (max=-1), and filter afterwards by the custom attribute.
The other option (pointed out by #Lucas) is to extend Keycloak functionality by adding your own custom Service Provider Interfaces (SPI) and adding your custom endpoint. There you can take advantage of the searchForUserByUserAttribute method from the UserQueryProvider interface.
Step-by-step with Keycloak Admin API versions from 15.1.0 upwards
To use the Keycloak Admin REST API, you need an access token from a user with the proper permissions. For now, I will be using the admin user from the master realm, and later explain how to use another user:
curl “https://${KEYCLOAK_HOST}/auth/realms/master/protocol/openid-connect/token” \
-d "client_id=admin-cli" \
-d "username=${ADMIN_NAME}” \
-d "password=${ADMIN_PASSWORD}" \
-d "grant_type=password"
You get a JSON response with the admin's token. Extract the value of property access_token from that response. Let us save it in the variable $ACCESS_TOKEN for later reference.
To get the list of users from your realm $REALM_NAME with a given set of attributes (i.e., ${ATTRIBUTES}).
curl -X GET “https://${KEYCLOAK_HOST}/auth/admin/realms/${REALM_NAME}/users?q=${ATTRIBUTES}” \
-H "Content-Type: application/json" \
-H "Authorization: bearer ${ACCESS_TOKEN}”
I have the aforementioned steps coded in the script getUserByAttributes.sh on my GitHub repo for those that are interested. An example :
sh getUserByAttributes.sh localhost:8080 admin admin test_realm 'employeeNumber:4445 something:a'
Assigning the proper user permissions
For those that do not want to get an access token from the master admin user, you can get it from another user but that user needs the permission manage-users from the realm-management client. For that you can check this answer on how to do it
Current Keycloak API version is 4.8 and there is API:
Get users Returns a list of users, filtered according to query parameters
GET /{realm}/users
See doc: https://www.keycloak.org/docs-api/4.8/rest-api/index.html#_users_resource
Only this "search" is available from the API. If you need search by user attributes, then you need to implement it in your own code.
You can filter keycloak users using their custom attributes by passing the 'q' get parameter:
IN SUMMARY:
<get-users-url>?q=key1:value1 key2:value2
EXAMPLE:
curl --location --request GET 'http://localhost:8080/admin/realms/realm-name/users?q=key1:value1 key2:value2' \
--header 'Authorization: Bearer <your-token>'
Related
Is there a way to get the website link via github api? Currently I don't see it when using the following:
curl
-H "Accept: application/vnd.github.v3+json"
https://api.github.com/orgs/ORG/repos
I can get the topics and the description text but not the website (winterbe.com/projects...).
You'll need to use the GraphQL API, and you're looking for the homepageUrl field under a given repo. For example, the query below will give all public repositories for a given user, and for each repository, it will give its name, description, and homepage:
query MyQuery {
repositoryOwner(login: "username") {
repositories(orderBy: {field: CREATED_AT, direction: DESC}, first: 100, privacy: PUBLIC) {
nodes {
name
description
homepageUrl
}
}
}
}
Unlike the REST api, the GraphQL api requires authentication. You can tinker around with the query here.
I have a Micronaut microservice that handles authentication via JsonWebTokens (JWT) from this guide.
Now I'd like to extend this code. The users in my app have some extra attributes such as email, adress, teamId etc. I have all users in the database.
How do I know in the backend controller method which user corresponds to the JWT that is sent by the client?
The guide contains this example code for the Micronaut REST controller:
#Secured(SecurityRule.IS_AUTHENTICATED)
#Controller
public class HomeController {
#Produces(MediaType.TEXT_PLAIN)
#Get
public String index(Principal principal) {
return principal.getName();
}
}
I know that I can get the name of the principal, ie. the username from the HttpRequest. But how do I get my additional attributes?
(Maybe I misunderstand JWT a bit???)
Are these JWT "claims" ?
Do I need to load the corresponding user by username from my DB table?
How can I verify that the sent username is actually valid?
edit Describing my usecase in more detail:
Security requirements of my use case
Do not expose valid information to the client
Validate everything the client (a mobile app) sends via REST
Authentication Flow
default oauth2 flow with JWTs:
Precondition: User is already registerd. Username, hash(password) and furhter attributes (email, adress, teamId, ..) are known on the backend.
Client POSTs username and password to /login endpoint
Client receives JWT in return, signed with server secret
On every future request the client sends this JWT as bearer in the Http header.
Backend validates JWT <==== this is what I want to know how to do this in Micronaut.
Questions
How to validate that the JWT is valid?
How to and where in which Java class should I fetch additional information for that user (the additional attributes). What ID should I use to fetch this information. The "sub" or "name" from the decoded JWT?
How do I load a “user” in a micronaut backend when JWT is provided?
I am reading this as you plan to load some kind of User object your database and access it in the controller.
If this is the case you need to hook into the place where Authentication instance is created to read the "sub" (username) of the token and then load it from the database.
How to extend authentication attributes with more details ?
By default for JWT authentication is created using JwtAuthenticationFactory and going more concrete default implementation is DefaultJwtAuthenticationFactory. If you plan to load more claims this could be done by replacing it and creating extended JWTClaimsSet or your own implementation of Authentication interface.
How do I access jwt claims ?
You need to check SecurityService -> getAuthentication() ->getAttributes(), it returns a map of security attributes which represent your token serialised as a map.
How to validate that the JWT is valid?
There is a basic validation rules checking the token is not expired and properly signed, all the rest validations especially for custom claims and validating agains a third parties sources have to be done on your own.
If you plan to validate your custom claims, I have already open source a project in this scope, please have a look.
https://github.com/traycho/micronaut-security-attributes
How to extend existing token with extra claims during its issuing ?
It is required to create your own claims generator extending JWTClaimsSetGenerator
#Singleton
#Replaces(JWTClaimsSetGenerator)
class CustomJWTClaimsSetGenerator extends JWTClaimsSetGenerator {
CustomJWTClaimsSetGenerator(TokenConfiguration tokenConfiguration, #Nullable JwtIdGenerator jwtIdGenerator, #Nullable ClaimsAudienceProvider claimsAudienceProvider) {
super(tokenConfiguration, jwtIdGenerator, claimsAudienceProvider)
}
protected void populateWithUserDetails(JWTClaimsSet.Builder builder, UserDetails userDetails) {
super.populateWithUserDetails(builder, userDetails)
// You your custom claims here
builder.claim('email', userDetails.getAttributes().get("email"));
}
}
How do I access jwt claims ?
If you want to access them from the rest handler just add io.micronaut.security.authentication.Authentication as an additional parameter in the handling method. Example
#Get("/{fooId}")
#Secured(SecurityRule.IS_AUTHENTICATED)
public HttpResponse<Foo> getFoo(long fooId, Authentication authentication) {
...
}
I found a solution. The UserDetails.attributes are serialized into the JWT. And they can easily be set in my CustomAuthenticationProviderclass:
#Singleton
#Slf4j
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Publisher<AuthenticationResponse> authenticate(
#Nullable HttpRequest<?> httpRequest,
AuthenticationRequest<?, ?> authenticationRequest)
{
// ... autenticate the request here ...
// eg. via BasicAuth or Oauth 2.0 OneTimeToken
// then if valid:
return Flowable.create(emitter -> {
UserDetails userDetails = new UserDetails("sherlock", Collections.emptyList(), "sherlock#micronaut.example");
// These attributes will be serialized as custom claims in the JWT
Map attrs = CollectionUtils.mapOf("email", email, "teamId", teamId)
userDetails.setAttributes(attrs);
emitter.onNext(userDetails);
emitter.onComplete();
}, BackpressureStrategy.ERROR);
}
}
And some more pitfalls when validating the JWT in the backend
A JWT in Micronaut MUST contain a "sub" claim. The JWT spec does not require this, but Micronaut does. The value of the "sub" claim will become the username of the created UserDetails object.
If you want to load addition attributes into these UserDetails when the JWT is validated in the backend, then you can do this by implementing a TokenValidator. But (another pitfal) then you must set its ORDER to a value larger than micronaut's JwtTokenValidator. Your order must be > 0 otherwise your TokenValidator will not be called at all.
When I call http://localhost:8080/nuxeo/api/v1/id/bad6cbc5-b75f-4373-981f-6908cec66779?enrichers.document=children endpoint it returns all child elements include deleted elements. But I need to get only active elements and I think I should add isTrashed=false query to endpoint. But http://localhost:8080/nuxeo/api/v1/id/bad6cbc5-b75f-4373-981f-6908cec66779?enrichers.document=children&isTrashed=false does not any effect. How can I get only active child elemets from nuxeo server using rest api?
/nuxeo/api/v1/id endpoint with children enricher does not support this kind of filtering.
I see two options:
Implement own enricher which will support filtering of trashed documents. children enricher is implemented by org.nuxeo.ecm.core.io.marshallers.json.enrichers.ChildrenJsonEnricher class so you can inspire there how to do that.
Use different end point with page provider which supports filtering of trashed documents:
/nuxeo/api/v1/search/pp/advanced_document_content/execute?&ecm_parentId=bad6cbc5-b75f-4373-981f-6908cec66779&ecm_trashed=false
Benefits of the second option:
paging - simply add currentPageIndex=0&offset=0&pageSize=20 to the query
properties - you can define what properties do you need by adding of header: properties:dublincore,common,uid,file
enrichers - it means that you can use enrichers for each child and receive for example permission or thumbnail URL for each child. To do that add this header: enrichers-document: thumbnail, permissions
Example curl call:
curl -X GET -u Administrator:Administrator \
-H "properties:dublincore,common,uid,file" \
-H "enrichers-document: thumbnail, permissions" \
"http://localhost:8080/nuxeo/api/v1/search/pp/advanced_document_content/execute?&ecm_parentId=bad6cbc5-b75f-4373-981f-6908cec66779&ecm_trashed=false" | jq
How to get the last login session details of a user in Keycloak using keycloak rest endpoints?
Example:
builder.append(OAuth2Constants.AUDIENCE+"="+clientId+"&");
builder.append(OAuth2Constants.GRANT_TYPE+"="+OAuth2Constants.UMA_GRANT_TYPE+"&");
headers.put("Content-Type", "application/x-www-form-urlencoded");
headers.put("Authorization", "Bearer "+accessToken);
//String keycloakURL = keyCloakCFGBean.getCreateRefreshSession();
String keycloakURL="http://10.10.8.113:10004/auth/realms/{realm}/protocol/openid-connect/token";
keycloakURL = keycloakURL.replace("{realm}", realmName);
URL url = new URL(keycloakURL);
httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setUseCaches(false);
httpURLConnection.setDoInput(true);
httpURLConnection.setRequestMethod("POST");
httpURLConnection.setDoOutput(true);
if (headers != null && headers.size() > 0) {
Iterator<Entry<String, String>> itr = headers.entrySet().iterator();
while (itr.hasNext()) {
Entry<String, String> entry = itr.next();
httpURLConnection.setRequestProperty(entry.getKey(), entry.getValue());
}
}
outputStreamWriter = new OutputStreamWriter(httpURLConnection.getOutputStream(), StandardCharsets.UTF_8);
outputStreamWriter.write(builder.toString());
outputStreamWriter.flush();
So there are a couple of scenarios here. All of this information assumes that you have an appropriate bearer token that you are sending in the header of the request for authentication/authorisation, and requires that you have sufficient admin privileges in the Keycloak realm.
I've not gone into detail in terms of the precise code you write in a particular language, but hopefully the instructions are clear in terms of what you need your code to do.
Sessions
If you are interested in ACTIVE user sessions specifically, you can use the API endpoint as described at: https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_getsessions
That is:
GET /{realm}/users/{id}/sessions
e.g. the full URL would be:
https://{server}/auth/admin/realms/{realm}/users/{id}/sessions
In the response there will be a property called lastAccess that will contain a number that is the usual UNIX milliseconds since 1/1/1970. If you take that number, you can then parse it in your language of choice (Java from the looks of it?) to get the date/time in the format that you require.
All Logins
However I suspect what you really want is to look at the last login across all of the stored information in Keycloak, not just active user sessions, so for that you need to look for the Realm EVENTS. Note that Keycloak only stores events for a certain amount of time, so if it's older than that then you won't find any entries. You can change how long events are stored for in the events config page of the realm admin console.
To get all realm events you call the endpoint mentioned here: https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_getevents (Search for "Get events Returns all events, or filters them based on URL query parameters listed here" if the link doesn't take you straight there).
i.e.
GET /{realm}/events
e.g. the full URL would be: https://{server}/auth/admin/realms/{realm}/events
You will need to filter the results based on "type" (i.e. so that you only have events of type "LOGIN"), and if you want to check a specific user you would also want to filter the results on userId based on the ID of that user account.
You can perform both of these filters as part of the request, to save you having to get the full list of events and filter it client-side. To filter in the request you do something like the following:
https://{server}/auth/admin/realms/{realm}/events?type=LOGIN&user={id}
From the resultant JSON you can then get the result with the highest value of the time property, that represents that login event. The time property will be a UNIX time of milliseconds since 1/1/1970 again, so again you can convert this to a format that is appropriate to you once you have it.
Hope that's helpful!
use Keycloak rest Api
${keycloakUri}/admin/realms/${keycloakRealm}/users
and you will get a response as JWT. Decode it and you will get all the info related to the user.
OR you may use the java client API for example by
Keycloak kc = KeycloakBuilder.builder()
.serverUrl("https://localhost:8443/auth")
.realm("master")
.username("admin")
.password("admin")
.clientId("Mycli")
.resteasyClient(new ResteasyClientBuilder().connectionPoolSize(10).build())
.build();
CredentialRepresentation credential = new CredentialRepresentation();
credential.setType(CredentialRepresentation.PASSWORD);
credential.setValue("test123");
UserRepresentation user = new UserRepresentation();
user.setUsername("testuser2");
user.setFirstName("Test2");
user.setLastName("User2");
user.setEmail("aaa#bbb.com");
user.setCredentials(Arrays.asList(credential));
user.setEnabled(true);
user.setRealmRoles(Arrays.asList("admin"));
UsersResource usersResource = kc.realm("my-realem").users();
UserResource userResource = usersResource.get("08afb701-fae5-40b4-8895-e387ba1902fb");
you will get the list of users. Filter by user ID then you will find all user info.
How to get the section's user information (like name, password)? Is there any method that returns it?
I'm trying the function "core_user_get_users_by_field", but it isn't works. That's I've done:
String serverurl = url + "/webservice/rest/server.php" + "?wstoken=" + token + "&wsfunction=" + functionName;
obs: The server was constructed using REST.
This works for me
/webservice/rest/server.php?wstoken=xxx&wsfunction=core_user_get_users_by_field&field=id&values[0]=2
2 is the user id.
You can use any field that uniquely identifies the user. eg: field=username
You can also retrieve more than one user at a time eg: values[0]=2&values[1]=3
This is assuming the function was added as a web service following these instructions
http://docs.moodle.org/25/en/Using_web_services
There is global object called $USER in moodle, this object contains all information about user, So where you want these information just access like,
global $USER; // <= don't forget to write this before to access
$USER->username;
$USER->firstname;
$USER->lastname;
$USER->password;
Its works with:
https://url.moodle.xyz/webservice/rest/server.php?wstoken=XXXXXXX&wsfunction=core_user_get_users_by_field&field=id&values%5B0%5D=1306
The query parameters:
wsfunction=core_user_get_users_by_field
field=id
values[0]=1234
values[0] is user id.
To get moodle userdetails based on user name you can use like this :
/webservice/rest/server.php?wstoken=8888&wsfunction=core_user_get_users_by_field&field=username&values[0]=mark
To get JSON data Use :
/webservice/rest/server.php?wstoken=8888&wsfunction=core_user_get_users_by_field&field=username&values[0]=mark&moodlewsrestformat=json
Via curl POST request:
curl \
-X POST \
--data-urlencode "wstoken=123456789..." \
--data-urlencode "wsfunction=core_user_get_users_by_field" \
--data-urlencode "field=id" \
--data-urlencode "values[0]=123456" \
https://moodle.domain.tld/webservice/rest/server.php
As Russell England said:
[...] use any field that uniquely identifies the user. eg: field=username