In this step, you’ll learn how to complete the SIWB authentication process by signing the message you received in Step 1 and converting it into an Internet Computer identity.
What You’ll Accomplish
By the end of this step, you’ll have:
✅ Created utility functions for delegation chain management
✅ Implemented the complete SIWB login process
✅ Generated a working Internet Computer identity
✅ Tested the SIWB authentication flow Step 4
Phase 2: Complete SIWB Login
Now that you have a message to sign from Step 1, let’s complete the authentication process by signing that message with your Bitcoin wallet and converting it into an Internet Computer identity.
Authentication Utilities
First, let’s create utility functions that we’ll need for managing delegation chains:
import { PublicKey } from '../canister/service.interface' ;
import { Delegation , DelegationChain } from '@dfinity/identity' ;
const asSignature = ( signature : any ) : any => {
const arrayBuffer = signature . buffer ;
const s = arrayBuffer ;
s . __signature__ = void 0 ;
return s ;
};
const asDerEncodedPublicKey = ( publicKey : any ) => {
const arrayBuffer = publicKey . buffer ;
const pk = arrayBuffer ;
pk . __derEncodedPublicKey__ = void 0 ;
return pk ;
};
export const createDelegationChain = ( signedDelegation : any , publicKey : PublicKey ) => {
const delegations = [
{
delegation: new Delegation (
signedDelegation . delegation . pubkey . buffer ,
signedDelegation . delegation . expiration ,
signedDelegation . delegation . targets [ 0 ]
),
signature: asSignature ( signedDelegation . signature ),
},
];
return DelegationChain . fromDelegations ( delegations , asDerEncodedPublicKey ( publicKey ));
};
These utility functions handle the conversion between the SIWB canister’s response format and the Internet Computer’s delegation identity format.
Implementing the Login Function
Now let’s implement the main login function that completes the SIWB authentication process:
import { Actor , HttpAgent } from "@dfinity/agent" ;
import { DEFAULT_IC_HOST , SIWB_CANISTER_ID } from "../constants/canister" ;
import { idlFactory as SIWBIdlFactory } from '../canister/ic_siwb_provider.idl' ;
import { SIWBActor , SIWBGetDelegationResponse , SIWBLoginResponse , SIWBSignMessageType } from "../types/siwb" ;
import { SignatureType } from '../types/siwb' ;
import { Ed25519KeyIdentity , DelegationIdentity } from '@dfinity/identity' ;
import { createDelegationChain } from '../utils/auth' ;
/**
* Parameters for completing the login process
*/
export interface LoginParams {
/** Bitcoin address */
address : string ;
/** Original message that was signed */
message : string ;
/** Signed message from wallet */
signature : string ;
/** Bitcoin public key */
publicKey : string ;
/** Type of signature used */
signatureType : SignatureType ;
/** Optional referrer code */
referrer ?: string ;
}
/**
* Phase 2: Complete authentication with signed message
*/
export const login = async ( params : LoginParams ) : Promise < DelegationIdentity > => {
// Step 1: Create anonymous agent and SIWB actor
const agent = new HttpAgent ({ host: DEFAULT_IC_HOST });
const siwbActor = Actor . createActor ( SIWBIdlFactory , {
agent ,
canisterId: SIWB_CANISTER_ID ,
}) as SIWBActor ;
// Step 2: Generate session identity
const sessionIdentity = Ed25519KeyIdentity . generate ();
const sessionPublicKey = sessionIdentity . getPublicKey (). toDer ();
// Step 3: Call siwb_login
const signMessageType : SIWBSignMessageType = { Bip322Simple: null };
const loginResponse : SIWBLoginResponse = await siwbActor . siwb_login (
params . signature , // Keep as string (base64)
params . address , // Keep as string
params . publicKey , // Keep as string (hex)
new Uint8Array ( sessionPublicKey ),
signMessageType
);
if ( 'Err' in loginResponse ) {
throw new Error ( `SIWB login failed: ${ loginResponse . Err } ` );
}
const delegationResponse : SIWBGetDelegationResponse = await siwbActor . siwb_get_delegation (
params . address ,
new Uint8Array ( sessionPublicKey ),
loginResponse . Ok . expiration
);
if ( 'Err' in delegationResponse ) {
throw new Error ( `SIWB get delegation failed: ${ delegationResponse . Err } ` );
}
// Create delegation chain
const delegationChain = createDelegationChain (
delegationResponse . Ok ,
loginResponse . Ok . user_canister_pubkey
);
// Step 5: Create delegation identity
const identity = DelegationIdentity . fromDelegation ( sessionIdentity , delegationChain );
return identity
}
Understanding the Login Process
The login function performs several important steps:
Create SIWB Actor
Establishes a connection to the SIWB canister using an anonymous agent
Generate Session Identity
Creates a temporary identity for this authentication session
Submit Login Credentials
Sends the signed message, address, and public key to the SIWB canister
Retrieve Delegation
Gets a delegation that proves the authentication was successful
Create Identity
Combines the session identity with the delegation to create a usable Internet Computer identity
At this point, you can start using the identity to interact with Odin canisters and other Internet Computer services.
Testing the Complete Flow
Let’s test our complete authentication flow by combining the prepare and login functions:
import { login , prepare } from "./core/prepare" ;
import { createSampleWallet } from "./sample-wallet" ;
( async () => {
const wallet = await createSampleWallet ();
const result = await prepare ( wallet . address );
const signature = await wallet . signMessage ( result . message );
const loginResult = await login ({
address: wallet . address ,
message: result . message ,
signature: signature ,
publicKey: wallet . publicKey ,
signatureType: 'Bip322Simple' ,
});
console . log ( loginResult . getPrincipal (). toString ());
})();
sldvg - tvzjh - 6 mm76 - 6 szcs - tprn5 - a3lxs - kxg4z - mvvxz - alwum - jsequ - pqe
Success! You now have a working Internet Computer identity generated through Bitcoin wallet authentication.
Bitcoin Wallet Integration
If you don’t know how to create and sign messages with Bitcoin, don’t worry! We’ve created a comprehensive guide in Sample Wallet Setup that shows you how to implement a complete Bitcoin wallet for testing.
Troubleshooting Common Issues
Ensure you are signing with Bip322 strict
Verify that the message being signed matches exactly the one returned from prepare()
Make sure that you are matching the signature method (Bip322 vs ECDSA)
If authentication fails due to expiration, restart from Step 1 with a fresh prepare()
call
What’s Next?
Now that you have a working Internet Computer identity, you can use it to authenticate with Odin’s API services. In the next step, you’ll learn how to exchange your identity for an API token.
Continue to Step 3 Learn how to authenticate with Odin’s API using your Internet Computer identity