Skip to main content

Migration to SSO keeping original accounts

Transferring SSO external_ids to original users

If you have previously used EMS prior to setting up SSO, you will likely want to continue using your existing Matrix accounts instead of migrating over to newly created SSO accounts. You can do this by transferring the 'external_ids' from these new accounts, to your original accounts instead. To do this, you will need to use the Admin API.

Admin API Documentation

Getting an Access Token

Before being able to use the Admin API, you will need an admin account and it's 'Access Token', you can make a user an admin by either following the steps in the link above, or following these steps on the EMS Control Panel:

  1. Access the 'Server Admin' tab
  2. Under the 'Users' tab, select the user that should be made Synapse Admin
  3. Click the checkbox, next to 'Synapse Admin' and click 'Yes' to confirm

Once a user is a Synapse Admin, you can retrieve their 'Access Token' by logging in via the Element Matrix client:

  1. Click on the users' profile icon in the top-left and select 'All Settings'
  2. Open the 'Help & About' settings page, then scroll down to the 'Advanced' section
  3. Click 'Access Token' to reveal the token, copy this to interact with the Admin API

Using an Access Token

Access tokens will need to be passed into all API requests as an Authorization Bearer token, see examples below:

cURL:

-H 'Authorization: Bearer syt_b25l_example_2RCMR1'

Python:

import requests
headers = {
    'Authorization': 'Bearer syt_b25l_example_2RCMR1',
}

Getting SSO users' external_ids

Admin API List Accounts

Admin API Query User Account

  1. For each user you can then use their name in another GET request to /_synapse/admin/v2/users/<user_id>, replacing <user_id> with name.

    cURL:

    curl -X GET -H 'Authorization: Bearer syt_b25l_example_2RCMR1' https://example.ems.host:443/_synapse/admin/v2/users/@exampleuser:example.ems.host
    
  2. You will find the external_ids for each user within the JSON output. You can programatically run through all users and generate a list of only those with external_ids, removing unneeded information. (See example below)

    Python:

    import requests
    
    # REPLACE THESE VALUES WITH ACCESS TOKEN AND HOME SERVER URL
    headers = {
        'Authorization': 'Bearer syt_b25l_example_2RCMR1',
    }
    url = 'https://example.ems.host'
    
    # GET LIST OF ALL USERS ON HOME SERVER
    # OUTPUT: 'all_users' contains a list of all users
    next_token = '0'
    last_token = ''
    all_users = []
    get_users = requests.get(url + '/_synapse/admin/v2/users?from=' + next_token + '&limit=10&guests=false', headers=headers).json()
    for user in get_users['users']:
        all_users.append(user['name'])
    while ('next_token' in get_users) and (next_token != last_token):
        next_token = get_users['next_token']
        get_users = requests.get(url + '/_synapse/admin/v2/users?from=' + next_token + '&limit=10&guests=false', headers=headers).json()
        for user in get_users['users']:
            all_users.append(user['name'])
    
    # FOR EACH USER, GET ALL INFO, EXCLUDE THOSE WITHOUT 'external_ids'
    # OUTPUT: 'all_external_ids' contains a list of all users with external ids
    all_external_ids = []
    for user in all_users:
        get_user = requests.get(url + '/_synapse/admin/v2/users/' + user, headers=headers).json()
        if get_user['external_ids'].__len__() != 0:
            all_external_ids.append(
                {
                    'sso_username': user,
                    'original_username': '',
                    'external_ids': get_user['external_ids']
                }
            )
    
    

Transferring SSO external_ids information

Admin API Create or Modify Account

With all external_ids collected, you will need to identify each SSO Account and the associated original account that you'd like to transfer the associated SSO information over too.

If using the Python example above, you will need to store the original username within all_external_ids[X]['original_username'], replacing X with the index of the SSO user. If you create a dictionary with keys named of the SSO username, and values of the desired Original username you could use the following to update all_external_ids:

Python:

# ADD REQUIRED ORIGINAL USERNAME
for user in all_external_ids:
    dict_storing_sso2orig = {'@example_sso_user:example.ems.host': '@example_orig_user:example.ems.host'}
    user['original_username'] = dict_storing_sso2orig[str(user['sso_username'])]

