Import Users

This API is used to bulk import multiple Users into FusionAuth. Each User must have at least an email or a username, and a password (plaintext or hash). If you don't have the User's password, you can set this field to a long random string and require the User to reset their password at their next login. This request is useful for migrating data from an existing database into FusionAuth. Additionally, you can provide an Id for each User inside the JSON User object of the request body. When using this API, the recommended batch size per request is dependent on deployment scale (note: 100,000 users per request is a reasonable batch size for a production capable deployment). After completing an import, you should reindex the Elasticsearch database as well.

You should not make multiple calls to this API in parallel. Multiple sequential calls to this API are fine.

Request#

API Key Authentication
Import Multiple Users
POST/api/user/import
OpenAPI Spec

Request Headers#

X-FusionAuth-TenantIdStringoptional

The unique Id of the tenant used to scope this API request.

When only a single tenant is configured the tenant Id can be assumed and this additional header is optional. Once more than one tenant has been configured in FusionAuth the tenant Id is required for this request. The tenant Id may be provided through this header or by using a tenant locked API key to achieve the same result.

See Making an API request using a Tenant Id for additional information.

You must provide either the email or the username field for each User. This will be used to authenticate the User and is also the User's unique identifier. These fields are marked as optional below, but you must provide one of them. The password field (plaintext or hash) is also required.

Request Body#

encryptionSchemeStringoptional

The encryption scheme used to encrypt plaintext passwords encountered in the request. If this value is omitted the system configured value will be used.

The following encryptors are provided with FusionAuth:

factorIntegeroptional

The factor used to encrypt plaintext passwords encountered in the request. If this value is omitted the system configured value will be used.

usersArrayrequired

The list of Users to import.

users[x].activeBooleanoptionalDefaults to false

True if the User is active. False if the User has been deactivated. Deactivated Users will not be able to login. Generally this should be set to true during the bulk import.

users[x].birthDateStringoptional

YYYY-MM-DD formatted date of the User's birth.

users[x].dataObjectoptional

An object that can hold any information about a User that should be persisted.

user[x].data.emailStringoptional

This field will be used as the email address if no user.email field is found. This field may be modified by advanced registration forms or the API. Setting this value to another account's email address allows that account to, in some cases, access information about this user.

If user richard has a user.data.email with a value of dinesh@fusionauth.io, whoever controls dinesh@fusionauth.io has elevated access to the richard account. That user can now reset the password on the richard account, for example. This functionality may be useful in certain scenarios, such as when accounts must share an email address. Think through the security ramifications before using this feature.

This feature was removed in version 1.26.0 and added back in in 1.27.2.

users[x].emailStringoptional

The User's email address.

users[x].encryptionSchemeStringoptional

The method for encrypting the User's password. If the password value is already encrypted this value is required.

Omitting this value indicates the password is in plain text and it will be encrypted using the default password encryptor.

Importing users with plain text passwords is very slow because FusionAuth will hash each one using the default scheme. Grab some popcorn and catch a movie it can take up to several hundred milliseconds per user.

The following encryptors are provided with FusionAuth:

If you don't see the scheme needed for importing your existing password you can also create your own password encryptor or we can build it for you. See the Custom Password Hashing section for more information.

users[x].expiryLongoptional

The expiration instant of the User's account. After this instant is reached, the User's account will automatically be deactivated.

users[x].factorStringoptional

The factor used by the password encryption scheme. If omitted the factor will determined either by the system configured factor or the default value defined by the PasswordEncryptor.

The factor will generally be used as an iteration count to generate the hash. The actual use of this value is up to the PasswordEncryptor implementation.

users[x].firstNameStringoptional

The first name of the User.

users[x].fullNameStringoptional

The User's full name as a separate field that is not calculated from firstName and lastName.

users[x].idUUIDoptionalDefaults to secure random UUID

The Id of the User. If not specified a secure random UUID will be generated.

users[x].imageUrlStringoptional

The URL that points to an image file that is the User's profile image.

users[x].insertInstantLongoptionalDefaults to now

