rbrayb
rbrayb

Reputation: 46720

Using the client_credentials flow with ADFS 4.0 returns 401

I have the "Server application accessing a web API" scenario.

The web site uses OIDC and authenticates no problem.

However, I have a use case for accessing some of the web API without a user context and for that, I use client_credentials.

The server app has a client ID and the secret key.

So assume the web API URL is:

https://my-pc/WebService/api/my-api/

The web API has the RP identifier:

https://my-pc/WebService/api/my-api/

Access control policy is:

Permit everyone

I have one claim rule:

c:[] => issue(claim = c);

Client permissions is set to:

"All clients" with scope of openid and user_impersonation.

The code is:

AuthenticationContext ac = new AuthenticationContext("https://my-adfs/adfs/", false);
// ClientCredential contains client_id and secret key 
AuthenticationResult result = await ac.AcquireTokenAsync("https://my-pc/WebService/api/my-api/", clientCredential);

HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://my-pc/WebService/api/my-api/");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);

HttpContent content = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("foo", "blah"), new KeyValuePair<string, string>("foo1", "blah1") });
request.Content = content;
HttpResponseMessage response = await client.SendAsync(request);

ADFS returns the access token no problem but when I call the web api, I keep getting a 401 - Unauthenticated.

Any ideas?

Upvotes: 0

Views: 1261

Answers (2)

rbrayb
rbrayb

Reputation: 46720

Finally figured this out and wrote it up.

The two pieces of code:

using Microsoft.IdentityModel.Clients.ActiveDirectory;

ClientCredential clientCredential = new ClientCredential(clientId, secretKey);

AuthenticationContext ac = new AuthenticationContext("https://my-adfs/adfs/", false);
AuthenticationResult result = await ac.AcquireTokenAsync("https://my-pc/WebService", 

clientCredential);

HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post,  

"https://my-pc/WebService/api/my-api/");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);

HttpContent content = new FormUrlEncodedContent(new[] { new KeyValuePair<string,  

string>("foo", "blah"), new KeyValuePair<string, string>("foo1", "blah1") });
request.Content = content;
HttpResponseMessage response = await client.SendAsync(request);

if (response.IsSuccessStatusCode)
    // etc

And in Startup.Auth.cs

app.UseActiveDirectoryFederationServicesBearerAuthentication(
    new ActiveDirectoryFederationServicesBearerAuthenticationOptions
    {
        TokenValidationParameters = new TokenValidationParameters()
        {
            SaveSigninToken = true,
            ValidAudience = "https://my-pc/WebService"
        },

        MetadataEndpoint = "https://my-adfs/FederationMetadata/2007-06/FederationMetadata.xml"
});

Took me a while!

Upvotes: 1

krishna
krishna

Reputation: 1

Since you don't get the user principal in the access token, you have no user session attached to a principal. When the Web API looks for the principal in the cookie(or Authorization header if you're sending access token) it doesn't find the principal and you will still be an anonymous user.

Upvotes: 0

Related Questions