Deema
Deema

Reputation: 19

How to create Custom credential provider with biometric finger print authentication - Windows Biometric fingerprint (WBF)

I'm looking for assistance designing a Windows custom credential provider that includes fingerprint authentication. Specifically:

It needs to authenticate users via their fingerprints, using an external fingerprint reader/software.

It must interface with the Windows biometric fingerprint API to integrate with the operating system.

Unfortunately I don't have experience working with Windows or its API, so I was hoping to find an open source sample project I could adapt. However, I haven't been able to find anything suitable.

Does anyone have experience building Windows credential providers they could advise me on? I'm essentially looking for guidance on:

1-Using the Windows biometric fingerprint API

2-Integrating an external fingerprint reader/software

3-The overall architecture for a custom credential provider

Any pointers in the right direction would be greatly appreciated! If a full working sample/template exists that others have built, that would be ideal.

i asked chatgpt and it provided me with the following code, but i dont think its sufficient :

#include <credentialprovider.h>
#include <windows.h>

class MyCredentialProvider : public ICredentialProvider, public ICredentialProviderCredential
{
public:
    // Implement ICredentialProvider methods
    HRESULT STDMETHODCALLTYPE SetUsageScenario(
        __in CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus,
        __in DWORD dwFlags
        ) override;

    HRESULT STDMETHODCALLTYPE SetSerialization(
        __in const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs
        ) override;

    HRESULT STDMETHODCALLTYPE Advise(
        __in ICredentialProviderEvents* pcpe,
        __in UINT_PTR upAdviseContext
        ) override;

    HRESULT STDMETHODCALLTYPE UnAdvise() override;

    HRESULT STDMETHODCALLTYPE GetFieldDescriptorCount(
        __out DWORD* pdwCount
        ) override;

    HRESULT STDMETHODCALLTYPE GetFieldDescriptorAt(
        __in DWORD dwIndex,
        __deref_out CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR** ppcpfd
        ) override;

    HRESULT STDMETHODCALLTYPE GetCredentialCount(
        __out DWORD* pdwCount,
        __out DWORD* pdwDefault,
        __out BOOL* pbAutoLogonWithDefault
        ) override;

    HRESULT STDMETHODCALLTYPE GetCredentialAt(
        __in DWORD dwIndex,
        __out ICredentialProviderCredential** ppcpc
        ) override;

    // Implement ICredentialProviderCredential methods
    HRESULT STDMETHODCALLTYPE Advise(
        __in ICredentialProviderCredentialEvents* pcpce
        ) override;

    HRESULT STDMETHODCALLTYPE UnAdvise() override;

    HRESULT STDMETHODCALLTYPE SetSelected(
        __out BOOL* pbAutoLogon
        ) override;

    HRESULT STDMETHODCALLTYPE SetDeselected() override;

    HRESULT STDMETHODCALLTYPE GetFieldState(
        __in DWORD dwIndex,
        __out CREDENTIAL_PROVIDER_FIELD_STATE* pcpfs,
        __out CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE* pcpfis
        ) override;

    HRESULT STDMETHODCALLTYPE GetStringValue(
        __in DWORD dwIndex,
        __out LPWSTR* ppwsz
        ) override;

    HRESULT STDMETHODCALLTYPE GetBitmapValue(
        __in DWORD dwIndex,
        __out HBITMAP* phbmp
        ) override;

    HRESULT STDMETHODCALLTYPE GetCheckboxValue(
        __in DWORD dwIndex,
        __out BOOL* pbChecked,
        __deref_out LPWSTR* ppwszLabel
        ) override;

    HRESULT STDMETHODCALLTYPE GetSubmitButtonValue(
        __in DWORD dwIndex,
        __out DWORD* pdwAdjacentTo
        ) override;

    HRESULT STDMETHODCALLTYPE GetComboBoxValueCount(
        __in DWORD dwIndex,
        __out DWORD* pcItems,
        __out DWORD* pdwSelectedItem
        ) override;

    HRESULT STDMETHODCALLTYPE GetComboBoxValueAt(
        __in DWORD dwIndex,
        __in DWORD dwItem,
        __deref_out LPWSTR* ppwszItem
        ) override;

    HRESULT STDMETHODCALLTYPE SetStringValue(
        __in DWORD dwIndex,
        __in PCWSTR pwz
        ) override;

private:
    // Add private members and methods as needed
};