The instant when the user was created.

users[x].lastNameStringoptional

The User's last name.

users[x].membershipsArrayoptional

The list of memberships for the User.

users[x].memberships[x].dataObjectoptional

An object that can hold any information about the User for this membership that should be persisted.

users[x].memberships[x].groupIdUUIDrequired

The Id of the Group of this membership.

users[x].memberships[x].idUUIDoptionalDefaults to secure random UUID

The unique Id of this membership. If not specified a secure random UUID will be generated.

users[x].middleNameStringoptional

The User's middle name.

users[x].mobilePhoneStringoptional

The User's mobile phone number. This is useful to send the User push notifications or SMS messages.

The phone number in this field can't be used for login. Use users[x].phoneNumber for a phone that can be used for login.

users[x].passwordStringoptional

The User's password. If encryptionScheme is provided this value is the existing password hash, if encryptionScheme is not provided this value is assumed to be plain text and it will be hashed during import.

The value of password should contain only the password hash, or, if the encryptionScheme is omitted, the plain text password. Unless you are using a custom plugin that does not utilize base64, password should be base64 encoded. It is recommended to use a base64 encoded string, but if you are importing hashes from a legacy system that uses base16, base32, or another encoding and are writing a custom plugin, import the hash in the same format the plugin produces.

Please see the Password Hashes section for more information about importing particular password hashes.

Prior to version 1.59.0, this field is required. Beginning with version 1.59.0, this field is optional.

users[x].passwordChangeReasonStringoptionalDefaults to Administrative

When passwordChangeRequired is set to true you may optionally set a reason for this change. The possible values are:

  • Administrative - the password must be changed due to an administrative action
  • Breached - the password must be changed due to it being identified as breached in some fashion
  • Expired - the password must be changed due to expiration
  • Validation - the password must be changed due to the password no longer meeting the configured minimum requirements

In practice, leaving the default makes the most sense. The other reasons are used by FusionAuth during runtime to automatically require a user to change their password.

users[x].passwordChangeRequiredBooleanoptionalDefaults to false

Indicates that the User's password needs to be changed during the next login attempt.

users[x].passwordLastUpdateInstantLongoptionalDefaults to now

Indicates that last instant the password was changed.

If encryptionScheme is omitted this value will be ignored and set to now.

Note that if you have enabled password expiration or plan to do so in the future and you set this value to 0 or some other arbitrary time in the past that is greater than the password expiration value in days, these users will be required to change their password. It is recommended that you omit this value and allow the default value to be set unless you know the last time the user changed their password.

users[x].phoneNumberStringoptionalAvailable since 1.59.0

The phone number of the User.

Use this field instead of users[x].mobilePhone for a phone number that is used to log in, just like an email address or a username.

users[x].preferredLanguagesArray<String>optional

An array of locale strings of the user's preferred languages, ordered by preference from most to least.

Use the language preferences for email templates and other localized text. For more information, see Locales.

users[x].registrationsArrayoptional

The list of registrations for the User.

users[x].registrations[x].applicationIdUUIDrequired

The Id of the Application that this registration is for.

users[x].registrations[x].cleanSpeakIdUUIDoptional

This Id is used by FusionAuth when the User's username for this registration is sent to CleanSpeak to be moderated (filtered and potentially sent to the approval queue). It is the content Id of the username inside CleanSpeak.

users[x].registrations[x].dataObjectoptional

An object that can hold any information about the User for this registration that should be persisted.

users[x].registrations[x].idUUIDoptionalDefaults to secure random UUID

The Id of this registration. If not specified a secure random UUID will be generated.

users[x].registrations[x].insertInstantLongoptionalDefaults to now

The instant that this registration was created.

users[x].registrations[x].lastLoginInstantLongoptional

The instant that the User last logged into the Application for this registration.

users[x].registrations[x].preferredLanguagesArray<String>optional

An array of locale strings of the user's preferred languages, ordered by preference from most to least.

Use the language preferences for email templates and other localized text. For more information, see Locales.

users[x].registrations[x].rolesArray<String>optional

