Migration From Forgerock
Overview
This document will help you migrate off of Forgerock.
This guide assumes you have installed FusionAuth. If you have not, please view our installation guides and install FusionAuth before you begin. For more general migration information, please view the FusionAuth migration guide.
There are a number of different ways applications can be integrated with Forgerock, and it would be difficult to cover them all. This guide mentions the typical parts of a bulk migration and in particular focuses on migrating user data from a Forgerock user database into FusionAuth.
This guide was tested against Forgerock 7.4. You can find the Forgerock Cloud Development Kit here.
Planning Considerations
Obtaining User Data
You will need API read access to your Forgerock instance. As part of the planning process, gather the credentials that will allow you to run the API queries against this instance.
There are many ways to get data from the Forgerock instances. If you cannot get API access and use something like the ./ldapsearch on the server, you will need to convert the output to similar JSON as output by the API calls.
Mapping User Attributes
The attributes of the User object in FusionAuth are well documented.
If there is an attribute in your user which cannot be directly mapped to a FusionAuth attribute, you can place it in the user.data field. This field can store arbitrary JSON values and will be indexed and searchable.
Social Logins
Forgerock also provides integrations with other social login providers such as Twitter, Google or Facebook. Review the supported FusionAuth Identity Providers to ensure your social providers are supported.
If not supported explicitly, a provider may work with an OIDC or SAML connection. Otherwise, please open a feature request.
Migrating users with social logins such as Apple or Facebook requires that you have an existing user Id for that provider. What this unique user Id looks like depends on the particular social identity provider. The unique Id may be an email address, an integer, UUID, or a random string.
Configure the appropriate FusionAuth Identity Provider with the same values (client_id
, etc) as the original user management system you are migrating away from.
Import users with the Import API, assigning each user with a social login a random password such as a UUID.
Your next step depends on whether the social login provider’s unique identifier is available as part of your migration data. If you have the social login provider’s unique identifier, for each user, use the Link API to create a link with the appropriate User Id, Identity Provider Id and Identity Provider User Id.
- The User Id is the Id of the recently created FusionAuth User.
- The Identity Provider Id is found on the corresponding Identity Provider API documentation. Look for identityProvider.id .
- The Identity Provider User Id is the existing social provider user identifier exported or otherwise extracted from the original system.
You do not need to migrate the social network token, which may or may not be accessible. During the first login of a newly migrated user, FusionAuth finds the unique user in the social login provider based on the migrated Identity Provider User Id, and completes the login. During this process, FusionAuth stores a token on the Link, if the social provider returns one. Depending on the configuration of the social provider, users may see a prompt asking if they want to allow FusionAuth to have access to user data such as email address.
IdP Linking Strategies are available since version 1.28.0. Before that version, users were linked on email.
If you do not have the social login provider’s identifier, you need to decide if you want to transparently link the two accounts, which is easier for the end user, or if you want to ask the user to manually link the accounts, which is more accurate, but may be confusing.
To transparently link the accounts, choose a linking strategy of Link On Email
or Link On Username
, which will create the user if they don’t exist. However, if the user has an email address at their social provider which differs from the email address that was used to sign up for your application and which you imported to FusionAuth, then two accounts will be created.
For example, if the user has a Google account richard@gmail.com
, but signed up for your application with richard@fusionauth.io
, then if you use the Link On Email
strategy, two different accounts will be created, since FusionAuth is trying to match on email address and they don’t. The same holds true for usernames with the Link on Username
strategy.
To prompt the user to link the accounts, choose a linking strategy of Pending
, which will prompt the end user to sign into FusionAuth after they sign into the social provider, authoritatively linking the two accounts.
Here’s more information about IdP Linking Strategies.
Other Entities
There are often other important entities, such as identity providers, clients, or keys, that need to be migrated. There are usually fewer of these, so an automated migration may not make sense, but plan to move this configuration somehow.
Be aware that functionality may not be the same between Forgerock and FusionAuth. This is different from user data; as long as you can somehow migrate a login identifier (a username or email) and a password hash, a user will be authenticated and successfully migrated. You can download FusionAuth before you begin a migration and build a proof of concept to learn more about the differences.
A partial list of what may need to be migrated for your application to work properly includes the following:
- In Forgerock, Identity Providers and User Federation allow user data to remain in external systems of record. FusionAuth has similar concepts of Identity Providers and Connectors.
- Mappers are ways for you to customize authentication or authorization workflows. FusionAuth has a similar concept called Lambdas.
- With Forgerock, Clients are what your users can log in to. FusionAuth refers to these as Applications. In both cases, you can use either OAuth/OIDC or SAML to do so.
- Realms are a high level construct which groups other entities such as users and clients together. FusionAuth calls these Tenants. FusionAuth supports a multi-tenant configuration by default.
- For Forgerock, Roles provide information about what your users can do in your custom or off the shelf applications. Roles can be associated with other roles. FusionAuth has Roles and they are defined on an Application by Application basis and cannot be composed.
- Refresh tokens allow JWTs to be refreshed without a user logging in. These can be migrated using the Import Refresh Tokens API. Forgerock calls these “Offline Sessions”
Identifiers
When creating an object with the FusionAuth API, you can specify the Id. It must be a UUID.
This works for users, applications, and tenants, among others.
If you have external dependencies on an Id stored in , port the same Id over to FusionAuth.
Once you’ve planned your migration, the next step is to export your user data from Forgerock.
Exporting Users
To import your users, you’ll need their attributes, including their password hashes and other information.
During this migration, passwords are never exposed in plaintext. Neither FusionAuth, Forgerock, nor you ever have access to the password.
Here are scripts that will query the needed fields.
Exporting Using the API
There are many ways to export data from Forgerock. For this example, you will be using the Rest API.
By default, Forgerock has disabled the exporting of the password hash through the Rest API. To enable, select Identity Management Native Console -> Configure -> Managed Object -> User. Find the ‘password’ field. Select Edit. Then, select the Privacy & Encryption tab. Next, set Private to false. You may wish to re-enable after the export.
If you do not want to set the Private
switch to false in your environment, you may use the ./ldapsearch method mentioned above or another viable alternative method. The results of that will then have to be merged with the other user information into a .json
file using the method of your choosing.
Forgerock Export Script
require 'net/http'
require 'uri'
require 'openssl'
require 'optparse'
require 'json'
# option handling
options = {}
# default options
options[:outputfile] = "forgerock-user-export.json"
options[:forgerockbaseurl] = "https://cdk.example.com/"
options[:forgerockrealm] = "realms/root/realms/alpha/"
options[:forgerockusername] = "postmanAdminUser"
options[:forgerockuserpassword] = "Password1234!"
options[:forgerockclientid] = "postmanAdminClient"
options[:forgerockclientsecret] = "Password1234!"
OptionParser.new do |opts|
opts.banner = "Usage: forgerock-export.rb [options]"
opts.on("-o", "--output-file OUTPUT_FILE", "The name and location of the output file to write.") do |outputfile|
options[:outputfile] = outputfile
end
opts.on("-b", "--base-url BASE_URL", "The base url for the Forgerock instance.") do |baseurl|
options[:forgerockbaseurl] = baseurl
end
opts.on("-r", "--realm REALM", "The Forgerock realm the client belongs to.") do |realm|
options[:forgerockrealm] = realm
end
opts.on("-u", "--user-name USER_NAME", "The user name of the api user.") do |username|
options[:forgerockusername] = username
end
opts.on("-p", "--user-password USER_PASSWORD", "The password for the api user.") do |password|
options[:forgerockuserpassword] = password
end
opts.on("-c", "--client-id CLIENT_ID", "The client id of the application to use for the authentication.") do |clientid|
options[:forgerockclientid] = clientid
end
opts.on("-s", "--client-secret", "The client secret for the application to use for authentication.") do |clientsecret|
options[:forgerockclientsecret] = clientsecret
end
opts.on("-h", "--help", "Prints this help.") do
puts opts
exit
end
end.parse!
#get access_token
uri = URI.parse(options[:forgerockbaseurl] + "am/oauth2/" + options[:forgerockrealm] + "/access_token")
request = Net::HTTP::Post.new(uri)
request.content_type = "application/x-www-form-urlencoded"
request.set_form_data({
'grant_type' => 'password',
'username' => options[:forgerockusername],
'password' => options[:forgerockuserpassword],
'scope' => 'fr:idm:*',
'client_id' => options[:forgerockclientid],
'client_secret' => options[:forgerockclientsecret]
})
req_options = {
use_ssl: uri.scheme == "https",
verify_mode: OpenSSL::SSL::VERIFY_NONE,
}
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
http.request(request)
end
response_body = JSON.parse(response.body)
access_token = response_body['access_token']
#get user info
#update fields to return desired fields
uri = URI(options[:forgerockbaseurl] + "openidm/managed/user")
params = {
:_fields => 'mail,userName,sn,givenName,telephoneNumber,favoriteColor,password',
:_prettyPrint => 'true',
:_queryFilter => 'true',
}
uri.query = URI.encode_www_form(params)
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer ' + access_token
req_options = {
use_ssl: uri.scheme == 'https',
verify_mode: OpenSSL::SSL::VERIFY_NONE
}
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
http.request(request)
end
response_body = JSON.parse(response.body)
File.write(options[:outputfile] , JSON.pretty_generate(response_body))
There are several options that can be set when running the script. To see a list of options available, use the -h switch.
The output of the script will be a .json
file:
Forgerock Export Script Help
ruby ./forgerock-export.rb -h
Forgerock Export Script Help Results
Usage: forgerock-export.rb [options]
-o, --output-file OUTPUT_FILE The name and location of the output file to write.
-b, --base-url BASE_URL The base url for the Forgerock instance.
-r, --realm REALM The Forgerock realm the client belongs to.
-u, --user-name USER_NAME The user name of the api user.
-p USER_PASSWORD, The password for the api user.
--user-password
-c, --client-id CLIENT_ID The client id of the application to use for the authentication.
-s, --client-secret The client secret for the application to use for authentication.
-h, --help Prints this help.
Generate JSON Export with Forgerock Rest API
ruby forgerock-export.rb -o "forgerock_user_export.json" -b "https://cdk.example.com/" -r "realms/root/realms/alpha/" -u "postmanAdminUser" -p "Password1234\!" -c "postmanAdminClient" -s "Password1234\!"
Export Result
The result of running the script will be a JSON file that looks like:
Sample User Export
{
"result": [
{
"_id": "b8fb2d96-2cfe-492c-924c-fd365bdb8f69",
"_rev": "afba2c3c-2f7e-4416-8f3f-2de234a3021b-145868",
"favoriteColor": "red",
"telephoneNumber": null,
"mail": "testuser1@example.com",
"password": "{PBKDF2-HMAC-SHA256}10:NnfLb4gwMj31YdtATz8824xGQrJylmwCNm2LQqy0wixtuWvU+vMKMpZ7mye04Jfp",
"sn": "User1",
"givenName": "Test1",
"userName": "testuser1"
},
{
"_id": "be0d0fb2-1634-4e14-9fe4-5ac8775cf6b0",
"_rev": "afba2c3c-2f7e-4416-8f3f-2de234a3021b-145875",
"favoriteColor": "blue",
"telephoneNumber": null,
"mail": "testuser2@example.com",
"password": "{PBKDF2-HMAC-SHA256}10:8c7nLGEIXeZf45YQ92A2MD+v8olvKKl6iWXGQZoluJ/awqZnHwFvslIOx7xOZ9AV",
"sn": "User2",
"givenName": "Test2",
"userName": "testuser2"
}
],
"resultCount": 2,
"pagedResultsCookie": null,
"totalPagedResultsPolicy": "NONE",
"totalPagedResults": -1,
"remainingPagedResults": -1
}
Now, you can begin the user import process.
Importing Users
Next up, import the user data. Here are the steps we need to take:
- Set Up FusionAuth
- Get the Script
- Install Needed Gems
- Use the Script
- Verify the Import
- The Final Destination of Imported Users
Set Up FusionAuth
You need to set up FusionAuth so migrated user data can be stored. As mentioned above, this guide assumes you have FusionAuth installed.
If you don’t, view our installation guides and install it before proceeding further.
Optionally Install a Hashing Plugin
FusionAuth will work with the Forgerock default PBKDF2-HMAC-SHA256 encryption.
If you have configured Forgerock to use a different hashing algorithm, you may need to write and install a plugin using that algorithm. You’ll also need to update the map_hashing_algorithm
method in the import.rb
script.
Create a Test Tenant
It is best to create a separate tenant for migration testing. Tenants logically isolate configuration settings and users. If a migration goes awry or you need to redo it after tweaking settings, you can delete the test tenant and start with a clean system. To add a tenant, navigate to Tenants and choose the Add button (green plus sign).
Give it a descriptive Name like Forgerock import test
. You shouldn’t need to modify any of the other configuration options to test importing users.
Save the tenant.
Record the Id of the tenant, which will be a UUID. It will look something like 25c9d123-8a79-4edd-9f76-8dd9c806b0f3
. You’ll use this later.
Create a Test Application
Applications are anything a user can log in to. In FusionAuth there’s no differentiation between web applications, SaaS applications, APIs and native apps. To add an application, navigate to Applications and click on the Add button (the green plus sign). Give the application a descriptive name like Forgerock application
.
Select your new tenant, created above, in the dropdown for the Tenant field.
Navigate to the OAuth tab and add an entry to Authorized redirect URLs . Use a dummy value such as https://example.com
. Later, you’ll need to update this to be a valid redirect URL that can take the authorization code and exchange it for a token. Learn more about this in the FusionAuth OAuth documentation.
You shouldn’t need to modify any of the other configuration options to test importing users. Save the application.
Next, view the application by clicking the green magnifying glass and note the OAuth IdP login URL . You’ll be using it to test that users can log in.
Add an API Key
The next step is to create an API key. This will be used by the import script. To do so, navigate to Settings -> API Keys in the administrative user interface.
This key needs to have the permission to run a bulk import of users. In the spirit of the principle of least privilege, give it the permission to POST
to the /api/user/import
endpoint. Record the API key string, as you’ll use it below.
Get the Script
FusionAuth provides an import script under a permissive open source license. It requires ruby (tested with ruby 2.7). To get the script, clone the git repository:
Getting the import scripts
git clone https://github.com/FusionAuth/fusionauth-import-scripts
Navigate to the forgerock
directory:
cd fusionauth-import-scripts/forgerock
Install Needed Gems
The following gems must be available to the import script:
date
json
optparse
fusionauth_client
base64
Most likely all of these will be on your system already, except the fusionauth_client
gem.
If you have bundler
installed, run bundle install
in the forgerock directory. Otherwise install the needed gems in some other way.
Use the Script
Running the import script with the help command line switch
ruby ./import.rb -h
Import Script Help Results
Usage: import.rb [options]
-r APPLICATION_IDS, A comma separated list of existing applications Ids. All users will be registered for these applications.
--register-users
-u, --users-file USERS_FILE The exported json user data file from Forgerock. Defaults to samplefiles/forgerock-user-export.json.
-f FUSIONAUTH_URL, The location of the FusionAuth instance. Defaults to http://localhost:9011.
--fusionauth-url
-k, --fusionauth-api-key API_KEY The FusionAuth API key.
-t TENANT_ID, The FusionAuth tenant id. Required if more than one tenant exists.
--fusionauth-tenant-id
-m, --map-forgerock-user-id Whether to map the forgerock user id for normal imported users to the FusionAuth user id.
-h, --help Prints this help.
For this script to work correctly, set the following switches, unless the defaults work for you:
-u
should point to the location of the user export file you created.-f
must point to your FusionAuth instance. If you are testing locally, it will probably behttp://localhost:9011
.-k
needs to be set to the value of the API key created above.-t
should be set to the Id of the testing tenant created above.
You may or may not want to use the -m
switch, which takes the Forgerock Id and uses that for the FusionAuth user Id. If you have external systems reliant on the identifier, set this. Doing so ensures imported users have the same Id as they did in Forgerock. Otherwise, you can omit this switch.
When you run the script, you’ll see output like:
Import script output
$ ruby ./import.rb -u ./sample-files/forgerock-user-export.json -k 33052c8a-c283-4e96-9d2a-eb1215c69f8f-not-for-prod -t 25c9d123-8a79-4edd-9f76-8dd9c806b0f3
FusionAuth Importer : Forgerock
> Working Directory: forgerock
> User file: ./sample-files/forgerock-user-export.json
> Call FusionAuth to import users
> Import success
Duplicate users 0
Import complete. 2 users imported.
Enhancing the Script
You may also want to migrate additional data. Currently, the following attributes are migrated:
user_id
email
username
firstName
lastName
- the password hash and supporting attributes, if available
The migrated user will have the original Forgerock user Id stored on the user.data
object. If you have additional user attributes to migrate, review and modify the map_user
method.
You may also want to assign Roles, or associate users with Groups, by creating the appropriate JSON data structures in the import call. These are documented in the Import User API docs. This will require modifying the import.rb
code.
Verify the Import
Next, log in to the FusionAuth administrative user interface. Review the user entries to ensure the data was correctly imported.
You can manage the user by clicking on the Manage button (black button) to the right of the Created date in the list to review the details of the imported user’s profile.
If you have a test user whose password you know, open an incognito window and log in to ensure the hash migration was successful. You recorded the URL to log in to the example application in Create a Test Application .
After the test login, the user will be redirected to a URL like https://example.com/?code=FlZF97WIYLNxt4SGD_22qvpRh4fZ6kg_N89ZbBAy1E4&locale=fr&userState=Authenticated
. This happens because you haven't set up a web application to handle the authorization code redirect.
That is an important next step but is beyond the scope of this document. Consult the 5 minute setup guide
for an example of how to do this.The Final Destination of Imported Users
After you are done testing, you can choose to import users into the default tenant or a new tenant. Whichever you choose, make sure to update the -t
switch to the correct value before running the import for the final time.
If you aren’t keeping users in the test tenant, delete it.
If you need to start over because the import failed or you need to tweak a setting, delete the tenant you created. This will remove all the users and other configurations for this tenant, giving you a fresh start. To do so, navigate to Tenants and choose the Delete button (red trash can icon).
Confirm your desire to delete the tenant. Depending on how many users you have imported, this may take some time.
What to Do Next
You now have your users migrated, or a plan to do so. Congratulations! What is next?
You need to migrate additional configurations, as mentioned in Other Entities . Since the type of configuration varies, it is hard to provide a full list of how to import these items, but the general pattern will be:
- Identify corresponding FusionAuth functionality.
- Configure it in your FusionAuth instance, either manually or by scripting it using the client libraries or API.
- Update your application configuration to use the new FusionAuth functionality.
Make sure you assign your users to the appropriate FusionAuth applications. You can do this either:
- As part of your import process by adding registrations at import time.
- After users have been migrated with the Registrations API.
You’ll also need to modify and test each of your applications, whether custom, open source, or commercial, to ensure:
- Users can successfully log in.
- The authorization code redirect is handled correctly.
- Users receive appropriate permissions and roles based on the JWT.
- The look and feel of the hosted login pages matches each application’s look and feel.
If your application uses a standard OAuth, SAML or OIDC library to communicate with , the transition should be relatively painless.
Additional Support
If you need support in your migration beyond that provided in this guide, you may:
- Post in our forums and get support from the community.
- Purchase a support plan and get expert help from the Engineering Team.