// Implement ICredentialProvider methods
HRESULT STDMETHODCALLTYPE MyCredentialProvider::SetUsageScenario(
    __in CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus,
    __in DWORD dwFlags
    )
{
    // TODO: Implement SetUsageScenario method
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyCredentialProvider::SetSerialization(
    __in const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs
    )
{
    // TODO: Implement SetSerialization method
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyCredentialProvider::Advise(
    __in ICredentialProviderEvents* pcpe,
    __in UINT_PTR upAdviseContext
    )
{
    // TODO: Implement Advise method
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyCredentialProvider::UnAdvise()
{
    // TODO: Implement UnAdvise method
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyCredentialProvider::GetFieldDescriptorCount(
    __out DWORD* pdwCount
    )
{
    // TODO: Implement GetFieldDescriptorCount method
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyCredentialProvider::GetFieldDescriptorAt(
    __in DWORD dwIndex,
    __deref_out CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR** ppcpfd
    )
{
    // TODO: Implement GetFieldDescriptorAt method
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyCredentialProvider::GetCredentialCount(
    __out DWORD* pdwCount,
    __out DWORD* pdwDefault,
    __out BOOL* pbAutoLogonWithDefault
    )
{
    // TODO: Implement GetCredentialCount method
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyCredentialProvider::GetCredentialAt(
    __in DWORD dwIndex,
    __out ICredentialProviderCredential** ppcpc
    )
{
    // TODO: Implement GetCredentialAt method
    return E_NOTIMPL;
}

// Implement ICredentialProviderCredential methods
HRESULT STDMETHODCALLTYPE MyCredentialProvider::Advise(
    __in ICredentialProviderCredentialEvents* pcpce
    )
{
    // TODO: Implement Advise method
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyCredentialProvider::UnAdvise()
{
    // TODO: Implement UnAdvise method
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyCredentialProvider::SetSelected(
    __out BOOL* pbAutoLogon
    )
{
    // TODO: Implement SetSelected method
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyCredentialProvider::SetDeselected()
{
    // TODO: Implement SetDeselected method
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyCredentialProvider::GetFieldState(
    __in DWORD dwIndex,
    __out CREDENTIAL_PROVIDER_FIELD_STATE* pcpfs,
    __out CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE* pcpfis
    )
{
    // TODO: Implement GetFieldState method
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyCredentialProvider::GetStringValue(
    __in DWORD dwIndex,
    __out LPWSTR* ppwsz
    )
{
    // TODO: Implement GetStringValue method
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyCredentialProvider::GetBitmapValue(
    __in DWORD dwIndex,
    __out HBITMAP* phbmp
    )
{
    // TODO: Implement GetBitmapValue method
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyCredentialProvider::GetCheckboxValue(
    __in DWORD dwIndex,
    __out BOOL* pbChecked,
    __deref_out LPWSTR* ppwszLabel
    )
{
    // TODO: Implement GetCheckboxValue method
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyCredentialProvider::GetSubmitButtonValue(
    __in DWORD dwIndex,
    __out DWORD* pdwAdjacentTo
    )
{
    // TODO: Implement GetSubmitButtonValue method
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyCredentialProvider::GetComboBoxValueCount(
    __in DWORD dwIndex,
    __out DWORD* pcItems,
    __out DWORD* pdwSelectedItem
    )
{
    // TODO: Implement GetComboBoxValueCount method
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyCredentialProvider::GetComboBoxValueAt(
    __in DWORD dwIndex,
    __in DWORD dwItem,
    __deref_out LPWSTR* ppwszItem
    )
{
    // TODO: Implement GetComboBoxValueAt method
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyCredentialProvider::SetStringValue(
    __in DWORD dwIndex,
    __in PCWSTR pwz
    )
{
    // TODO: Implement SetStringValue method
    return E_NOTIMPL;
}

Upvotes: 0

Views: 640

Answers (1)

Nehluxhes
Nehluxhes

Reputation: 186

You can find a basic working credential provider in the Microsoft classic samples

A credential provider is ideally only supposed to retrieve credentials, serialize them, and pass the serialization to an authentication package, doing no authentication logic on its own. That would be the authentication package responsibility.

I say ideally because a real authentication package is a lot more complicated to implement and you will only be able to find very partial example on the web. That's why I assume most people don't bother and still do their authentication logic in the credential provider. The drawback is the builtin authentication packages requires a password (or a smartcard), so you will only be able to add biometric authentication as a second factor, but you will still need the password. A workaround could be to encrypt the password with the biometric data and replay it in the next logins if you don't mind the security risk of storing passwords.

If you go the credential provider route, most methods are just to tell windows what type of UI you want, you won't draw it yourself. For the authentication logic itself, you would either do it in the IConnectableCredentialProviderCredential::Connect or the ICredentialProviderCredential::GetSerialization methods. Think of the credential provider as being a big loop calling Connect/GetSerialization each time the user click the submit button until you tell windows you have enough data.

So that could be something like :

GetSerialization(__out CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr, __out CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs,
    __deref_out_opt PWSTR* ppwszOptionalStatusText, __out CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon)
{
   if (no_biometric_data)
   {
      // Let the user input its credential, wait for the next loop 
      // iteration, update the UI if needed
      *pcpgsr = CPGSR_NO_CREDENTIAL_NOT_FINISHED;
   }
   // Call your fingerprint API here
   else if (ValidateBiometricData(data) == SUCCESS)
   {
      // The built-in Negotiate authentication package use a 
      // username/password pair
      SerializeCredential(pcpcs, password, username);
      *pcpgsr = CPGSR_RETURN_CREDENTIAL_FINISHED;
   }
   else
   {
     // Invalid credentials, display a message then wait for next loop
     SHStrDupW(L"Invalid credentials", ppwszOptionalStatusText);
     *pcpgsr = CPGSR_NO_CREDENTIAL_NOT_FINISHED;
     *pcpsiOptionalStatusIcon = CPSI_ERROR;
   }
   return S_OK;
}

Upvotes: 1

Related Questions