Usage
Once the SDK is installed and the AirService
is successfully initialized, it can be used to authenticate users. Further, the native provider given by the service instance can be used to let users interact with the blockchain.
The AirService
instance offers following:
init(…)
The init method should generally be called right after creating the AirService
instance.
login(…)
This will login the user with their Global ID.
getUserInfo()
Retrieves general user information.
goToPartner(...)
Share the user's session from your dApp to another AIR Kit-enabled dApp.
getProvider()
getAccessToken()
Returns the current access token or a new one in case of expiry.
preloadWallet()
Loads the wallet in the background. Needs the user to be logged in.
setupOrUpdateMfa()
Triggers the MFA enrollment screen where the user can set up or update the Passkey.
isSmartAccountDeployed()
Check if the AA has been deployed. During this call the wallet will be loaded if not yet happened.
deploySmartAccount()
Deploy the AA if not yet deployed. It will also automatically be deployed when minting an Air Id. During this call the wallet will be loaded if not yet happened.
logout()
Logs out the user and clears up the session
clearInit()
Clears the initialization of the AirService
on(…) / off(…)
To keep in sync with the service's state, you can subscribe to it and receive events such as initialized
, logged_in
, wallet_initialized
*, air_id_minting_started
*, air_id_minting_failed
* and logged_out
.
Import and Initialize
The AirService creates an iframe that loads the login flow and sets up communication streams between the iframe and the DApp's JavaScript context.
import { AirService, BUILD_ENV } from "@mocanetwork/airkit";
const service = new AirService({
partnerId: YOUR_PARTNER_ID, // Replace with your actual Partner ID
});
await service.init({
buildEnv: BUILD_ENV.SANDBOX,
enableLogging: true,
});
// Without any parameters, this will trigger the default Air login dialog which provides different login methods for the user to choose from.
await embed.login();
Once the SDK is installed and AirService
is successfully initialized, it can be used to authenticate users.
Login
login(options?: { authToken?: string; })
: Promise<AirLoginResult>
Without any parameters, this will trigger the default Air login dialog, which provides different login methods for the user to choose from.
We recommend to always require passing in a signed JWT via the authToken
parameter with at minimum your partnerId
inside the payload. This would also trigger the default login but enhances security. If you're having your users already authenticated on your side, you can add the email
and partnerUserId
to the payload as well.
If an email is provided, we will verify via a one-time password sent to this email since it is used as an identifier on our side.
An example JWT could look like the following:
{
"partnerId": "YOUR PARTNER ID",
"partnerUserId": "YOUR USER ID", // optional
"email": "YOUR USER EMAIL", // optional
"exp": 1728973684,
"iat": 1728970084
}
To validate the JWTs on our end, we need to know your JWKS endpoint with the following JSON:
{
"keys": [
{
"kty": "EC",
"use": "sig",
"alg": "ES256",
"kid": "{your_kid}",
"crv": "P-256",
"x": "{your_x}",
"y": "{your_y}"
}
]
}
The above JWKS should contain the public key only, but the JWT generated on your end needs to be signed with the private key.
After successful login, an AirLoginResult
object will be returned, which also contains a property token
generated by us containing the following information:
{
sub: string,
abstractAccountAddress: string,
partnerId: string,
partnerUserId?: string,
sourcePartnerId?: string
}
This token can be used in the future to query some of our partner endpoints.
The native provider given by the embed instance can now be used to let users interact with the blockchain.
Signing Example
Using ethers
const ethProvider = new BrowserProvider(service.provider, 'any');
const signer = await ethProvider.getSigner();
const signedMessage = await signer.signMessage('Your message');
Using Web3
const web3 = new Web3(service.provider);
const signedMessage = await web3.eth.personal.sign(
'Your message',
eoaAccount,
'password',
);
Sending Transaction Example
Using ethers
const transactionParams: TransactionRequest = {...};
const ethProvider = new BrowserProvider(service.provider, 'any');
const signer = await ethProvider.getSigner();
const response = signer.sendTransaction(transactionParams);
Using web3
const transactionParams: Transaction = {...};
const web3 = new Web3(service.provider);
const response = await web3.eth.sendTransaction(transactionParams);
User Session Across AIR Kit dApps
To maintain a user's logged-in state across dApps in our ecosystem, we offer a convenient way to share the user's session from your dApp to another AIR Kit-enabled dApp.
From your dApp, call goToPartner(partnerUrl: string)
and pass in the target URL of the other dApp to receive an updated URL, which can be used to carry over the user. Once the user lands on the target dApp, the user session will automatically be rehydrated during SDK initialization.
Example:
const { urlWithToken } = await airService.goToPartner('https://www.partner-dapp.xyz/stake');
document.window.href = urlWithToken;
This method should be called at the time of navigating to the target url and not when displaying the link because the token attached to the url is short-lived. The target url also cannot be localhost
.
Additionally, the target dApp needs to make sure to allow automatic rehydration by not setting skipRehydration
to true
when calling init(...)
.
Handling MFA Requirements
In order to make use of any wallet functionality, the user needs to have set up MFA, which currently requires Passkey. After the user has set up the Passkey, any wallet action that requires signing or confirming a transaction will require users to verify their Passkey. This ensures funds are protected and only accessible by the user.
By default, the MFA enrollment screen will automatically pop up whenever the user has not set up MFA yet and a wallet action is triggered. This also includes getting the accounts and balance checks.
The MFA screen can also be programmatically triggered by calling setupOrUpdateMfa()
Before doing any wallet-related actions, if more control over the time of the MFA enrollment is needed.
The example below illustrates a potential way of handling the MFA setup:
const loggedInUser = await airService.login();
// The wallet initialization can take a few seconds, so we can preload the wallet
// in the background before we need it
airService.preloadWallet();
// Some time later...
// If the user has not set up MFA yet and we want to trigger it now, we can do
if(!loggedInUser.isMFASetup) {
await airService.setupOrUpdateMfa();
}
// At this point, we're ready to use the wallet
As long as the user has not set up MFA, the wallet address will not be returned via login, user info or token.
Interact with blockchain
The provider returned from the service is a compatible Eip1193Provider
and can be used with web3, ethers, Viem, etc.
To interact with blockchain data, libraries such as Ethers.js, Viem, and Web3.js can be used. Here are some small snippets for signing, querying, and mutating simple functions on the blockchain.
Sample smart contract
Consider a sample ERC20 smart contract with a minimal setup, including balanceOf
, symbol
, and mint
functions
Make sure to replace {CONTRACT_ADDRESS}
and {CHAIN_ID}
with your corresponding values.
const MOCK_TOKEN_CONTRACT = {
address: "{CONTRACT_ADDRESS}",
abi: [
"function balanceOf(address account) view returns (uint256)",
"function symbol() view returns (string)",
"function mint(address to, uint256 amount)",
],
chainId: {CHAIN_ID},
};
Wallet client
const airProvider = await service.getProvider();
const walletClient = createWalletClient({
transport: custom(airProvider),
chain: baseSepolia,
});
Sign a Message
const airProvider = await service.getProvider();
const ethProvider = new BrowserProvider(airProvider, "any");
const signer = await ethProvider.getSigner();
const signedMessage = await signer.signMessage(message);
Query the Blockchain
const airProvider = await service.getProvider();
const ethProvider = new BrowserProvider(airProvider, "any");
const signer = await ethProvider.getSigner();
const contract = new ethers.Contract(
MOCK_TOKEN_CONTRACT.address,
MOCK_TOKEN_CONTRACT.abi,
signer
);
const result = await contract.balanceOf(await signer.getAddress());
Mutation (Send a Transaction)
const airProvider = await service.getProvider();
const ethProvider = new BrowserProvider(airProvider, "any");
const accounts = await ethProvider.listAccounts();
const signer = accounts[0]; // The first account will be the AA account address, and the second will be the EOA/MPC signer
const contractAddress = MOCK_TOKEN_CONTRACT.address;
const abi = MOCK_TOKEN_CONTRACT.abi;
const contract = new ethers.Contract(contractAddress, abi, signer);
const tx = await contract.mint(await signer.getAddress(), ethers.parseUnits(amount, 18)); // Assuming 18 decimals
await tx.wait(); // Wait for the transaction to be mined\