Bulk Import Users from Oracle & APEX to Keycloak

Bulk Import Users from Oracle & APEX to Keycloak

For this guide, we are going to use the Keycloak API to bulk load users from our user table into Keycloak. Whilst loading we can optionally send informative emails to the users. Following loading, the users are required to follow the Forgot Password link. This will email them a one-time link, allowing them to set a password.

This guide is an alternative to using the plugin approach. If I was to recommend either this Bulk Import approach or the Plugin approach - I would recommend the plugin approach.

Prerequisites

Keycloak Setup

  1. Log into the Keycloak Admin Console

  2. Change from Master to your Realm

  3. Enable Forgot Password in Realm Settings

  4. In Clients, create an apiuser client with these capabilities

  5. After creation, click back into clients > apiuser > service accounts roles > Assign Role

  6. Select filter by clients and search for manage-users

  7. Select realm-management manage-users and click Assign

  8. Click clients > apiuser > credentials and copy the client secret to clipboard

Oracle Database

  1. Review the following code and change the first 4 variables under -- change me. In addition change the WITH my_users clause to select from your users table instead of dual. In addition, whilst in the loop, consider sending your users an email directing them to the forgot password link.

     DECLARE
         -- Change Me
         l_url_base           CONSTANT VARCHAR2(1000) := 'https://identity.example.com';
         l_realm              CONSTANT VARCHAR2(1000) := 'MY_REALM';
         l_client_id          CONSTANT VARCHAR2(100) := 'apiuser';
         l_client_secret      CONSTANT VARCHAR2(100) := 'my-secret'; 
         -- Variables
         l_url_token          CONSTANT VARCHAR2(1000) := l_url_base || '/realms/' || l_realm || '/protocol/openid-connect/token';
         l_url_user           CONSTANT VARCHAR2(1000) := l_url_base || '/admin/realms/' || l_realm || '/users';
         l_clob_response  CLOB;
         l_response_code  NUMBER;
         l_response_msg   VARCHAR2(2000);
         l_access_token   VARCHAR2(4000);
     BEGIN
    
         apex_web_service.set_request_headers(
             p_name_01        => 'Content-Type',
             p_value_01       => 'application/x-www-form-urlencoded' );
    
         -- Make the REST request
         l_clob_response := APEX_WEB_SERVICE.MAKE_REST_REQUEST(
             p_url            => l_url_token,
             p_http_method    => 'POST', 
             p_parm_name      => APEX_UTIL.STRING_TO_TABLE('client_id:client_secret:grant_type', ':'),
             p_parm_value     => APEX_UTIL.STRING_TO_TABLE(apex_string.format('%0:%1:client_credentials', l_client_id, l_client_secret ), ':')
         );
    
         APEX_JSON.PARSE(l_clob_response);
         l_access_token := APEX_JSON.GET_VARCHAR2(p_path => 'access_token'); 
    
         FOR x in ( 
             WITH my_users AS (
           SELECT 'mmulvaney' AS username,
                  1 AS enabled,
                  'Matt' AS first_name,
                  'Mulvaney' AS last_name,
                  'mmulvaney@example.com' AS email
           FROM dual
         )
         SELECT  
             JSON_OBJECT(
               'username' VALUE username,
               'enabled' VALUE CASE WHEN enabled = 1 THEN 'true' ELSE 'false' END FORMAT JSON,
               'totp' VALUE 'false' FORMAT JSON,
               'emailVerified' VALUE 'true' FORMAT JSON,
               'firstName' VALUE first_name,
               'lastName' VALUE last_name,
               'email' VALUE email,
               'credentials' VALUE '[]' FORMAT JSON,
               'disableableCredentialTypes' VALUE '[]' FORMAT JSON,
               'requiredActions' VALUE JSON_ARRAY('UPDATE_PASSWORD'),
               'realmRoles' VALUE JSON_ARRAY('default-roles-lufpod'),
               'notBefore' VALUE 0,
               'groups' VALUE '[]' FORMAT JSON
             )
           AS user_json
         FROM my_users
          )
          LOOP
    
              apex_web_service.set_request_headers(
                 p_name_01        => 'Content-Type',
                 p_value_01       => 'application/json',
                 p_name_02        => 'Authorization',
                 p_value_02       => 'Bearer ' || l_access_token
                  );     
    
             -- Make the REST request
             l_clob_response := APEX_WEB_SERVICE.MAKE_REST_REQUEST(
                 p_url            => l_url_user,
                 p_http_method    => 'POST', 
                 p_body           => x.user_json
             );
    
             dbms_output.put_line( l_clob_response );
          END LOOP;
    
     EXCEPTION
         WHEN OTHERS THEN
             DBMS_OUTPUT.PUT_LINE('Error: ' || SQLERRM);
     END;
    

Testing

  1. Sign out of Keycloak and click this link / or use a new Private Tab e.g.

     https://identity.example.com/realms/YOUR-REALM-NAME/account
    
  2. Click Sign In

  3. Click Forgot Password

  4. Follow the wizard

  5. The user will then receive an email

  6. You’ll then be prompted to change your password (as per the required_actions)

  7. The user is now signed in and the password credentials are now created in Keycloak

    ENJOY

ENJOY

What’s the picture? A Christmassy Leeds Bradford Airport - Visit Yorkshire! (via this Airport)