Modern Security Keys can be used for logging into services without username and password. The user experience then just involves clicking a login button and pressing the Security Key. It frees the user from having to remember username/password combinations. Furthermore, it makes server-side storage and validation of passwords obsolete.
A PIN code can provide an additional protection in case the Security Key gets lost. In contrast to a password, it can be as short as some digits since Security Keys lock after eight failed attempts. Alternatively, some Security Keys provide biometric capabilities instead of PINs.
Our SDK brings passwordless FIDO2 authentication to Android that works with security keys over NFC and USB.
Passwordless login is supported by modern FIDO2 Security Keys, such as the YubiKeys 5 series as well as Solokeys. Security Keys can be directly connected over USB-C and NFC. USB-A keys require an additional USB OTG cable.
We are not selling yet another SaaS login solution. Our SDK is a standard-compliant clean-room implementation that works with any FIDO server. In contrast to Google's FIDO APIs, our SDK works without Google Play Services. Thus, it also works in countries where phones do not ship with Google Play.
The FIDO Alliance does not certify native FIDO clients. However, our SDK has been successfully tested with a wide variety of FIDO authenticators. It has been implemented by carefully following the FIDO standards.
Hardware Security SDK | Client to Authenticator Protocol 2 (CTAP2) |
FIDO2 Artifact | WebAuthn Specification |
class MainActivity : AppCompatActivity() {
private fun showAuthenticateDialog() {
// Make an authentication request to the server. In a real application, this would perform
// an HTTP request. The server will send us a challenge based on the FIDO2 key we registered
// before (see above), asking us to prove we still have the same key.
val authenticateRequest =
PublicKeyCredentialRequestOptions.create(challenge, timeout, rpId, allowCredentials, userVerification)
// This opens a UI fragment, which takes care of the user interaction as well as all FIDO2 internal
// operations for us, and triggers a callback to #OnGetAssertionCallback(PublicKeyCredential).
WebauthnDialogFragment.newInstance(PublicKeyCredentialGet.create(origin, authenticateRequest))
dialogFragment.setOnGetAssertionCallback(onGetAssertionCallback)
dialogFragment.show(requireFragmentManager())
}
private val onGetAssertionCallback = OnGetAssertionCallback { publicKeyCredential ->
// Finish the FIDO2 authentication with your server here
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
webView = view.findViewById(R.id.webview)
webView.settings.javaScriptEnabled = true
val webViewWebauthnBridge = WebViewWebauthnBridge
.createInstanceForWebView(this, webView)
webView.webViewClient = object : WebViewClient() {
override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?):
WebResourceResponse? {
webViewWebauthnBridge.delegateShouldInterceptRequest(view, request)
webViewFidoBridge.delegateShouldInterceptRequest(view, request)
return super.shouldInterceptRequest(view, request)
}
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
webViewWebauthnBridge.delegateOnPageStarted(view, url, favicon)
webViewFidoBridge.delegateOnPageStarted(view, url, favicon)
}
}
webView.loadUrl("https://webauthn.hwsecurity.dev")
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView webView = findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
WebViewWebauthnBridge webViewWebauthnBridge = WebViewWebauthnBridge
.createInstanceForWebView(this, webView);
webView.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
webViewWebauthnBridge.delegateShouldInterceptRequest(view, request);
return super.shouldInterceptRequest(view, request);
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
webViewWebauthnBridge.delegateOnPageStarted(view, url, favicon);
}
});
webView.loadUrl("https://webauthn.hwsecurity.dev");
}