'Biometric login using IdentityServer on iOS/Android app

We have a website(client1) which uses IdentityServer4 for authentication. Now we want our xamarain mobile app(client2) to authenticate with IdentityServer4 using bio metric (touch id , faceid and fingerprint) without using resource owner credentials grant(as it's not recommended) Anyone done that?



Solution 1:[1]

I want to stress that IdentityServer wants you to authenticate on the server! That is why you don't see many solutions to authenticate credentials that are input outside of the server. I have not even tried resource owner password flow since I've read it is discouraged. Then how do we use biometrics?

Here's one recipe that works.

We have Progressive app (Web, iOS, Android) and the technology stack is Ionic Platform v6 using angular-auth-oidc-client, cordova plugins: cordova-plugin-fingerprint-aio, cordova-plugin-inappbrowser, ionic-plugin-deeplinks and IdentityServer4.

InAppBrowser is critical because we use this to open the login page and set event handlers. The login page is served by IdentityServer. Login form contains username, password fields and login button and biometrics button. User clicks the biometrics button that is on the login form - sending request to controller. The controller task should redirect back to the client to let user activate biometrics plugin logic. The client knows biometrics button was clicked because of url I send back and read from InAppBrowser event handler.

Section from Login Method of Controller to redirect back to the client

  if (loginViewModel.ExternalLoginScheme=="Biometrics") {  //check for biometric button click  - we will go back to client to get biometric auth and inAppBrowser event handler execute loginFormSubmit() on Login.cshtml
            var redirectUrl = System.Web.HttpUtility.ParseQueryString(loginViewModel.ReturnUrl).Get("redirect_uri");
            var appendToUrl = "?biometricsLogin=true";
            return Redirect(redirectUrl + appendToUrl);
        }

This is just one piece of the puzzle solved. Nothing big here. Just returning '?biometricsLogin=true' on the path back to the client. Now we need to read that from the InAppBrowser event on the client. Here is a section of Login.ts where I open the InAppBrowser and the event handlers.

urlHandler(authUrl) {
console.log('urlHandler: authUrl='+ authUrl);
this.browser = this.iab.create(authUrl, '_blank', 'toolbar=no,location=no,menubar=no,clearcache=yes,clearsessioncache=yes');
this.browser.on('loadstart').subscribe((event: InAppBrowserEvent) => {
  console.log('login.ts->InAppBrowserEvent='+JSON.stringify(event));
  if (event.url.includes('ionic://insuredportalmobile?')||
    event.url.includes('ionic:/insuredportalmobile?')) {
      if (event.url.includes('biometricsLogin')) {
        this.biometricsLogin();
      } else {
this.ngZone.run(() => {
              this.oidcSecurityService.checkAuth(event.url).subscribe();
            });
          }
        }
    });
    this.browser.on('loaderror').subscribe(event => {  //this handles android
      if (event.message === 'net::ERR_UNKNOWN_URL_SCHEME') {
         //close the browser in ionViewWillLeave
         if (this.browser) {
          this.browser.close();
        }
        this.browser = null;
      }
    });

  }

We can now use the plugin to do bioMetricsLogin on the client!

Section of code to do that

loadBiometricsLogin() {
this.fingerprintAIO.loadBiometricSecret({
  description: 'loginMyCompany',
  disableBackup: true, // always disabled on Android
}).then( (res2) => {
  //successful either face or fingerprint was verified get credentials and place them in variables for username and password 
  const u = res2.substring(0,20);
  const p = res2.substring(20);
  const script = 'submitLoginForm(\''+u+'\',\''+p+'\');';
  this.browser.executeScript({code: script});   //This is magical!
}).catch( (err) => {
  console.log('Fail loadBiometricSecret');
  console.log(JSON.stringify(err));
});

}

The this.browser.executeScript line of code listed above will execute the script to call the submitLoginForm method that is located in login.cshtml shown below.

function submitLoginForm(u,p) {
            var el = document.getElementById("Username");
            el.value = u;
            el = document.getElementById("Password");
            el.value = p;
            document.forms['LoginForm'].submit();
        }

I am not providing the complete code here. Just a recipe that works. Good luck!

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1