Skip to main content

Import identities

Ory allows you to import identities from any other system. To import identities, you use the same endpoint as for creating identities. The main difference between creating and importing identities is that when you import identities, you must provide the credentials field.

Importing verified addresses

Use the verifiable_addresses field to import a verified address like an email address.

danger

You must ensure that address verification is enabled and that the verifiable_address is present in the identity's traits. If the identity traits do not have the address set as the "verified address" type, the imported values will be deleted on the next identity update.

This is a sample payload for importing an identity with a verified address:

{
"schema_id": "preset://email",
"traits": {
"email": "docs-verify@example.org"
},
"verifiable_addresses": [
{
"value": "docs-verify@example.org",
"verified": true,
"via": "email",
"status": "completed"
}
]
}

Test the above example with a cURL command:

curl --request POST -sL \
--header "Authorization: Bearer ory_pat_xRKLsFEOUFQFVBjd6o3FQDifaLYhabGd" \
--header "Content-Type: application/json" \
--data '{
"schema_id": "preset://email",
"traits": {
"email": "docs-verify@example.org"
},
"verifiable_addresses": [
{
"value": "docs-verify@example.org",
"verified": true,
"via": "email",
"status": "completed"
}
]
}' https://$PROJECT_SLUG.projects.oryapis.com/admin/identities

The API response contains the created identity:

{
"id": "880052ae-d32c-4b56-b82d-0dc711080910",
"schema_id": "preset://email",
"schema_url": "https://$PROJECT_SLUG.projects.oryapis.com/schemas/cHJlc2V0Oi8vZW1haWw",
"state": "active",
"state_changed_at": "2022-02-24T15:33:17.845589803Z",
"traits": {
"email": "docs-verify@example.org"
},
"verifiable_addresses": [
{
"id": "c3f67b59-ab58-410b-971a-06b80f38468a",
"value": "docs-verify@example.org",
"verified": true,
"via": "email",
"status": "completed",
"created_at": "2022-02-24T15:33:17.848941Z",
"updated_at": "2022-02-24T15:33:17.848941Z"
}
],
"recovery_addresses": [
{
"id": "819b53bf-79e3-452e-8a9b-0323ec9d193c",
"value": "docs-verify@example.org",
"via": "email",
"created_at": "2022-02-24T15:33:17.849758Z",
"updated_at": "2022-02-24T15:33:17.849758Z"
}
],
"created_at": "2022-02-24T15:33:17.848475Z",
"updated_at": "2022-02-24T15:33:17.848475Z"
}

Importing recovery addresses

It is possible to import a list of recovery_addresses - similar to verifiable_addresses. It is better to let the identity schema handle setting the appropriate fields since there is no status to set for this address type.

We don't recommend setting these fields as they will be overwritten by other self-service flows. For more information on account recovery read the account recovery documentation.

Importing credentials

Ory supports importing credentials for identities including passwords and social sign-in connections.

Clear text password

To import a clear text password, provide the password in the JSON payload.

danger

Password imports don't use any password validation. Users have to update their password according to the policy themselves using self-service flows.

{
"schema_id": "preset://email",
"traits": {
"email": "docs-cleartext@example.org"
},
"credentials": {
"password": {
"config": {
"password": "the-password"
}
}
}
}

The password the-password will then be hashed according to the configured password hashing algorithm and stored in the database. The identity will be able to sign in using docs-cleartext@example.org and the-password as credentials.

Hashed passwords

To import a hashed password, provide the hashed password in the JSON payload.

{
"schema_id": "preset://email",
"traits": {
"email": "docs-hash@example.org"
},
"credentials": {
"password": {
"config": {
"hashed_password": "$2a$10$ZsCsoVQ3xfBG/K2z2XpBf.tm90GZmtOqtqWcB5.pYd5Eq8y7RlDyq"
}
}
}
}

The value of the hashed password is different depending on the algorithm used. The following algorithms are supported:

Ory Identities can hash passwords by BCrypt and can compare stored BCrypt hash and migrate if configured hasher (hashers.algorithm) isn't BCrypt.

BCrypt format is described here.

Password migration using a web hook

If you want to import users, but do not have access to the hashed password, or the hashed password is in a format that Ory Identities does not support, you can use a web hook to migrate the password.

The web hook will be called when the user logs in, and will receive the user's identifier and password. If the web hook returns successfully, the user will be logged in and the hashed password will be stored.

