Skip to content
Documentation/canton-network-docs/SDKsWallet SDKUsing the SDKv0 to v1 migrationView on canton-network-docs
canton-network-docs/SDKsWallet SDKUsing the SDKv0 to v1 migration

User Namespace

The user namespace provides methods for user management on the Canton Network.

Availability

The user namespace is always available as part of the basic SDK interface. It’s initialized automatically when you create an SDK instance and doesn’t require additional configuration via extend().
import { localNetStaticConfig, SDK } from '@canton-network/wallet-sdk'

export default async function () {
    const sdk = await SDK.create({
        auth: TOKEN_PROVIDER_CONFIG_DEFAULT,
        ledgerClientUrl: localNetStaticConfig.LOCALNET_APP_USER_LEDGER_URL,
    })

    const primaryParty = EXISTING_PARTY_1
    const userId = 'user-id'

    // user namespace is immediately available
    await sdk.user.create({ userId, primaryParty })
}

Key changes from v0 to v1

The distinction betwen the user ledger and admin ledger have been removed. Instead, the token is used to determine whether a user has admin rights. Creating users
await sdk.adminLedger.createUser('userId', primaryParty)

const result = await sdk.user
    .create({
      userId: 'userId',
      primaryParty: primaryParty,
      userRights: {...}
    })
Granting user rights
await sdk.userLedger.grantRights([actAsRights], [readAsRights])

const result = await sdk.rights
    .grant({
      userRights: {...},
      userId: {'userId'}, //optional parameter
      idp: {'idp'} //optional parameter otherwise will use default IDP
    })
The below example demonstrates the full usage of the feature:
Full example: multi-user setup and rights
import { localNetStaticConfig, SDK } from '@canton-network/wallet-sdk'
import { pino } from 'pino'
import { TOKEN_PROVIDER_CONFIG_DEFAULT } from './utils/index.js'
const logger = pino({ name: 'v1-multi-user-setup', level: 'info' })

logger.info('Operator sets up users and primary parties')

const operatorSdk = await SDK.create({
    auth: TOKEN_PROVIDER_CONFIG_DEFAULT,
    ledgerClientUrl: localNetStaticConfig.LOCALNET_APP_USER_LEDGER_URL,
})

const aliceInternal = await operatorSdk.party.internal.allocate({
    partyHint: 'v1-09-alice',
})

const bobInternal = await operatorSdk.party.internal.allocate({
    partyHint: 'v1-09-bob',
})

const masterPartyInternal = await operatorSdk.party.internal.allocate({
    partyHint: 'v1-09-master',
})

logger.info('Created the internal parties')

const aliceUser = await operatorSdk.user.create({
    userId: 'alice-user',
    primaryParty: aliceInternal,
    userRights: {
        participantAdmin: true,
    },
})

const bobUser = await operatorSdk.user.create({
    userId: 'bob-user',
    primaryParty: bobInternal,
    userRights: {
        participantAdmin: true,
    },
})

const masterUser = await operatorSdk.user.create({
    userId: 'master-user',
    primaryParty: masterPartyInternal,
    userRights: {
        participantAdmin: true,
    },
})

logger.info('created the users')

if (!(aliceUser || bobUser || masterUser)) {
    throw new Error(`One of the users was not created correctly`)
}

await operatorSdk.user.rights.grant({
    userId: masterUser.id!,
    userRights: {
        canExecuteAsAnyParty: true,
        canReadAsAnyParty: true,
    },
})

logger.info(
    `Created alice user: ${aliceUser.id} with primary party (internal) ${aliceUser.primaryParty}`
)
logger.info(
    `Created bob user: ${bobUser.id} with primary party (internal) ${bobUser.primaryParty}`
)
logger.info(
    `Created master user: ${masterUser.id} with primary party (internal) ${masterUser.primaryParty}, with read as and execute as rights`
)

const aliceSdk = await SDK.create({
    auth: {
        method: 'self_signed',
        issuer: 'unsafe-auth',
        credentials: {
            clientId: aliceUser.id,
            clientSecret: 'unsafe',
            audience: 'https://canton.network.global',
            scope: '',
        },
    },
    ledgerClientUrl: localNetStaticConfig.LOCALNET_APP_USER_LEDGER_URL,
})

const aliceKeyPair = aliceSdk.keys.generate()
const aliceExternal = await aliceSdk.party.external
    .create(aliceKeyPair.publicKey, {
        partyHint: 'v1-09-alice',
    })
    .sign(aliceKeyPair.privateKey)
    .execute()

logger.info(`alice created external party`)

const bobSdk = await SDK.create({
    auth: {
        method: 'self_signed',
        issuer: 'unsafe-auth',
        credentials: {
            clientId: bobUser.id,
            clientSecret: 'unsafe',
            audience: 'https://canton.network.global',
            scope: '',
        },
    },
    ledgerClientUrl: localNetStaticConfig.LOCALNET_APP_USER_LEDGER_URL,
})

const bobKeyPair = bobSdk.keys.generate()
const bobExternal = await bobSdk.party.external
    .create(bobKeyPair.publicKey, {
        partyHint: 'v1-09-bob',
    })
    .sign(bobKeyPair.privateKey)
    .execute()
logger.info(`bob created external party`)

const masterUserSdk = await SDK.create({
    auth: {
        method: 'self_signed',
        issuer: 'unsafe-auth',
        credentials: {
            clientId: masterUser.id,
            clientSecret: 'unsafe',
            audience: 'https://canton.network.global',
            scope: '',
        },
    },
    ledgerClientUrl: localNetStaticConfig.LOCALNET_APP_USER_LEDGER_URL,
})

const masterWalletView = await masterUserSdk.party.list()

if (!masterWalletView?.find((p) => p === aliceExternal.partyId)) {
    throw new Error('master user cannot see alice party')
}
if (!masterWalletView?.find((p) => p === bobExternal.partyId)) {
    throw new Error('master user cannot see bob party')
}

const aliceWalletView = await aliceSdk.party.list()
logger.info(aliceWalletView)

if (aliceWalletView?.find((p) => p === bobExternal.partyId)) {
    throw new Error('alice user can see bob party')
}

const bobWalletView = await bobSdk.party.list()

if (bobWalletView?.find((p) => p === aliceExternal.partyId)) {
    throw new Error('bob user can see alice party')
}

logger.info(
    'alice and bob have proper isolation and cannot see each others external parties'
)

//user management test
await bobSdk.user.rights.grant({
    userRights: {
        readAs: [aliceExternal.partyId],
    },
})

const bobWalletViewAfterGrantRights = await bobSdk.party.list()

if (!bobWalletViewAfterGrantRights?.find((p) => p === aliceExternal.partyId)) {
    throw new Error('bob user cannot see alice party even with ReadAs rights')
}

const bobRightsAfterGrantRights = await bobSdk.user.rights.list()

logger.info(bobRightsAfterGrantRights, 'Bob user rights')

await bobSdk.user.rights.revoke({
    userRights: {
        readAs: [aliceExternal.partyId],
    },
})

const bobWalletViewAfterRevokeRights = await bobSdk.party.list()

if (bobWalletViewAfterRevokeRights?.find((p) => p === aliceExternal.partyId)) {
    throw new Error('bob user can see alice party even after revoking rights')
}
See all 189 lines

Migration reference

v0 methodv1 method
sdk.adminLedger.createUsersdk.user.create
sdk.userLedger.grantRightssdk.user.rights.grant

See also