Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

PRF providers

The built-in PasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProvider covers the common case. Reach for this page when:

  • You need platform-specific provider options (iOS URLSession / presentation anchor, Android Activity wiring, web authenticatorAttachment).
  • You're integrating Python, Go, or C# (no built-in PasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProvider ships for those bindings).
  • You need a custom PrfProviderPrfProviderPrfProviderPrfProviderPrfProviderPrfProviderPrfProviderPrfProviderPrfProvider (CLI YubiKey, FIDO2, air-gapped backup file, hardware module).

Built-in PasskeyProvider options

The built-in PasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProvider takes a PasskeyProviderOptionsPasskeyProviderOptionsPasskeyProviderOptionsPasskeyProviderOptionsPasskeyProviderOptionsPasskeyProviderOptionsPasskeyProviderOptionsPasskeyProviderOptionsPasskeyProviderOptions:

FieldDefaultDescription
rp_idrp_idrpIdrpIdrpIdrpIdrpIdRpIdRpIdBreez shared RPRelying Party ID. Your app's domain, or PasskeyProvider.BREEZ_RP_ID (keys.breez.technology) if Breez-registered. Changing it makes existing passkeys derive a different seed (see migration considerations).
rp_namerp_namerpNamerpNamerpNamerpNamerpNameRpNameRpName"Breez"Display name for your app, shown in some authenticator UIs. Registration-only.
user_nameuser_nameuserNameuserNameuserNameuserNameuserNameUserNameUserNamerp_namerp_namerpNamerpNamerpNamerpNamerpNameRpNameRpNameAccount identifier shown beneath the display name in the OS picker, e.g. john@doe.com. Pass a stable per-user value so each registration is a distinct entry (Apple Passwords dedupes by (rpId, user.name)). Registration-only.
user_display_nameuser_display_nameuserDisplayNameuserDisplayNameuserDisplayNameuserDisplayNameuserDisplayNameUserDisplayNameUserDisplayNameuser_nameuser_nameuserNameuserNameuserNameuserNameuserNameUserNameUserNameHuman-friendly name shown most prominently, e.g. John Doe. Registration-only.

The same PasskeyProviderOptionsPasskeyProviderOptionsPasskeyProviderOptionsPasskeyProviderOptionsPasskeyProviderOptionsPasskeyProviderOptionsPasskeyProviderOptionsPasskeyProviderOptionsPasskeyProviderOptions is settable on passkey_configpasskey_configpasskeyConfigpasskeyConfigpasskeyConfigpasskeyConfigpasskeyConfigPasskeyConfigPasskeyConfig via provider_optionsprovider_optionsproviderOptionsproviderOptionsproviderOptionsproviderOptionsproviderOptionsProviderOptionsProviderOptions, which builds the provider for you (see Initialization). Construct PasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProvider directly only for platform-specific options (iOS URLSession, web authenticatorAttachment) or a custom backend.

C# / Go / Python limitation

The SDK does not ship a built-in PasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProviderPasskeyProvider for C#, Go, or Python (no native passkey API to wrap). On those bindings, implement your own PrfProviderPrfProviderPrfProviderPrfProviderPrfProviderPrfProviderPrfProviderPrfProviderPrfProvider and pass it to PasskeyClientPasskeyClientPasskeyClientPasskeyClientPasskeyClientPasskeyClientPasskeyClientPasskeyClientPasskeyClient.

Custom PrfProvider

To support a custom authenticator (hardware security key, FIDO2/CTAP2 transport, air-gapped backup file), implement the PrfProviderPrfProviderPrfProviderPrfProviderPrfProviderPrfProviderPrfProviderPrfProviderPrfProvider interface directly. The Breez CLI ships YubiKey, FIDO2, and file-based implementations as references.

Rust
/// Implement PrfProvider for a custom authenticator (hardware key, FIDO2,
/// file-backed). Only derive_seeds and is_supported are required.
struct CustomPrfProvider;

#[async_trait::async_trait]
impl PrfProvider for CustomPrfProvider {
    async fn derive_seeds(
        &self,
        _request: DeriveSeedsRequest,
    ) -> Result<DeriveSeedsOutput, PrfProviderError> {
        // Return one 32-byte PRF output per salt, in input order.
        todo!("Implement using WebAuthn or native passkey APIs")
    }

