Search code examples
c#dynamics-crmmicrosoft-dynamicsazure-identity

Package Azure.Identity v1.11.0 and above caused an error with SqlClient and Dynamics 365


Azure.Identity package version 1.11.0 caused an error with SqlClient and Dynamics 365.

I connected to Dynamics 365 using Microsoft Entra Service Principal authentication, and everything worked perfectly. I used SQL Server Management Studio 20.0.70.0.

enter image description here

Also, I have a C# application using Dapper and SqlClient (check the code below).

Everything worked perfectly until I upgraded the Azure.Identity Package to 1.11.0 (updated five days ago). Downgrading it to 1.10.4 will make it work.

I cannot find any solution in Microsoft Docs.

Thank you.

Exception:

ClientSecretCredential authentication failed: AADSTS900023: Specified tenant identifier 'authorize' is neither a valid DNS name, nor a valid external domain. Trace ID: 52222220-3332-3331-3033-222225335233 Correlation ID: fddd3533-aaaa-4fab-8b49-22ea71b32723 Timestamp: 2024-04-14 20:20:52Z

Code:

enter image description here

enter image description here


Solution

  • I finally found it here, But I applied minor modifications to make it work with Dynamics 365. Here is the code

    
    private SqlAuthenticationToken? _sqlAuthenticationToken;
    
    protected Func<SqlAuthenticationParameters, CancellationToken, Task<SqlAuthenticationToken?>> GetTokenCallback()
    {
        var tenantId = "[Taken From Azure Portal]";
        var clientId = "[Taken From Azure Portal]";
        var clientSecret = "[Taken From Azure Portal]";
        const string defaultScopeSuffix = ".default";
    
        var cred = new ClientSecretCredential(tenantId, clientId, clientSecret);
    
        return TokenCallback;
    
        async Task<SqlAuthenticationToken?> TokenCallback(SqlAuthenticationParameters ctx, CancellationToken cancellationToken)
        {
            var validToken = _sqlAuthenticationToken != null && _sqlAuthenticationToken.ExpiresOn.UtcDateTime > dateTimeService.Now;
            if (validToken) return _sqlAuthenticationToken;
    
            string scope = ctx.Resource.EndsWith(defaultScopeSuffix)
                ? ctx.Resource
                : ctx.Resource + defaultScopeSuffix;
    
            var token = await cred.GetTokenAsync(new TokenRequestContext([scope]), CancellationToken.None);
            _sqlAuthenticationToken = new SqlAuthenticationToken(token.Token, token.ExpiresOn);
            return _sqlAuthenticationToken;
        }
    }
    

    Then I modified the creation of SqlConnection to be like this

    await using var connection = new SqlConnection(ConnectionString);
    connection.AccessTokenCallback = GetTokenCallback();
    

    One more thing: You need to modify the connection string from this

    "Server=your-app.crm.dynamics.com; Authentication=Active Directory Service Principal; Encrypt=True; Database=[db-name]; User Id=[clientId]; Password=[clientSecret]"
    

    to this

    "Server=your-app.crm.dynamics.com; Encrypt=True; Database=[db-name];
    

    So I removed User Id, Password, and Authentication.