Getting Started
Real quick-start guide for react-native-yubikit
Install
npm install @doko/react-native-yubikit
# or
yarn add @doko/react-native-yubikitThen complete the platform setup in Installation - iOS needs a Podfile override before it will build.
The mental model
The library doesn't give you a single YubiKeyReader class. Instead you:
- Start discovery (
Core.startUsbDiscovery/Core.startNfcDiscovery). - Listen for
attached/detached/errorevents (Core.addYubiKeyListener). - Once you have a device's
handle, call functions from the module you need (Oath,Piv,OpenPgp,YubiOtp,Fido,Management,Support) directly with thathandle- the modules manage their own connection internally. Core.requestConnection/Core.sendApdu/Core.closeConnectionare a lower-level escape hatch for raw APDU access; you don't need them for normal OATH/PIV/FIDO2/etc. usage.
Discover a device
import { Core } from '@doko/react-native-yubikit';
import type { YubiKeyDevice, YubiKeyEvent } from '@doko/react-native-yubikit';
const subscription = Core.addYubiKeyListener((event: YubiKeyEvent) => {
switch (event.type) {
case 'attached':
console.log('YubiKey attached:', event.device);
break;
case 'detached':
console.log('YubiKey detached:', event.handle);
break;
case 'error':
console.error('Discovery error:', event.error);
break;
}
});
// USB discovery - requests OS-level permission on Android automatically
Core.startUsbDiscovery({ handlePermissions: true });
// NFC discovery (needs the Info.plist / capability setup from Installation, on iOS)
Core.startNfcDiscovery();
// later, e.g. on unmount:
Core.stopUsbDiscovery();
Core.stopNfcDiscovery();
subscription.remove();You can also poll for whatever is already connected:
const devices: YubiKeyDevice[] = Core.getDiscoveredDevices();Read basic device info
import { Support } from '@doko/react-native-yubikit';
async function describeDevice(device: YubiKeyDevice) {
const info = await Support.readInfo(device.handle);
const name = Support.getName(info); // synchronous, not a Promise
console.log(`${name} - serial ${info.serialNumber}`);
}Read OATH (TOTP/HOTP) codes
import { Oath } from '@doko/react-native-yubikit';
async function readCodes(device: YubiKeyDevice) {
const codes = await Oath.calculateCodes(device.handle);
console.log(codes);
}FIDO2 registration and authentication
import { Fido } from '@doko/react-native-yubikit';
async function register(device: YubiKeyDevice) {
const credential = await Fido.makeCredential(
device.handle,
{
rp: { id: 'example.com', name: 'Example App' },
user: { id: base64UserId, name: 'user@example.com', displayName: 'User' },
challenge: base64Challenge,
pubKeyCredParams: [{ type: 'public-key', alg: -7 }],
// see the PublicKeyCredentialCreationOptions type for the full WebAuthn-shaped
// option set - binary fields (challenge, user.id, credential ids) are base64
// strings on this bridge, not ArrayBuffers
},
'example.com', // effectiveDomain
pin, // optional - only needed if user verification requires a PIN
);
// send `credential` to your server for verification
}Fido.getAssertion follows the same shape for authentication. Credential management functions (getCredentialCount, getRpIdList, getCredentials, deleteCredential, updateUserInformation) only work on Android - the iOS SDK doesn't expose them.
Error handling
There's no custom error class - rejections are plain RN bridge errors with a .message, tagged with one of a fixed set of native error codes (CONNECTION_ERROR, APDU_ERROR, MANAGEMENT_ERROR, OATH_ERROR, PIV_ERROR, OPENPGP_ERROR, YUBIOTP_ERROR, FIDO_ERROR, SUPPORT_ERROR):
try {
const codes = await Oath.calculateCodes(device.handle);
} catch (error) {
console.error('OATH read failed:', String(error));
}