    async fn is_supported(&self) -> Result<bool, PrfProviderError> {
        todo!("Check platform passkey availability")
    }

    async fn create_passkey(
        &self,
        _exclude_credentials: Vec<Vec<u8>>,
    ) -> Result<PasskeyCredential, PrfProviderError> {
        // Register a credential and return its ID plus attestation.
        todo!("Implement registration via WebAuthn create() / native API")
    }

    async fn check_domain_association(&self) -> Result<DomainAssociation, PrfProviderError> {
        // Custom providers without a verification source return Skipped.
        Ok(DomainAssociation::Skipped {
            reason: "CustomPrfProvider does not verify domain association".to_string(),
        })
    }
}
Swift
// Implement PrfProvider for a custom authenticator (hardware key, FIDO2,
// file-backed). Only deriveSeeds and isSupported are required.
class CustomPrfProvider: PrfProvider {
    func deriveSeeds(request: DeriveSeedsRequest) async throws -> DeriveSeedsOutput {
        // Return one 32-byte PRF output per salt, in input order.
        fatalError("Implement using WebAuthn or native passkey APIs")
    }

    func isSupported() async throws -> Bool {
        fatalError("Check platform passkey availability")
    }

    func createPasskey(excludeCredentials: [Data]) async throws -> PasskeyCredential {
        // Register a credential and return its ID plus attestation.
        fatalError("Implement registration via WebAuthn create() / native API")
    }

    func checkDomainAssociation() async throws -> DomainAssociation {
        return .skipped(reason: "CustomPrfProvider does not verify domain association")
    }
}
Kotlin
// Implement PrfProvider for a custom authenticator. Only deriveSeeds and
// isSupported are required.
class CustomPrfProvider : PrfProvider {
    override suspend fun deriveSeeds(request: DeriveSeedsRequest): DeriveSeedsOutput {
        // Return one 32-byte PRF output per salt, in input order.
        TODO("Implement using WebAuthn or native passkey APIs")
    }

    override suspend fun isSupported(): Boolean {
        TODO("Check platform passkey availability")
    }

    override suspend fun createPasskey(excludeCredentials: List<ByteArray>): PasskeyCredential {
        // Register a credential and return its ID plus attestation.
        TODO("Implement registration via native passkey API")
    }

    override suspend fun checkDomainAssociation(): DomainAssociation {
        return DomainAssociation.Skipped("CustomPrfProvider does not verify domain association")
    }
}
C#
// Implement PrfProvider for a custom authenticator (hardware key, FIDO2,
// file-backed). Only DeriveSeeds and IsSupported are required.
class CustomPrfProvider : PrfProvider
{
    public async Task<DeriveSeedsOutput> DeriveSeeds(DeriveSeedsRequest request)
    {
        // Return one 32-byte PRF output per salt, in input order.
        throw new NotImplementedException("Implement using WebAuthn or native passkey APIs");
    }

    public async Task<bool> IsSupported()
    {
        throw new NotImplementedException("Check platform passkey availability");
    }

    public async Task<PasskeyCredential> CreatePasskey(byte[][] excludeCredentials)
    {
        // Register a credential and return its ID plus attestation.
        throw new NotImplementedException("Implement registration via native passkey API");
    }

    public async Task<DomainAssociation> CheckDomainAssociation()
    {
        return await Task.FromResult<DomainAssociation>(
            new DomainAssociation.Skipped("CustomPrfProvider does not verify domain association"));
    }

}
Javascript
// Implement PrfProvider for a custom authenticator (hardware key, FIDO2,
// file-backed). Only deriveSeeds and isSupported are required.
class CustomPrfProvider {
  deriveSeeds = async (
    salts: string[]
  ): Promise<{ seeds: Uint8Array[], credentialId: Uint8Array | null }> => {
    // Return one 32-byte PRF output per salt, in input order.
    throw new Error('Implement using WebAuthn or native passkey APIs')
  }

  createPasskey = async (
    _excludeCredentials: Uint8Array[]
  ): Promise<PasskeyCredential> => {
    // Register a credential and return its ID plus attestation.
    throw new Error('Implement registration via WebAuthn create() / native API')
  }

