Which Version of ADAL are you using ?
Microsoft.Identity.Client v2.6.2
Which platform has the issue?
net462
What authentication flow has the issue?
Other? - please describe;
Repro
var cred = new ClientCredential("<clientCredential>");
var app = new ConfidentialClientApplication(
clientId: "<clientId>",
authority: "https://login.microsoftonline.com/<tenantId>",
redirectUri: "https://localhost",
clientCredential: cred,
userTokenCache: null,
appTokenCache: new TokenCache());
var result = app.AcquireTokenForClientAsync(new[]
{
"https://database.windows.net/.default",
}).GetAwaiter().GetResult();
var token = result.AccessToken;
var builder = new SqlConnectionStringBuilder()
{
DataSource = "<dbServer>",
InitialCatalog = "<dbName>",
// ... other options
};
var conn = new SqlConnection(builder.ToString())
{
AccessToken = token,
};
conn.Open(); // Throw Unhandled Exception: System.Data.SqlClient.SqlException: Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON'.
Expected behavior
Sql connection should be opened without any exception.
Actual behavior
Throw Unhandled Exception:
System.Data.SqlClient.SqlException: Login failed for user 'NT AUTHORITYANONYMOUS LOGON'.
Possible Solution
Tried to do this using Microsoft.IdentityModel.Clients.ActiveDirectory v4.4.2 with same settings (clientId, clientSecret, etc.) and it opened the connection successfully.
Additional context/ Logs / Screenshots
I checked the token acquired by each library and find some difference:
Token from Microsoft.IdentityModel.Clients.ActiveDirectory v4.4.2
"aud": "https://database.windows.net/"
while token from Microsoft.Identity.Client v2.6.2:
"aud": "https://database.windows.net"
The trailing slash is missing. Here's a related SO question: Token-based database authentication fails with “Login failed for user 'NT AUTHORITYANONYMOUS LOGON'.”
@yhvicey
You need to add another / to the scope value:
var result = app.AcquireTokenForClientAsync(new[]
{
"https://database.windows.net//.default",
}).GetAwaiter().GetResult();
Note the double // in database.windows.net//
MSAL does not look inside the access token claims, we just return it as a string. This works with ADAL because you're hitting a v1 endpoint, and eSTS uses the resource you define as the "aud" claim.
I heard from eSTS, and in your case, you're hitting the v2 endpoint with MSAL using a v1 access token, so eSTS is parsing the desired audience from the requested scope (MSAL does not use resources, but scopes), and eSTS takes everything before the last slash and uses it as the resource identifier ("aud" claim).
Your request.scope value is https://database.windows.net/.default, so eSTS will send back "aud":"https://database.windows.net"
Let us know if the suggestion above solves the issue for you. I will add this to our wiki.
cc: @jmprieur
Adding another slash solved the issue. Thanks for your help!
@yhvicey Great! thanks for the quick response.
Most helpful comment
@yhvicey
You need to add another
/to the scope value:Note the double
//indatabase.windows.net//MSAL does not look inside the access token claims, we just return it as a string. This works with ADAL because you're hitting a v1 endpoint, and eSTS uses the resource you define as the "aud" claim.
I heard from eSTS, and in your case, you're hitting the v2 endpoint with MSAL using a v1 access token, so eSTS is parsing the desired audience from the requested scope (MSAL does not use resources, but scopes), and eSTS takes everything before the last slash and uses it as the resource identifier ("aud" claim).
Your request.scope value is
https://database.windows.net/.default, so eSTS will send back"aud":"https://database.windows.net"Let us know if the suggestion above solves the issue for you. I will add this to our wiki.
cc: @jmprieur