Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

suborgs tidying #51

Merged
merged 3 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions docs/getting-started/Organizations.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ In general it is recommended to keep the number of resources in an organization,

Organizations have resource limits, which are described in detail [here](/faq)

## Sub Organization

A sub-organization is an segregated organization that is nested within a parent organization. By default, the parent organization has read access to all sub-organizations, but no write access. That means users in the parent organization have no ability to use private keys or alter any resources in the sub-org by default. For more information on sub-organizations and common use cases for this functionality, keep reading.
## Sub-Organization

A sub-organization is a segregated organization that is nested within a parent organization. By default, the parent organization has *read* access to all sub-organizations, but no *write* access. That means users in the parent organization have no ability to use private keys or alter any resources in the sub-org by default. For more information on sub-organizations and common use cases for this functionality, follow along in the next section 👉.
111 changes: 77 additions & 34 deletions docs/getting-started/Sub-Organizations.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,45 @@ sidebar_position: 4
description: Learn about sub-orgs and how you can use them
slug: /getting-started/sub-organizations
---
# Sub-organizations
# Sub-Organizations

Using Turnkey’s flexible infrastructure, you can programmatically create and manage Sub-Organizations for your end users. Sub-Organizations aren't subject to size limits: you can create as many Sub-Organizations as needed. The parent organization has **read-only** visibility into all of its Sub-Organizations, and activities performed in Sub-Organizations roll up to the parent for billing purposes.
Using Turnkey’s flexible infrastructure, you can programmatically create and manage sub-organizations for your end-users. sub-organizations aren't subject to size limits: you can create as many sub-organizations as needed. The parent organization has **read-only** visibility into all of its sub-organizations, and activities performed in sub-organizations roll up to the parent for billing purposes.

We envision Sub-Organizations being very useful to model your End-Users if you're a business using Turnkey for key management. Let's explore how.
We envision sub-organizations being very useful to model your End-Users if you're a business using Turnkey for key management. Let's explore how.

## Creating Sub-Organizations

Creating a new Sub-Organization is an activity in the parent organization. The activity itself takes the following attributes as inputs:
Creating a new sub-organization is an activity performed by the parent organization. The activity itself takes the following attributes as inputs:
- organization name
- a list of root users
- a root quorum threshold
- [optional] a wallet (note: in versions prior to V4, this was a private key)

Root users in the root users list can be programmatic or human, with one or many credentials attached. Below we explain how you might want to use this primitive as a way to model end-user controlled wallets, or custodial wallets. If you have another use-case in mind, or questions/feedback on this page, reach out to [[email protected]](mailto:[email protected])!
Root users can be programmatic or human, with one or many credentials attached. Below we explain how you might want to use this primitive as a way to model end-user controlled wallets, or custodial wallets. If you have another use-case in mind, or questions/feedback on this page, reach out to [[email protected]](mailto:[email protected])!

## Sub-Organizations as End-User controlled Wallets
## Sub-Organizations as end-user controlled wallets

### Overview

Turnkey has built a new model for private key management that utilizes secure enclaves. All transactions are signed within an enclave and private keys are never exposed to Turnkey, your software, or your team. Turnkey’s role is similar to that of a safety deposit box operator — Turnkey secures and provides access to the safety deposit boxes, but our system requires cryptographic proof of ownership to take any action with the keys held within.

