Ok, i will answer my question.
As stated by @Jas you cannot get rid of client_id because of security validations. So if someone is trying to implement such scenario, i will explain my approach:
Create a Technical Profile like this with client_id="myaudience" and use METADATA item to point to your custom OIDC microservice metadata. A B2C app clientId (B2C URL clientId) must be passed as input claim to handle diferent Apple client_id's depending on registered App:
<TechnicalProfile Id="AppleID">
<Protocol Name="OpenIdConnect" />
<Metadata>
<Item Key="METADATA">https://xxxxxxxx.ngrok.io/metadata?provider=apple</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="response_types">code</Item>
<Item Key="UsePolicyInRedirectUri">false</Item>
<Item Key="client_id">myaudience</Item>
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_MultiIDP" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="groupId" />
<InputClaim ClaimTypeReferenceId="appId" PartnerClaimType="clientId" DefaultValue="{OIDC:ClientId}" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="sub" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="email" />
<OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="preferred_username" />
<OutputClaim ClaimTypeReferenceId="displayName" DefaultValue="Apple user" />
<OutputClaim ClaimTypeReferenceId="picture" PartnerClaimType="picture" />
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="apple.com" AlwaysUseDefaultValue="true" />
</OutputClaims>
</TechnicalProfile>
Your custom multi-IDP microservice will capture in the /authorize endpoint via queryString the clientId and should retrieve from a storage the Apple client_id, teamId, redirect_uri (same than Apple console), KeyId and Issuer corresponding to it.
your /authorize should redirect to the Apple authentication page and will prompt for your credentials.
After enter credentials and click on "continue" button your /token endpoint should be fired, at this point you need to retrieve the .p8 secret (Private Key) from a Key Vault to generate the token to be used with the code in the /token body.
The client_id, client_secret and keyId should be overrided with your custom stored data.

- After craft the token and build the data to send to Apple /token endpoint, retrieve the Apple final token with its access_token, id_token, refresh_token ..

- At this point you need to return all this data to B2C but the audience will fail, so you need to recraft a new id_token before passing the control to B2C through the configured redirect_uri, also you can move the Claims coming from Apple to the new forged token. The secret key used to sign the new token must be configured before as a Policy Key (signature) in your B2C Identity Experience Framewrok.
Refer to this doc for more info: https://learn.microsoft.com/en-us/azure/active-directory-b2c/openid-connect-technical-profile
This secret is referenced as cryptographickey in the Technical Profile above as: B2C_1A_MultiIDP

- Return the data from your microservice /token endpoint to B2C:

