Been battling with this for ages. I'm using ADFS 4.0 on Server 2016.
My web site uses OpenID Connect and that uses the OWIN authorisation code grant.
When I use the authorisation code grant, I can get the code, exchange the code for an access token and then call a web API with that access token no problem.
But that flow requires a user to authenticate and for some of my use cases there is no user. An example would be a forgotten password flow where the user cannot authenticate. To do that, I use the client_credentials flow.
Getting this to work was a non-trivial task since the documentation is (shall we say) sub optimal.
I also needed the ADAL Nuget package. Interesting that you can mix OWIN and ADAL
So under "Application Groups", I have the "Server application accessing a web API" scenario.
The server application has a client ID and the secret key.
So assume the general API URL is:
https://my-pc/WebService
And the API's inside this are like:
https://my-pc/WebService/api/my-api/
The web API in the wizard has the RP identifier:
https://my-pc/WebService
(This matches the ValidAudience below).
Access control policy is:
Permit everyone
I have one custom claim rule:
c:[] => issue(claim = c);
Now there is no way to pass these claims back in the JWT but I added this during my troubleshooting.
Client permissions is set to:
"All clients"
with scope of:
openid and user_impersonation.
The web API controller is decorated with [Authorize].
The code is:
To call a second API, it's the same code with one change:
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://my-pc/WebService/api/my-api1/");
I also has to change to code for the web API App_Start, Startup.Auth.cs.
Enjoy!
My web site uses OpenID Connect and that uses the OWIN authorisation code grant.
When I use the authorisation code grant, I can get the code, exchange the code for an access token and then call a web API with that access token no problem.
But that flow requires a user to authenticate and for some of my use cases there is no user. An example would be a forgotten password flow where the user cannot authenticate. To do that, I use the client_credentials flow.
Getting this to work was a non-trivial task since the documentation is (shall we say) sub optimal.
I also needed the ADAL Nuget package. Interesting that you can mix OWIN and ADAL
So under "Application Groups", I have the "Server application accessing a web API" scenario.
The server application has a client ID and the secret key.
So assume the general API URL is:
https://my-pc/WebService
And the API's inside this are like:
https://my-pc/WebService/api/my-api/
The web API in the wizard has the RP identifier:
https://my-pc/WebService
(This matches the ValidAudience below).
Access control policy is:
Permit everyone
I have one custom claim rule:
c:[] => issue(claim = c);
Now there is no way to pass these claims back in the JWT but I added this during my troubleshooting.
Client permissions is set to:
"All clients"
with scope of:
openid and user_impersonation.
The web API controller is decorated with [Authorize].
The code is:
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
To call a second API, it's the same code with one change:
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://my-pc/WebService/api/my-api1/");
I also has to change to code for the web API App_Start, 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"
});
Enjoy!
4 comments:
In which method & file goes the first code example please ?
In which method and file goes the first code example please ?
In the controller.
Lots of examples here - https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-code-samples
Can't thank you enough for all the blog posts relates to ADFS. I have been trying to find decent blogs to implement OAuth on ADFS. Microsoft Official Documentations have to be improved massively. I now got everything working thanks to your blog posts.
Post a Comment