The following steps are necessary to set up password migration using a web hook:

  1. Import an identity with an empty password hash and use_password_migration_hook set to true

    Bulk-import all your users with an empty password hash and use_password_migration_hook set:

    {
    "schema_id": "preset://email",
    "traits": {
    "email": "pw-migration@example.org"
    },
    "credentials": {
    "password": {
    "config": {
    "hashed_password": "",
    "use_password_migration_hook": true
    }
    }
    }
    }
  2. Configure a password migration web hook

    Add the web hook URL to the configuration. See the web hook configuration for authentication options.

    selfservice:
    methods:
    password:
    config:
    migrate_hook:
    enabled: true
    config:
    url: https://example.org/migrate-password
    auth:
    type: api_key
    config:
    name: Authorization
    value: { API Key value }
    in: header # alternatively "cookie"
  3. Implement the password migration web hook

    If a user logs in and the identifier points to an identity that has use_password_migration_hook set to true, Ory Identities will call the configured web hook URL with the following payload:

    {
    "identifier": "pw-migration@example.org",
    "password": "the-password"
    }

    The web hook can then check the identifier and password against the legacy system. If the password matches, the web hook must respond with a 200 OK status code and the following payload:

    {
    "status": "password_match"
    }

    After a successful response, the identity will be updated with the hashed password and the user will be logged in. The password migration hook will not be called again for this identity.

    Any other response will be treated as an invalid password, and the user will be notified that the password is incorrect.

Social sign-in connections

When importing social sign-in connections, the provider field is the social sign-in provider ID you set in your social sign-in configuration. The subject ID must be the ID of the user on the given platform. Usually, this is the sub claim of the OpenID Connect ID Token provider such as Google.

{
"schema_id": "preset://email",
"traits": {
"email": "docs-oidc@example.org"
},
"credentials": {
"oidc": {
"config": {
"providers": [
{
"provider": "github",
"subject": "12345"
},
{
"provider": "google",
"subject": "12345"
}
]
}
}
}
}

Bulk import identities from other providers

To import multiple identities into Ory Identities, use the Identity Import API.

A maximum of 2000 identities can be created in a single request. If you need to import more identities, split the import into multiple requests.

The endpoint accepts a JSON array of identities, each of which must have a create property that holds the identity that should be created. Optionally, you can specify a patch_id property (which must be a UUID) which will be returned in the response. This can be used to correlate the response with the patch.

The following example shows how to import two identities. It will create two identities with the email addresses foo@example.com and bar@example.com and the passwords foopassword and barpassword respectively.

curl --location --request PATCH 'https://${YOUR_PROJECT_SLUG}.projects.oryapis.com/admin/identities' \
--header 'Authorization: Bearer ${YOUR_ORY_ACCESS_TOKEN}' \
--header 'Content-Type: application/json' \
--data-raw '{
"identities": [
{
"patch_id": "6086b0a8-d851-5431-91b4-b6e5e39dc88b",
"create": {
"credentials": {
"password": {
"config": {
"password": "foopassword"
}
}
},
"state": "active",
"traits": {
"email": "foo@example.com"
},
"schema_id": "preset://email"
}
},
{
"patch_id": "d554dc00-49ce-5381-9bdc-79637dec85a2",
"create": {
"credentials": {
"password": {
"config": {
"password": "barpassword"
}
}
},
"state": "active",
"traits": {
"email": "bar@example.com"
},
"schema_id": "preset://email"
}
}
]
}'

The service will respond with the two identity IDs created. Note that the patch_id is returned in the response to correlate the response with the request.

{
"identities": [
{
"action": "create",
"patch_id": "6086b0a8-d851-5431-91b4-b6e5e39dc88b",
"identity": "55f93ea4-09ff-4273-8b88-082cc70d6d44"
},
{
"action": "create",
"patch_id": "d554dc00-49ce-5381-9bdc-79637dec85a2",
"identity": "f70c9b29-4790-4330-90dc-920db16a4b85"
}
]
}

Errors during bulk import

Failure to import an identity will not fail the whole bulk import. In the response, "action": "error" indicates that the identity with the corresponding patch_id (which can be set on each identity passed to PATCH /admin/identities) could not be imported. The error object contains more details about why the import failed.

This is an example response where two identities were created and two imports failed:

{
"identities": [
{
"action": "create",
"identity": "0d0ed560-43ce-42a9-bd40-aafe921c3af1",
"patch_id": "6086b0a8-d851-5431-91b4-b6e5e39dc88b"
},
{
"action": "error",
"patch_id": "d554dc00-49ce-5381-9bdc-79637dec85a2",
"error": {
"code": 400,
"status": "Bad Request",
"reason": "The request was malformed or contained invalid parameters",
"message": "The request was malformed or contained invalid parameters"
}
},
{
"action": "error",
"patch_id": "1634f1e9-8419-5a54-8191-2260c8aaea31",
"error": {
"code": 409,
"status": "Conflict",
"reason": "This identity conflicts with another identity that already exists.",
"message": "The resource could not be created due to a conflict"
}
},
{
"action": "create",
"identity": "b8651770-be8d-460b-b86f-679c4ba50264",
"patch_id": "eade6651-9311-5624-8afd-e4a3e05e0c4a"
}
]
}