The list of roles that the User has for this registration. The string is the role's Name not the role's Id, e.g. admin or user-role.

users[x].registrations[x].usernameStringoptional

The username of the User for this registration only.

users[x].registrations[x].usernameStatusStringoptionalDefaults to ACTIVE

The current status of the username. This is used if you are moderating usernames via CleanSpeak. The possible values are:

  • ACTIVE - the username is active
  • PENDING - the username is pending approval/moderation
  • REJECTED - the username was rejected during moderation

This state is managed by CleanSpeak, it may be changed by setting it on this request.

users[x].registrations[x].verifiedBooleanoptionalDefaults to false

Set this value to true in order to mark this registration as verified.

users[x].registrations[x].verifiedInstantLongoptionalDefaults to now

The instant this registration was verified. This value will be ignored if verified is false.

users[x].saltStringoptional

The User's password salt. Required if encryptionScheme is provided. Empty string is allowed.

When using any of the FusionAuth provided password encryption schemes, the incoming value is expected to be a Base64 encoded string of UTF-8 bytes. If your salt is stored as a plain string, you will need to Base64 encode it prior to import.

users[x].tenantIdUUIDoptional

The User's tenant Id. If more than one tenant exists and you do not provide the X-FusionAuth-TenantId HTTP header or use API key to indicate which Tenant this request is for then this field will be required to complete the request.

When an API key is used that is locked to a Tenant or the tenant X-FusionAuth-TenantId HTTP header is provided this field is ignored.

users[x].timezoneStringoptional

The User's preferred timezone. The string must be in an IANA time zone format.

users[x].twoFactor.methods[x].authenticator.algorithmStringoptional

The algorithm used by the TOTP authenticator. With the current implementation, this must be HmacSHA1 if provided. Any other value will be ignored.

users[x].twoFactor.methods[x].authenticator.codeLengthIntegeroptional

The length of code generated by the TOTP. With the current implementation, this must be 6 if provided. Any other value will be ignored.

users[x].twoFactor.methods[x].authenticator.timeStepIntegeroptional

The time-step size in seconds. With the current implementation, this must be 30 if provided. Any other value will be ignored.

users[x].twoFactor.methods[x].emailStringoptional

The value of the email address for this method. Only present if user.twoFactor.methods[x].method is email.

users[x].twoFactor.methods[x].methodStringoptional

The type of this method. There will also be an object with the same value containing additional information about this method. The possible values are:

  • authenticator
  • email
  • sms
users[x].twoFactor.methods[x].mobilePhoneStringoptional

The value of the mobile phone for this method. Only present if user.twoFactor.methods[x].method is sms.

users[x].twoFactor.methods[x].secretStringoptional

A base64 encoded secret.

This field is required when method is authenticator.

users[x].twoFactor.recoveryCodesArray<String>optional

A list of recovery codes. These may be used in place of a code provided by an MFA factor. They are single use.

If a recovery code is used in a disable request, all MFA methods are removed. If, after that, a Multi-Factor method is added, a new set of recovery codes will be generated.

Since 1.68.0, plaintext recovery codes provided here will be hashed during import. For large imports with many users having recovery codes, this will increase import time. To avoid this, pre-hash recovery codes and provide users[x].twoFactor.recoveryCodeEncryptionScheme and users[x].twoFactor.recoveryCodeWorkFactor.

users[x].twoFactor.recoveryCodeEncryptionSchemeStringoptionalAvailable since 1.68.0

The encryption scheme used to hash the recovery codes provided in users[x].twoFactor.recoveryCodes. When provided, FusionAuth will treat the recovery codes as pre-hashed values and will not hash them again during import.

If this field is omitted, any plaintext recovery codes provided in users[x].twoFactor.recoveryCodes will be hashed using the default scheme during import.

Any supported password encryption scheme may be used. See users[x].encryptionScheme for FusionAuth-provided values.

users[x].twoFactor.recoveryCodeWorkFactorIntegeroptionalAvailable since 1.68.0

