keycloak-rest-api

How to create enabled users in Keycloak via API calls?


I’m trying to create a new user in a Keycloak 22.0.3 server via API calls.

My code is mostly working, in that it manages to create the user and it manages to add the user to a specific group, but the newly created user is disabled in Keycloak and the credentials aren’t being saved, as you can see in the users list:

in the users list

and in the newly created user's credentials tab:

credentials tab.

Here is the sequence of API calls I make from my NodeJS/Typescript code (cut and pasted from the output of my console.log()):

URL: https://localhost:8080/admin/realms/MyRealm/users
options: {"headers":{"Authorization":"Bearer ey...","Content-Type":"application/json"},"method":"POST","body":"{\"email\":\"marioross12i@example.com\",\"username\":\"mariorossi12\",\"attributes\":{\"provisioned\":true},\"credentials\":[{\"secretData\":\"changeme\",\"temporary\":true}]}"}

URL: https://localhost:8080/admin/realms/MyRealm/attack-detection/brute-force/users/fe3d9e63-cd24-423f-a620-2027f907a9ca
options: {"headers":{"Authorization":"Bearer ey...","Content-Type":"application/x-www-form-urlencoded"},"method":"DELETE"}

URL: https://localhost:8080/admin/realms/MyRealm/users/fe3d9e63-cd24-423f-a620-2027f907a9ca/groups/97788f9e-a113-420b-a629-ad39e5d70f09
options: {"headers":{"Authorization":"Bearer ey...","Content-Type":"application/x-www-form-urlencoded"},"method":"PUT"}

I think posting here the code itself is quite useless, since it's overall complex in building the URL and options, but in the end it boils down to calling the endpoints here above, using fetch from the undici library, and, as far as calling endpoints is the problem, the code is working ok. In fact I get the same results if I call the same endpoints via curl.

Why does Keycloak create a disabled user with these calls? Why doesn't it store the credentials?


Solution

  • You need to add enabled key with true value in user payload

    Create User

    POST http://localhost:8080/admin/realms/MyRealm/users
    

    Body

    {
        "username": "{user name}",
        "emailVerified": true,
        "email": "{user e-mail}",
        "firstName": "{user first name}",
        "lastName": "{user last name}",
        "enabled": true,
        "credentials" : [
            {
                "type" : "password",
                "value" : "{user password}"
            }
        ]
    }
    

    Example

    {
        "username": "mariorossi12",
        "emailVerified": true,
        "email": "marioross12i@example.com",
        "firstName": "Mario",
        "lastName": "Ross",
        "enabled": true,
        "credentials" : [
            {
                "type" : "password",
                "value" : "1234"
            }
        ]
    }
    

    The credentials is optional

    Demo

    It will create an user using undici library.

    Save as create-user.js

    // create-user.js
    const { request } = require('undici');
    
    const getMasterToken = async (user_name, password) => {
        try {
            const params = new URLSearchParams({
                'client_id': 'admin-cli',
                'username': user_name,
                'password': password,
                'grant_type': 'password'
            });
    
            const { statusCode, body } = await request('http://localhost:8080/realms/master/protocol/openid-connect/token', {
                method: 'POST',
                body: params.toString(),  // Explicitly convert to string
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                }
            });
    
            if (statusCode !== 200) {
                const responseData = await body.text();  // Change to text() to see raw response
                throw new Error(`Failed to fetch token: Server responded with status ${statusCode}. Response: ${responseData}`);
            }
    
            const responseData = await body.json();
            return responseData.access_token;
        } catch (err) {
            console.error('Error during token retrieval:', err);
            return null; // Return null to signify failure
        }
    };
    
    
    const createUser = async (token, payload) => {
        if (!token) {
            console.error('No valid token provided');
            return false;
        }
    
        try {
            const { statusCode, body } = await request('http://localhost:8080/admin/realms/MyRealm/users', {
                method: 'POST',
                body: JSON.stringify(payload),
                headers: {
                    Authorization: `Bearer ${token}`,
                    'Content-Type': 'application/json'
                }
            });
    
            if (statusCode === 201) {
                return true;
            } else {
                const responseBody = await body.json();
                console.error(`Failed to create user: Server responded with status ${statusCode}`, responseBody);
                return false;
            }
        } catch (error) {
            console.error('Error during user creation:', error);
            return false;
        }
    };
    
    (async () => {
        const master_token = await getMasterToken('admin', 'admin');
    
        if (!master_token) {
            console.error('Failed to get master token');
            return;
        }
    
        const userPayload = {
            "username": "mariorossi12",
            "emailVerified": true,
            "email": "marioross12i@example.com",
            "firstName": "Mario",
            "lastName": "Ross",
            "enabled": true,
            "credentials": [
                {
                    "type": "password",
                    "value": "1234"
                }
            ]
        };
        const result = await createUser(master_token, userPayload);
    
        if (result) {
            console.log('Create User successful');
        } else {
            console.error('Failed to create user');
        }
    })();
    

    Install dependency

    npm install undici
    

    Run demo

    node create-user.js
    

    Result

    enter image description here

    enter image description here

    enter image description here

    Questions

    Why does Keycloak create a disabled user with these calls?

    You missed the enable key and value.

    Why doesn't it store the credentials?

    Keycloak does not store passwords directly in the database. Instead, it stores a cryptographic hash of the password. This is a security best practice for handling user passwords.

    Hashing is a one-way process, meaning once you convert the original input (like a password) into a hash, you cannot convert it back to the original input.

    It helps security having hashed passwords adds a layer of security. Unlike plain text passwords, hashed passwords are not directly usable by an attacker.