After much triaL and error and gnashing of teeth, I eventually came up with the answer which I will share here.
The B2C custom policy calls an Azure Function App (passing in all fields from the Sign/SignUp form) that will do the following:
On Sign Up or Sign In:
Search in dataverse table External Identity for the b2c obbject ID in the adx_username field.
If found, we have a match, job done
-----------------------------------------------------------------------------------------
On Sign Up Only
Search in dataverse table Contact for the person using the fields on the SignUp form
If 1 match found - create a row in the dataverse table External Identity setting;
adx_username = b2c object ID
adx_identityprovidername = Issuer URL from the b2c SignIn/SignUp custom policy
adx_contactid = Id from the matched contact record
NOTE: the value to set adx_identityprovidername is NOT the Authority URL that you set in power pages for the b2c settings.
Next update the Contact record like so:
adx_identity_username = b2c object ID
adx_identity_logonenabled = YES (true)
If the contact does not have a value for adx_identity_securitystamp, set it to a new GUID
If no matches found, create a row in the Contact dataverse table setting all fields you have including the ones mentioned above.
and then create a row in the External Identity dataverse table as described above.
When the Function App returns, it will return the following claims to power pages:
oid - b2c object ID
iss - b2c issuer URL
check that your policy returns these.
Now in the Power Pages configuration page identity providers, open the one for Azure AD B2c
set both claims mapping fields to:
adx_username=oid,adx_identityprovidername=iss,adx_identity_username=oid
Then set Contact mapping with Email = OFF
Registration enabled = OFF
and this is our working solution!