Once you have related all SSO Usernames to Original Usernames you can then, using the link above, use a PUT request to /_synapse/admin/v2/users/<user_id> to change the external_ids data for each account. (Remember to remove the external_ids information from the soon to be defunct SSO accounts)

cURL:

curl -X PUT -H 'Authorization: Bearer syt_example_2RCMR1' -d '{"external_ids":[]}' https://example.ems.host:443/_synapse/admin/v2/users/@exampleuser:example.ems.host

Continuing with the Python example, you can now use this to remove the external_ids from each SSO account, and add that information to the associated Original account.

Python:

# REMOVE 'external_ids' from 'sso_username' ACCOUNTS FROM 'all_external_ids' THEN
# UPDATE ALL 'original_username' ACCOUNTS FROM 'all_external_ids' WITH 'external_ids' FROM 'sso_username'
for user in all_external_ids:
    data = '{"external_ids":' + str(user['external_ids']).replace("'", '"').replace(" ", "") + '}'
    remove_sso = requests.put(url + '/_synapse/admin/v2/users/' + user['sso_username'], headers=headers, data='{"external_ids":[]}')
    if remove_sso.status_code == 200:
        add_sso = requests.put(url + '/_synapse/admin/v2/users/' + user['original_username'], headers=headers, data=data)

The full python script is available below:

import requests

# REPLACE THESE VALUES WITH ACCESS TOKEN AND HOME SERVER URL
headers = {
    'Authorization': 'Bearer syt_example_2RCMR1',
}
url = 'https://example.ems.host'

# GET LIST OF ALL USERS ON HOME SERVER
# OUTPUT: 'all_users' contains a list of all users
next_token = '0'
last_token = ''
all_users = []
get_users = requests.get(url + '/_synapse/admin/v2/users?from=' + next_token + '&limit=10&guests=false',
                         headers=headers).json()
for user in get_users['users']:
    all_users.append(user['name'])
while ('next_token' in get_users) and (next_token != last_token):
    next_token = get_users['next_token']
    get_users = requests.get(url + '/_synapse/admin/v2/users?from=' + next_token + '&limit=10&guests=false',
                             headers=headers).json()
    for user in get_users['users']:
        all_users.append(user['name'])

# FOR EACH USER, GET ALL INFO, EXCLUDE THOSE WITHOUT 'external_ids'
# OUTPUT: 'all_external_ids' contains a list of all users with external ids
all_external_ids = []
for user in all_users:
    get_user = requests.get(url + '/_synapse/admin/v2/users/' + user, headers=headers).json()
    if get_user['external_ids'].__len__() != 0:
        all_external_ids.append(
            {
                'sso_username': user,
                'original_username': '',
                'external_ids': get_user['external_ids']
            }
        )

# ADD REQUIRED ORIGINAL USERNAME
# REPLACE CONTENTS OF 'dict_storing_sso2orig' TO SET UP RELATED SSO -> ORIGINAL ACCOUNTS
# CHANGE 'readme' VARIABLE BELOW TO 'True' TO CONTINUE
readme = False
dict_storing_sso2orig = {'@example_sso_user:example.ems.host': '@example_orig_user:example.ems.host'}
for user in all_external_ids:
    if readme is True:
        user['original_username'] = dict_storing_sso2orig[str(user['sso_username'])]

# REMOVE 'external_ids' from 'sso_username' ACCOUNTS FROM 'all_external_ids' THEN
# UPDATE ALL 'original_username' ACCOUNTS FROM 'all_external_ids' WITH 'external_ids' FROM 'sso_username'
# CHANGE 'dict_storing_sso2orig_check' VARIABLE BELOW TO 'True' TO CONTINUE
dict_storing_sso2orig_check = False
for user in all_external_ids:
    if (dict_storing_sso2orig_check is True) and (dict_storing_sso2orig != {'@example_sso_user:example.ems.host': '@example_orig_user:example.ems.host'}):
        data = '{"external_ids":' + str(user['external_ids']).replace("'", '"').replace(" ", "") + '}'
        remove_sso = requests.put(url + '/_synapse/admin/v2/users/' + user['sso_username'], headers=headers, data='{"external_ids":[]}')
        if remove_sso.status_code == 200:
            add_sso = requests.put(url + '/_synapse/admin/v2/users/' + user['original_username'], headers=headers, data=data)