Thats all! Happy coding!
Update Jun 2023:
Token Generator class:
public class TokenGenerator : ITokenGenerator
{
private IConfiguration _config;
public TokenGenerator(IConfiguration config)
{
_config = config;
}
public string GenerateGenericToken(string code, string issuer)
{
string newToken = string.Empty;
Dictionary<string, object> claims = new Dictionary<string, object>();
string sub = Guid.NewGuid().ToString();
claims.Add("code", code);
claims.Add("sub", sub);
claims.Add("email", "dirty@gmail.com");
claims.Add("email_verified", "true");
claims.Add("auth_time", ((int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds).ToString());
newToken = GenerateToken(issuer, claims);
return newToken;
}
public string GenerateToken(string issuer, Dictionary<string, object> claims)
{
var B2CEncodedPrivateKey = _config[ConfigParameter.B2CIDPPrivateSecret.ToString()];
var mySecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(B2CEncodedPrivateKey));
var myAudience = "MyTPB2CCLIENTID";
var tokenHandler = new JwtSecurityTokenHandler();
Dictionary<string, object> header = new Dictionary<string, object>();
header.Add("kid", "XXXXXXXXXXXXXXXXXXXXXXXX");
header.Add("kty", "oct");
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.NameIdentifier, claims["sub"].ToString())
}),
Expires = DateTime.UtcNow.AddDays(7),
Claims = claims,
Issuer = issuer,
Audience = myAudience,
SigningCredentials = new SigningCredentials(mySecurityKey, SecurityAlgorithms.HmacSha256),
AdditionalHeaderClaims = header
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
}
Apple Service:
public async Task<string> AuthorizeAsync(AuthorizeModelDTO authorizeModel)
{
_logger.LogEvent("GENERACION URI PROVIDER <APPLE>");
string Uri = "";
AppleClientData clientData = await _context.GetClientDataByProviderAsync(authorizeModel.clientId, authorizeModel.provider);
Uri = GetAppleAuthorizeUri(authorizeModel.nonce, authorizeModel.state, clientData.TeamId, clientData.ProviderClientId, clientData.redirect_uri, authorizeModel.scope);
//TODO: Check provider..
_logger.LogEvent("FIN GENERACION URI PROVIDER <APPLE>");
return Uri;
}
public Task<Dictionary<string, string>> CorrelateAsync(string provider, string code, string clientSecret)
{
_logger.LogEvent("GENERACION TOKEN GENERICO");
Dictionary<string, string> result = new Dictionary<string, string>();
if (_config[ConfigParameter.B2CIDPPrivateSecret.ToString()] == clientSecret)
{
string issuer = "";
if (provider.ToLower() == "Apple".ToLower())
issuer = "https://appleid.apple.com";
result.Add("access_token", code);
result.Add("token_type", "code");
result.Add("expires_in", DateTime.Now.AddMinutes(30).ToLongTimeString());
result.Add("refresh_token", "fakerefrshtoken");
result.Add("id_token", _tokenGenerator.GenerateGenericToken(code, issuer));
}
else
{
result.Add("error", "invalid client_secret");
}
_logger.LogEvent("FIN GENERACION TOKEN GENERICO");
return Task.FromResult(result);
}
public async Task<TokenData> TokenAsync(string clientId, string code, string CorrelationId, string provider, string hostName)
{
_logger.LogEvent($"PETICION TOKEN A TRAVES DE CODE <{code}>");
Dictionary<string, string> result = new Dictionary<string, string>();
var finalContent = "";
try
{
var prov = provider.ToLower();
_logger.LogInformation("Buscamos informacion del cliente segun el proveedor");
//GET cosmos
AppleClientData clientData = await _context.GetClientDataByProviderAsync(clientId, provider);
if (clientData != null)
{
_logger.LogInformation("ENCONTRAMOS informacion del cliente segun el proveedor");
var appClientId = clientData.ProviderClientId.Replace(".", "");
_logger.LogInformation("BUSCAMOS KV Secret");
var kvSecret = _config[prov + "-" + appClientId].ToString();
if(kvSecret != null && kvSecret != "")
{
_logger.LogInformation("ENCONTRAMOS KV Secret");
var secret = new CreateClientSecret
{
issuer = clientData.TeamId,
keyid = clientData.KeyId,
subject = clientData.ProviderClientId,
thumb = kvSecret
};
_logger.LogInformation("GENERAMOS Apple Token");
var secretToken = _appleTokenHandler.GenerateAppleToken(_config, "", secret);
if (!string.IsNullOrEmpty(secretToken))
{
_logger.LogInformation("GENERADO Apple Token");
var data = _serviceAgent.GetAppleDataBody(clientData.ProviderClientId, secretToken, code, clientData.redirect_uri, CorrelationId);
_logger.LogInformation("LLAMADA a Apple /token");
finalContent = await _serviceAgent.GetAuthTokenApple(_config, data, "");
var r = JsonSerializer.Deserialize<AppleToken>(finalContent);
if (r != null)
{
//Validamos token
if (await _tokenValidator.CheckToken(r.id_token))
{
TokenData dataToReturn = _appleTokenHandler.DecodeAppleToken(r.id_token);
_logger.LogInformation($"////TOKEN DATA RESULT :");
_logger.LogInformation(dataToReturn.ToString());
_logger.LogEvent($"EXITO FIN PETICION TOKEN A TRAVES DE CODE");
return dataToReturn;
}
}
}
}
}
}
catch (Exception)
{
_logger.LogEvent($"ERROR FIN PETICION TOKEN A TRAVES DE CODE");
throw;
}
_logger.LogEvent($"SIN EXITO FIN PETICION TOKEN A TRAVES DE CODE");
return null;
}
private string GetAppleAuthorizeUri(string nonce, string state, string teamId, string AppleclientId, string redirectUri, string scope)
{
return $"https://appleid.apple.com/auth/authorize?client_id={AppleclientId}&redirect_uri={redirectUri}&response_type=code&scope={scope}&response_mode=form_post&nonce={nonce}&token_issuer={teamId}&state=StateProperties%3D{state}";
}