In this example wallet implementation, you will create a segregated sub-organization for each end-user, and leverage [passkeys](https://www.passkeys.io/) as cryptographic proof of ownership to ensure only the end user has the ability to approve signing with their private key.
In this example wallet implementation, you will create a segregated sub-organization for each end-user, and leverage [passkeys](https://www.passkeys.io/) as cryptographic proof of ownership to ensure only the end-user has the ability to approve signing with their private key.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could link our own passkey docs here: https://docs.turnkey.com/category/using-passkeys


### Before you start

Make sure you’ve set up your primary Turkey Organization as well as one or more API-only users that will programmatically manage user onboarding within your application. Check out the [quickstart guide](quickstart) if you need help getting started.
Make sure you’ve set up your primary Turkey organization as well as one or more users with API access that will programmatically manage user onboarding within your application. Check out the [quickstart guide](quickstart) if you need help getting started.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turkey -> Turnkey 😁


Note also that unlike some wallet providers, Turnkey is not a customer authentication platform. This gives you the flexibility to create the user experience you envision. Typically, developers implement their own standard end-user authentication flows for user login, then employ passkeys behind that login for transaction signing.

### Step 1: End user sub-org creation
### Step 1: End-user sub-organization creation

After the end user is logged in, your application prompts the user for passkey creation on the application domain. Our JavaScript SDK has a helper for this: `getWebAuthnAttestation`. See [this example](https://github.com/tkhq/sdk/tree/main/examples/with-federated-passkeys).
After the end-user is logged in, your application prompts the user for passkey creation on the application domain. Our JavaScript SDK has a helper for this: `getWebAuthnAttestation`. See [this example](https://github.com/tkhq/sdk/tree/main/examples/with-federated-passkeys).

The application then uses an API-only user to create a new sub-organization on behalf of the end user. Here's what the activity would look like:
```sh
The application then uses an API-only user to create a new sub-organization on behalf of the end-user. Here's what the activity would look like:

```json
{
"type": "ACTIVITY_TYPE_CREATE_SUB_ORGANIZATION_V2",
"type": "ACTIVITY_TYPE_CREATE_SUB_ORGANIZATION_V4",
"timestampMs": "<time-in-ms>",
"organizationId": "<your-organization-id>",
"parameters": {
Expand All @@ -59,21 +61,35 @@ The application then uses an API-only user to create a new sub-organization on b
}],
"apiKeys": [],
}],
"rootQuorumThreshold": 1
"rootQuorumThreshold": 1,
"wallet": {
"walletName": "Default ETH Wallet",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's drop the ETH here since it might confuse people into thinking that you can only use this wallet for ETH

"accounts": [
{
"curve": "CURVE_SECP256K1",
"pathFormat": "PATH_FORMAT_BIP32",
"path": "m/44'/60'/0'/0/0",
"addressFormat": "ADDRESS_FORMAT_ETHEREUM",
},
],
},
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be helpful to explain the expected response shape. If you feel like it muddies the water skip it!

{
  sub_organization_id: "" // the organization_id that the end-user must use when signing requests
  wallet: {
    wallet_id: "" // the wallet_id used to generate more accounts
    addresses: "" // the address used to sign with
  }
}

```
With this setup each end-user now has sole control over their Sub-Organization and any resources created within it. Your application cannot take any actions on resources within the Sub-Organization without explicitly cryptographic authorization from the end user in the form of a passkey signature.

It's important to note that the initial activity to create a sub-organization has to be authorized by an API key or a user in your main Turnkey organization. Otherwise anyone would be able to create sub-organizations in your organization! Here's an [example](https://github.com/tkhq/sdk/blob/a2bfbf3cbd6040902bbe4c247900ac560be42925/examples/with-federated-passkeys/src/pages/index.tsx#L88-L116) where the initial registration is done, and posted to a NextJS backend. The NextJS backend inserts the attestation and signs the "create sub-organization" activity [here](https://github.com/tkhq/sdk/blob/a2bfbf3cbd6040902bbe4c247900ac560be42925/examples/with-federated-passkeys/src/pages/api/subOrg.ts#L25-L82).
Note: by default, root users created with sub-organizations will have both API and authenticator (e.g. passkey) access permitted.

With this setup each end-user now has sole control over their sub-organization and any resources created within it. Your application cannot take any actions on resources within the sub-organization without explicitly cryptographic authorization from the end-user in the form of a passkey signature.

It's important to note that the initial activity to create a sub-organization has to be authorized by an API key or a user in your main Turnkey organization. Otherwise anyone would be able to create sub-organizations in your organization! Here's an [example](https://github.com/tkhq/sdk/blob/a2bfbf3cbd6040902bbe4c247900ac560be42925/examples/with-federated-passkeys/src/pages/index.tsx#L88-L116) where the initial registration is done, and posted to a NextJS backend. The NextJS backend inserts the attestation and signs the `CREATE_SUB_ORGANIZATION_V4` activity [here](https://github.com/tkhq/sdk/blob/ba360baeb60d80276f7faeca602b99190fe5affe/examples/with-federated-passkeys/src/pages/api/createSubOrg.ts#L27-L106).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can link wallet.tx.xyz as a live link potentially. Might easier for people to grok what's happening if they can both interact with the demo and look at the code.


#### Step 2: Creating a wallet

A user interface on your application prompts users to sign with their passkey to create a new wallet. This signature is used to produce a signed Turnkey request. Here are the request components:

- URL: `https://api.turnkey.com/api/v1/create_private_keys`
- URL: `https://api.turnkey.com/api/v1/create_wallet`
- `X-Stamp-Webauthn` header: set to the WebAuthn stamp collected on the application's frontend (the End-User passkey signature)
- The request body: `CREATE_PRIVATE_KEYS` activity request.
- The request body: `CREATE_WALLET` activity request.

We've abstracted getting WebAuthn signatures and creating signed Turnkey requests behind typed methods (e.g. `stampCreatePrivateKeys`).

Expand All @@ -93,17 +109,18 @@ const httpClient = new TurnkeyClient(
stamper
);

const signedRequest = await httpClient.stampCreatePrivateKeys({
type: "ACTIVITY_TYPE_CREATE_PRIVATE_KEYS_V2",
const signedRequest = await httpClient.stampCreateWallet({
type: "ACTIVITY_TYPE_CREATE_WALLET",
organizationId: "<user sub-organization>",
timestampMs: String(Date.now()),
parameters: {
privateKeys: [
walletName: "New ETH Wallet",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since in step 1 we've created a wallet, I wonder if we should show how to derive additional accounts from that wallet instead? ACTIVITY_TYPE_CREATE_WALLET_ACCOUNTS

accounts: [
{
privateKeyName: "<name>",
curve: "CURVE_SECP256K1",
addressFormats: ["ADDRESS_FORMAT_ETHEREUM"],
privateKeyTags: [],
pathFormat: "PATH_FORMAT_BIP32",
path: "m/44'/60'/0'/0/0",
addressFormat: "ADDRESS_FORMAT_ETHEREUM",
},
],
},
Expand All @@ -112,22 +129,36 @@ const signedRequest = await httpClient.stampCreatePrivateKeys({

The `signedRequest` contains all the components needed to forward it to turnkey: URL, body, and a stamp header (with name and value properties).

You can choose to send this request straight from your frontend, or proxy it through your backend server. If you want to send from the frontend, you can use `httpClient.createPrivateKeys` instead.
You can choose to send this request straight from your frontend, or proxy it through your backend server. If you want to send from the frontend, you can use `httpClient.createWallet` instead.

#### Step 3: Transaction signing

Similar to creating a wallet, the end-user must provide a signature over each "Sign Transaction" activity with their passkey. A user action, for example clicking "Withdraw Rewards", might trigger the flow. The details of this transaction are presented to the user for confirmation, followed by a request for their passkey to sign the Turnkey request. The signed request is then proxied and POSTed to Turnkey.

```json
{
"type": "ACTIVITY_TYPE_SIGN_TRANSACTION_V2",
"timestampMs": "<time-in-ms>",
"organizationId": "<your-organization-id>",
"parameters": {
"signWith": "<wallet account address, private key id, or private key address>",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since this is a quickstart and we don't mention private keys , let's just put <wallet account address> instead

"type": "TRANSACTION_TYPE_ETHEREUM",
"unsignedTransaction": "<your-unsigned-transaction>",
}
}
```

Turnkey returns a signed transaction which your application can broadcast using any provider you'd like.

## Sub-Organizations as custodial Wallets
## Sub-Organizations as custodial wallets

Most of the steps outlined in the previous section remain unchanged: application creating custodial wallets should still create segregated Sub-Organization for their end-users to avoid limits (we currently have a maximum of 500 users per organization).
Most of the steps outlined in the previous section remain unchanged: applications creating custodial wallets should still create segregated sub-organizations for their end-users to avoid limits (we currently have a maximum of 100 users per organization, whereas an organization can have unlimited sub-organizations).

The main difference in the Quorum settings: upon creating a new sub-organization, your business' API key is used to bootstrap each End-User organization. The "CREATE_SUB_ORGANIZATION_V2" activity becomes:
```sh
The main difference is in the Root Quorum settings: upon creating a new sub-organization, your business' API key is used to bootstrap each end-user organization. The `CREATE_SUB_ORGANIZATION_V4` activity becomes:

```json
{
"type": "ACTIVITY_TYPE_CREATE_SUB_ORGANIZATION_V2",
"type": "ACTIVITY_TYPE_CREATE_SUB_ORGANIZATION_V4",
"timestampMs": "<time-in-ms>",
"organizationId": "<your-organization-id>",
"parameters": {
Expand All @@ -141,22 +172,34 @@ The main difference in the Quorum settings: upon creating a new sub-organization
"publicKey": "<your-business-api-public-key>"
],
}],
"rootQuorumThreshold": 1
"rootQuorumThreshold": 1,
"wallet": {
"walletName": "Default ETH Wallet",
"accounts": [
{
"curve": "CURVE_SECP256K1",
"pathFormat": "PATH_FORMAT_BIP32",
"path": "m/44'/60'/0'/0/0",
"addressFormat": "ADDRESS_FORMAT_ETHEREUM",
},
],
},
}
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow Github is losing its mind here
image


(Note the empty "authenticators" list!)

Key creation and signatures can also be performed with this root API user, and the end-user doesn't need to be involved in the activity signing process.

Policies can be use to segregate permissions if needed: you could, for example, bootstrap each sub-org with 2 API users: one to create keys and setup the organization policies; the other to sign transaction.
Policies can be use to segregate permissions if needed: you could, for example, bootstrap each sub-org with 2 API users: one to create keys and setup the organization policies; the other to sign transactions.

## Sub-Organizations as shared Wallets

For the sake of completeness: it is possible to create "shared custody" wallets with the Sub-Organization primitive. To do this, an application would setup sub-organizations with the following settings:
For the sake of completeness: it is possible to create "shared custody" wallets with the sub-organization primitive. To do this, an application would setup sub-organizations with the following settings:

- Root quorum threshold: 2
- Root users:
- 1 user representing the end-user (with their Passkey as an authenticator)
- 1 user representing the business (with an API key attached)

The signing process would then have to involve **both** the user and the business since the root quorum threshold is 2.
The signing process would then have to involve **both** the user and the business since the root quorum threshold is 2.
2 changes: 1 addition & 1 deletion docs/user-management/Introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Turnkey Users are resources within an Organization. Their attributes are:
- API key: a list of API keys (see below for information)
- User tags: a list of User Tag UUIDs

A **user belongs to one organization**, and one organization can have many (**up to 500**) users. If you need to create more users, consider using Sub-Organizations.
A **user belongs to one organization**, and one organization can have many (**up to 100**) users. If you need to create more users, consider using Sub-Organizations.

## User Credentials

Expand Down