The work factor used when the recovery codes in users[x].twoFactor.recoveryCodes were hashed. Required when users[x].twoFactor.recoveryCodeEncryptionScheme is provided.

users[x].twoFactorDeliveryStringoptionalDefaults to NoneDEPRECATED

The User's preferred delivery for verification codes during a two-factor login request.

The possible values are:

  • None
  • TextMessage

When using TextMessage the User will also need a valid mobilePhone.

Removed in 1.26.0
users[x].twoFactorEnabledBooleanoptionalDefaults to false

Determines if the User has two-factor authentication enabled for their account or not.

See the Enable Two-Factor and Disable Two-Factor APIs as an alternative to performing this action using the User API.

Removed in 1.26.0
users[x].twoFactorSecretStringoptionalDEPRECATED

The Base64 encode secret used to generate Two-Factor verification codes.

You may optionally use value provided in the secret field returned by the Two-Factor Secret API instead of generating this value yourself.

Unless you are using TextMessage as your delivery type, ensure you are able to share the secret with the User before enabling Two-Factor authentication. Beginning in version 1.17.0, if you do create a User with TextMessage set as the twoFactorDelivery type and you omit this value, the secret will be generated for you. The secret can be generated because it is not necessary to share the secret with the User for this delivery method.

When using None as the twoFactorDelivery this value will be required.

Removed in 1.26.0
users[x].usernameStringoptional

The username of the User.

users[x].usernameStatusStringoptionalDefaults to ACTIVE

The current status of the username. This is used if you are moderating usernames via CleanSpeak. The possible values are:

  • ACTIVE - the username is active
  • PENDING - the username is pending approval/moderation
  • REJECTED - the username was rejected during moderation

This state is managed by CleanSpeak, it may be changed by setting it on this request.

users[x].verifiedBooleanoptionalDefaults to false

Set this value to true in order to mark the user as having a verified email address and/or phone number. If the user has both identity types, both will be marked as verified.

users[x].verifiedInstantLongoptionalDefaults to now

The instant this user's email address or phone number was verified. This value will be ignored if verified is false.

validateDbConstraintsBooleanoptionalDefaults to false

Set this value to true in order to perform additional validation of the request.

The import request is intended to be used to populate the initial set of users, this means FusionAuth does not expect to find duplicate users in the database. If a duplicate is encountered a 500 will be returned without this additional validation.

If you intend to use this API with existing users in FusionAuth set this value to true to request additional validation be performed on the input request and a 400 response will be returned with a JSON body indicating the duplicate values encountered.

Setting this value to true will dramatically decrease the performance of this request. If importing large numbers of users in a single request you may need to increase request timeouts to ensure this request does not timeout before it has completed.

Example Request JSON

{
  "users": [
    {
      "active": true,
      "birthDate": "1976-05-30",
      "data": {
        "displayName": "Johnny Boy",
        "favoriteColors": [
          "Red",
          "Blue"
        ]
      },
      "email": "example@fusionauth.io",
      "encryptionScheme": "salted-sha256",
      "expiry": 1571786483322,
      "factor": 24000,
      "firstName": "John",
      "fullName": "John Doe",
      "imageUrl": "http://65.media.tumblr.com/tumblr_l7dbl0MHbU1qz50x3o1_500.png",
      "insertInstant": 1331449200000,
      "lastName": "Doe",
      "memberships": [
        {
          "data": {
            "externalId": "cc6714c6-286c-411c-a6bc-ee413cda1dbc"
          },
          "groupId": "2cb5c83f-53ff-4d16-88bd-c5e3802111a5"
        }
      ],
      "middleName": "William",
      "mobilePhone": "303-555-1234",
      "password": "5ac152b6f8bdb8bb12959548d542cb237c4a730064bf88bbb8dd6e204912baad",
      "passwordChangeRequired": false,
      "phoneNumber": "303-555-1212",
      "preferredLanguages": [
        "en",
        "fr"
      ],
      "registrations": [
        {
          "applicationId": "00000000-0000-0000-0000-000000000002",
          "data": {
            "birthplace": "Bremen"
          },
          "insertInstant": 1331449200000,
          "preferredLanguages": [
            "de"
          ],
          "roles": [
            "moderator"
          ],
          "username": "Mausebär",
          "verified": true
        }
      ],
      "salt": "NDdiYWZkZDMtYjk5ZC00ZmZkLWE1YmUtZTQxNGM4MDkwNWYw",
      "timezone": "America/Denver",
      "twoFactor": {
        "methods": [
          {
            "authenticator": {
              "algorithm": "HmacSHA1",
              "codeLength": 6,
              "timeStep": 30
            },
            "secret": "aGVsbG8Kd29ybGQKaGVsbG8gaGVsbG8=",
            "method": "authenticator"
          },
          {
            "method": "sms",
            "mobilePhone": "555-555-5555"
          },
          {
            "method": "email",
            "email": "example@fusionauth.io"
          }
        ]
      },
      "usernameStatus": "ACTIVE",
      "username": "johnny123",
      "verified": true
    }
  ]
}