  isSupported = async (): Promise<boolean> => {
    throw new Error('Check platform passkey availability')
  }
}
React Native
// Implement PrfProvider for a custom authenticator (hardware key, FIDO2,
// file-backed). Only deriveSeeds and isSupported are required.
class CustomPrfProvider {
  deriveSeeds = async (
    _request: { salts: string[] }
  ): Promise<{ seeds: Uint8Array[], credentialId?: Uint8Array }> => {
    // Return one 32-byte PRF output per salt, in input order.
    throw new Error('Implement using WebAuthn or native passkey APIs')
  }

  createPasskey = async (
    _excludeCredentials: Uint8Array[]
  ): Promise<PasskeyCredential> => {
    // Register a credential and return its ID plus attestation.
    throw new Error('Implement registration via native passkey API')
  }

  isSupported = async (): Promise<boolean> => {
    throw new Error('Check platform passkey availability')
  }
}
Flutter
// Implement custom callbacks if the built-in PasskeyProvider doesn't
// fit your needs. Pass them to PasskeyClient.fromCallbacks instead
// of going through PasskeyClientBuilder.withPrfProvider.
Future<DeriveSeedsOutput> deriveSeeds(DeriveSeedsRequest request) async {
  // Return one 32-byte PRF output per salt, in input order.
  throw UnimplementedError('Implement using platform passkey APIs');
}

Future<PasskeyCredential> createPasskey(List<Uint8List> excludeCredentials) async {
  // Register a credential and return its ID plus attestation.
  throw UnimplementedError('Implement registration via native passkey API');
}

Future<bool> isSupported() async {
  throw UnimplementedError('Check platform passkey availability');
}
Python
# Implement the PrfProvider trait for custom logic if no built-in
# PasskeyProvider ships for your target. Three required methods:
# derive_seeds for derivation, is_supported for the capability probe;
# create_passkey for registration is optional.
class CustomPrfProvider(PrfProvider):
    async def derive_seeds(self, request: DeriveSeedsRequest) -> DeriveSeedsOutput:
        # Return one 32-byte PRF output per salt, in input order.
        raise NotImplementedError("Implement using WebAuthn or native passkey APIs")

    async def is_supported(self) -> bool:
        raise NotImplementedError("Check platform passkey availability")

    async def create_passkey(self, exclude_credentials: list[bytes]) -> PasskeyCredential:
        # Register a credential and return its ID plus attestation.
        raise NotImplementedError("Implement registration via native passkey API")

    async def check_domain_association(self) -> DomainAssociation:
        # Optional: verify the app's identity against the platform's
        # domain verification source. Custom providers without a
        # verification source return SKIPPED, which tells callers
        # "proceed with WebAuthn as normal". The UniFFI-generated
        # variant classes are reparented to DomainAssociation at
        # runtime but mypy can't see that, hence the cast.
        return cast(
            DomainAssociation,
            DomainAssociation.SKIPPED(
                reason="CustomPrfProvider does not verify domain association"
            ),
        )
Go
// Implement the PrfProvider interface for a custom authenticator (hardware
// key, FIDO2, file-backed). Only DeriveSeeds and IsSupported are required.
type CustomPrfProvider struct{}

func (p *CustomPrfProvider) DeriveSeeds(
	request breez_sdk_spark.DeriveSeedsRequest,
) (breez_sdk_spark.DeriveSeedsOutput, error) {
	// Return one 32-byte PRF output per salt, in input order.
	panic("Implement using WebAuthn or native passkey APIs")
}

func (p *CustomPrfProvider) IsSupported() (bool, error) {
	panic("Check platform passkey availability")
}

func (p *CustomPrfProvider) CreatePasskey(
	excludeCredentials [][]byte,
) (breez_sdk_spark.PasskeyCredential, error) {
	// Register a credential and return its ID plus attestation.
	panic("Implement registration via native passkey API")
}

func (p *CustomPrfProvider) CheckDomainAssociation() (breez_sdk_spark.DomainAssociation, error) {
	return breez_sdk_spark.DomainAssociationSkipped{
		Reason: "CustomPrfProvider does not verify domain association",
	}, nil
}