Module: MixinBot::API::User
- Included in:
- MixinBot::API
- Defined in:
- lib/mixin_bot/api/user.rb
Overview
User-related API endpoints: bot/network user lookup, plain network-user creation, and the full Safe Network registration flow.
The Safe-network registration mirrors the official Go SDK reference implementation in RegisterSafeWithSetupPin / RegisterSafeBareUser: github.com/MixinNetwork/bot-api-go-client/blob/master/safe_user.go
Constant Summary collapse
- SAFE_REGISTER_MAX_RETRIES =
Maximum number of times to retry
safe_registerwhen the freshly-set TIP PIN has not yet propagated through the Mixin server. 3- SAFE_REGISTER_RETRY_BASE_DELAY =
Base seconds to wait between
safe_registerretries. The wait grows linearly with the attempt number. 1- TIP_PIN_PROPAGATION_DELAY =
Seconds to wait after
update_pinbefore callingsafe_register, so the new TIP PIN has time to propagate on the server side. 1
Instance Method Summary collapse
-
#create_safe_user(name, private_key: nil, spend_key: nil) ⇒ Hash
Creates a Safe-network user end-to-end.
-
#create_user(full_name, key: nil) ⇒ Hash
Creates a Mixin network user.
- #fetch_users(user_ids) ⇒ Object
-
#migrate_to_safe(spend_key:, pin: nil) ⇒ TrueClass, Hash
Migrates an existing legacy user to the Safe network.
-
#safe_register(spend_key) ⇒ Hash
Registers an existing user on the Safe network.
- #search_user(query, access_token: nil) ⇒ Object
- #user(user_id, access_token: nil) ⇒ Object
Instance Method Details
#create_safe_user(name, private_key: nil, spend_key: nil) ⇒ Hash
Creates a Safe-network user end-to-end.
Mirrors RegisterSafeWithSetupPin in the Go SDK:
-
generate (or accept) a session keypair and a spend keypair
-
create the network user via #create_user
-
set the user’s PIN to the TIP public key derived from the spend key
-
wait briefly for propagation, then register on the Safe network (retrying transient failures)
The returned keystore is suitable for instantiating a new MixinBot::API that authenticates as the freshly-registered user.
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/mixin_bot/api/user.rb', line 94 def create_safe_user(name, private_key: nil, spend_key: nil) session_keypair = JOSE::JWA::Ed25519.keypair private_key spend_keypair = JOSE::JWA::Ed25519.keypair spend_key spend_key_hex = spend_keypair[1].unpack1('H*') user = create_user name, key: session_keypair[1][...32] data = user.fetch('data') keystore = { app_id: data['user_id'], session_id: data['session_id'], session_private_key: session_keypair[1].unpack1('H*'), server_public_key: data['pin_token_base64'], spend_key: spend_key_hex } user_api = MixinBot::API.new(**keystore) tip_pin = MixinBot.utils.tip_public_key spend_keypair[0], counter: data['tip_counter'] user_api.update_pin pin: tip_pin # Allow the freshly-set TIP PIN to propagate before registering. sleep TIP_PIN_PROPAGATION_DELAY with_safe_register_retries do user_api.safe_register spend_key_hex end keystore end |
#create_user(full_name, key: nil) ⇒ Hash
Creates a Mixin network user.
When key is omitted a fresh Ed25519 keypair is generated. The response is merged with the hex-encoded session private key under :private_key.
42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/mixin_bot/api/user.rb', line 42 def create_user(full_name, key: nil) keypair = JOSE::JWA::Ed25519.keypair key session_secret = Base64.urlsafe_encode64 keypair[0], padding: false private_key = keypair[1].unpack1('H*') path = '/users' payload = { full_name:, session_secret: } res = client.post path, **payload res.merge(private_key:).with_indifferent_access end |
#fetch_users(user_ids) ⇒ Object
63 64 65 66 67 68 69 |
# File 'lib/mixin_bot/api/user.rb', line 63 def fetch_users(user_ids) path = '/users/fetch' user_ids = [user_ids] if user_ids.is_a? String payload = user_ids client.post path, *payload end |
#migrate_to_safe(spend_key:, pin: nil) ⇒ TrueClass, Hash
Migrates an existing legacy user to the Safe network.
When the user has not yet upgraded to a TIP PIN, pin must be the user’s current 6-digit PIN so Pin#update_pin can rotate it to a TIP PIN derived from spend_key. When the user already has a TIP PIN, pin may be omitted.
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/mixin_bot/api/user.rb', line 187 def migrate_to_safe(spend_key:, pin: nil) profile = me['data'] return true if profile['has_safe'] spend_keypair = JOSE::JWA::Ed25519.keypair spend_key spend_key_hex = spend_keypair[1].unpack1('H*') if profile['tip_key_base64'].blank? new_pin = MixinBot.utils.tip_public_key spend_keypair[0], counter: profile['tip_counter'] update_pin pin: new_pin, old_pin: pin end # Allow the freshly-set TIP PIN to propagate before registering. sleep TIP_PIN_PROPAGATION_DELAY with_safe_register_retries do safe_register spend_key_hex end { spend_key: spend_key_hex }.with_indifferent_access end |
#safe_register(spend_key) ⇒ Hash
Registers an existing user on the Safe network.
spend_key may be supplied as raw bytes, a hex string, or a Base64-encoded string. It must encode the user’s full Ed25519 spend private key (or a 32-byte seed).
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/mixin_bot/api/user.rb', line 138 def safe_register(spend_key) path = '/safe/users' spend_key_bytes = MixinBot.utils.decode_key spend_key raise ArgumentError, 'invalid spend_key' if spend_key_bytes.nil? || spend_key_bytes.size < 32 keypair = JOSE::JWA::Ed25519.keypair spend_key_bytes[...32] public_key = keypair[0].unpack1('H*') # Normalize to a 64-byte signing key in hex so that callers may pass a # 32-byte seed without crashing the downstream signer. signing_key_hex = keypair[1].unpack1('H*') # NOTE: the Go SDK's +crypto.Sha256Hash+ is misleadingly named — it # actually computes SHA3-256, so +SHA3::Digest::SHA256+ is the correct # match. See bot-api-go-client safe_user.go +RegisterSafeBareUser+. app_id_hash = SHA3::Digest::SHA256.hexdigest config.app_id signature = Base64.urlsafe_encode64( JOSE::JWA::Ed25519.sign([app_id_hash].pack('H*'), keypair[1]), padding: false ) pin_base64 = encrypt_tip_pin signing_key_hex, 'SEQUENCER:REGISTER:', config.app_id, public_key payload = { public_key:, signature:, pin_base64: } client.post path, **payload end |
#search_user(query, access_token: nil) ⇒ Object
57 58 59 60 61 |
# File 'lib/mixin_bot/api/user.rb', line 57 def search_user(query, access_token: nil) path = format('/search/%<query>s', query:) client.get path, access_token: end |
#user(user_id, access_token: nil) ⇒ Object
26 27 28 29 |
# File 'lib/mixin_bot/api/user.rb', line 26 def user(user_id, access_token: nil) path = format('/users/%<user_id>s', user_id:) client.get path, access_token: end |