REST API Create User - No User identifier in response?

hello there,

i successfully managed to use the keycloak admin rest api and invoke user endpoints.
i was also able to create a new user with POST /{realm}/users

my question is:
what exactly is the response on this endpoint?
do i get the identifier of the created user? this would be very handy for invoking other endpoints after user creation, like setting roles for example.

on the documentation page,
https://www.keycloak.org/docs-api/9.0/rest-api/index.html#_users_resource
it says the endpoint has a response of type ā€œResponseā€ but the link leads to nowhere so i am unable to tell what is the response on this endpoint

does anybody know what is the response and how we can get the identifier of the recently created user?

thanks,
Tiago

I guess there is no response body, only 201 or 204 HTTP response code. You will need to list all users and find id based on user properties. But better option will be to POST id with all user properties in one go. (id is just uuid). You will get 409 (Conflict) if that particular id already exists.
The best option is try it - curl in command line and you will see.

The option of listing all users filtered by a specific field is what i was trying to avoid, but your idea of generating the uuid on my end and then submiting it is something I had not yet though of
thanks!

1 Like

Hi @tiago.diogo,

Check the headers of your response when you create the user. It should contain the location of the newly created user. You can get the id by cutting it of that String.

A header ā€˜Locationā€™ should be present in the response and should look like http://localhost:8088/auth/admin/realms/realm-name/users/12f2e22b-8137-495f-bccc-50bb15cdf0ec

2 Likes

hello @zonaut
thank you for your heads up, i confirm the UUID is in the location header and iā€™m already taking advantage of that :slight_smile:
hope this implementation does not change over time, since itā€™s not even documented AFAIK

I donā€™t think the implementation will change because itā€™s normal behavior to include a location header when creating entities through a REST API.
But maybe they could add a response body with the uuid overtime, this would be handy to have.

Iā€™ve seen a lot of activity recently on https://issues.redhat.com/projects/KEYCLOAK/issues about reviewing the documentation so these should be improving in the future.

Hello Tiago, how were you able to get the access token and invoke the rest api exactly? Ive been trying to invoke the rest api but i keep getting could not find resource for full path error or 401 unauthorized errors. Thanks

@andres I think your answer may be in here. https://robferguson.org/blog/2019/12/24/getting-started-with-keycloak/.

Thank you @robferguson.

@andres in my case i am using spring with a feign client that handles token management for me
https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html

Thank you @arozar . Thank you @tiago.diogo .

@andres - Here are a few python functions that will return the user UUID

from django.conf import settings
from urllib.parse import quote
import json
import requests

proxies = {
    'http': 'http://127.0.0.1:8080',
    'https': 'http://127.0.0.1:8080',
}

headers = {
    'Accept': 'application/json',
}


def get_token():
    """
    POST /auth/realms/master/protocol/openid-connect/token HTTP/1.1
    Host: IPADDR:8443
    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:74.0) Gecko/20100101 Firefox/74.0
    Accept: */*
    Accept-Language: en-US,en;q=0.5
    Accept-Encoding: gzip, deflate
    Content-type: application/x-www-form-urlencoded
    Content-Length: 79
    Origin: http://IPADDR:8443
    Connection: close
    Referer: http://IPADDR:8443/auth/admin/master/console/
    username=admin&password=secret&grant_type=password&client_id=admin-cli
    :return:
    """
    body = 'username=%s&password=%s&grant_type=password&client_id=admin-cli' % (settings.KEYCLOAK_ADMIN_USER, quote(settings.KEYCLOAK_ADMIN_PASSWD))
    headers['Content-type'] = 'application/x-www-form-urlencoded'
    r = requests.post(
        url='https://%s:%s/auth/realms/master/protocol/openid-connect/token' % (settings.KEYCLOAK_HOST, settings.KEYCLOAK_PORT),
        data=body,
        headers=headers,
        # proxies=proxies,
    )
    return r.json()


def create_user(username, email, first_name, last_name):
    """
    POST /auth/admin/realms/trolleye/users HTTP/1.1
    Host: IPADDR:8443
    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:74.0) Gecko/20100101 Firefox/74.0
    Accept: application/json, text/plain, */*
    Accept-Language: en-US,en;q=0.5
    Accept-Encoding: gzip, deflate
    Content-Type: application/json;charset=utf-8
    Authorization: Bearer TOKEN
    Content-Length: 181
    Origin: http://IPADDR:8443
    Connection: close
    Referer: http://IPADDR:8443/auth/admin/master/console/

    {"enabled":true,"attributes":{},"username":"bitsaboy","emailVerified":true,"email":"bob@domain.com","firstName":"Bobhadababy","lastName":"Itsaboy","requiredActions":["CONFIGURE_TOTP"]}
    :return:
    """
    access_token = get_token()['access_token']
    body = {"enabled": True, "attributes": {},
            "username": username,
            "emailVerified": True,
            "email": email,
            "firstName": first_name,
            "lastName": last_name,
            "requiredActions": ["CONFIGURE_TOTP"]
            }
    headers['Authorization'] = 'Bearer %s' % access_token
    headers['Content-type'] = 'application/json'
    r = requests.post(
        url='https://%s:%s/auth/admin/realms/%s/users' % (settings.KEYCLOAK_HOST, settings.KEYCLOAK_PORT, settings.CLIENT),
        data=json.dumps(body),
        headers=headers,
        # proxies=proxies,
    )
    return r.headers.get('Location').split('/')[-1]

And to create the user just do this:

user_uuid = create_user('bobhadababy.itsaboy', 'bobhadababy.itsaboy@domain.com', 'Bobhadababy', 'Itsaboy')

Update:
You have to make a GET request to the URL given in the Location header to get the UUID

$ curl -i -H "Authorization: Bearer $ACCESS_TOKEN" http://localhost:8080/auth/admin/realms/testrealm/clients/fb0411ca-3637-4925-9325-9f979bb0e826/roles/a-new-role
HTTP/1.1 200 OK
Cache-Control: no-cache
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Referrer-Policy: no-referrer
Date: Sat, 28 Aug 2021 19:21:01 GMT
Connection: keep-alive
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
Content-Type: application/json
Content-Length: 328

{"id":"6814dcb5-0264-4bc3-8384-1aa1a5fac2cf","name":"a-new-role","description":"This is a new role that was created using the Keycloak Admin REST API","composite":false,"clientRole":true,"containerId":"fb0411ca-3637-4925-9325-9f979bb0e826","attributes":{"key1":["key1val1"],"key2":["key2val2"]}}

Original post:
This doesnā€™t work anymore with Keycloak v15.0.2.
When creating a new client role using POST, the final part of the location header contains the name of the role. NOT the UUID generated by Keycloak. Also you canā€™t set the ID manually, Keycloak just ignores it and generates itā€™s own ID.

$ curl -i -X POST --data '@role.json' -H 'Content-Type: application/json' -H "Authorization: Bearer $ACCESS_TOKEN" http://localhost:8080/auth/admin/realms/testrealm/clients/fb0411ca-3637-4925-9325-9f979bb0e826/roles
HTTP/1.1 201 Created
X-XSS-Protection: 1; mode=block
Location: http://localhost:8080/auth/admin/realms/testrealm/clients/fb0411ca-3637-4925-9325-9f979bb0e826/roles/a-new-role
X-Frame-Options: SAMEORIGIN
Referrer-Policy: no-referrer
Date: Sat, 28 Aug 2021 19:06:48 GMT
Connection: keep-alive
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
Content-Length: 0
1 Like