Response#

Only a status code is available on the Import API, no JSON body will be returned.

Response Codes
CodeDescription
200The request was successful. The response will contain a JSON body.
400The request was invalid and/or malformed. The response will contain an Errors JSON Object with the specific errors. This status will also be returned if a paid FusionAuth license is required and is not present.
401You did not supply a valid Authorization header. The header was omitted or your API key was not valid. The response will be empty. See Authentication.
500There was an internal error. A stack trace is provided and logged in the FusionAuth log files. The response will be empty.
503The search index is not available or encountered an exception so the request cannot be completed. The response will contain a JSON body.
504One or more Webhook endpoints returned an invalid response or were unreachable. Based on the transaction configuration for this event your action cannot be completed. A stack trace is provided and logged in the FusionAuth log files.

Password Hashes#

Password hashes can be imported into FusionAuth. This allows users to transparently use their old passwords while at no time exposing the plaintext password. This section contains details about importing specific password hashes.

You can also learn more about hashes in the password hashing reference, and you can implement your own custom hash as well.

Encoding#

The standard FusionAuth password hashing schemes require the password hash to be a base64 encoded string. If your password hashes are encoded in a different format, they will need to either be converted and imported as base64, or you can create a custom plugin using an alternative encoding. You can see an example of converting below.

It is recommended to use a base64 encoded string, but if importing hashes from a legacy system that uses base16, base32, or another encoding, import the hash in the same format the plugin produces.

Example of Converting a Base16 Password Hash Value to Base64

hash_in_hex = "25d55ad283aa400af464c76d713c07ad"

def hex_to_base64_digest(hexdigest)
  [[hexdigest].pack("H*")].pack("m0")
end

puts hex_to_base64_digest(hash_in_hex)

Bcrypt#

When importing a bcrypt hash, you may have a value such as:

$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy

This single string represents the bcrypt version, factor, salt and hash.

The number before the final $ is the factor, the 22 characters after the final $ is the salt and the last 31 characters are the hash.

For the above bcrypt hash, 10 is the factor and should be placed in the factor field. N9qo8uLOickgx2ZMRZoMye is the salt and should be placed in the salt field. IjZAgcfl7p92ldGxad68LJZdL17lhWy is the hash value and should be placed in the password field.

MD5#

When importing an MD5 password hash, you may not have a salt. If this is the case, use the empty string, '', as the salt. You may still use the salted MD5 plugin provided by FusionAuth.

MD5 is commonly stored in hexadecimal format, such as 25d55ad283aa400af464c76d713c07ad. The FusionAuth Import Users API requires imported password hashes to be base64 encoded. Convert any hexadecimal values to base64 before importing, or the import will not work.

Here is an example ruby script to convert hexadecimal values to base64 encoded:

Convert a Hexadecimal Password Hash Value to Base64

hash_in_hex = "25d55ad283aa400af464c76d713c07ad"

def hex_to_base64_digest(hexdigest)
  [[hexdigest].pack("H*")].pack("m0")
end

puts hex_to_base64_digest(hash_in_hex)