I have a project that hosts the IdentityServer4 and I am attempting to also host in the same project a Web API, which accepts the access-token.
My question is, is possible that a single project contains the IdentityServer and an Web API that consume the same IdentityServer?
EDIT: The API must be secured with the Authorize attribute
I have an identity server 4 project, in the same project there is an API for CIUD of the clients. (Lets call it developer console api).
I then have a side project with is an asp .net core project that contains the actual razor pages for the Developer console it access the API within the Identity server project.
The reason i did it this way is that only one project should be updateing the database. So to update the database owned by the identity server it was decided the the API for accessing it should also be within the same project.
Yes you can have a web api from within your Identity server 4 project.
Configure service
services.AddAuthentication(IdentityServerConstants.DefaultCookieAuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
// base-address of your identityserver
options.Authority = settingsSetup.Settings.Authority;
// name of the API resource
options.ApiName = "testapi";
options.RequireHttpsMetadata = false;
});
Configure
I think it needs to have both of these.
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
app.UseAuthentication();
app.UseIdentityServer();
Endpoints
Because the requests are sent using the access token as a bearer token then the authorize for each of the API calls needs to include the authencationScheme. I havent exactly figured out why but without this it doesnt work.
[HttpGet("Client/List")]
[Authorize(AuthenticationSchemes = "Bearer")]
public ActionResult ClientList()
{
}
While #DaImTo's answer is correct and working and it's developed by IdentityServer team, it uses Introspection Endpoint which means for every request AddIdentityServerAuthentication will create a http request and send it to your server, which is the same app.
I developed a library called IdentityServer4.Contrib.LocalAccessTokenValidation which do the exact same thing but without using Introspection Endpoint. It will authenticate the token directly from TokenStore which is configured in Services. You can use it if you are interested.
nuget link : https://www.nuget.org/packages/IdentityServer4.Contrib.LocalAccessTokenValidation
github link : https://github.com/Kahbazi/IdentityServer4.Contrib.LocalAccessTokenValidation
Related
I have a .NET blazor wasm app that is using an api endpoint that is built into the solution , and trying to make use of the ms graph api too.
I have two named clients one for the app api and another for the graph api.
My Program.cs file looks like this (Blazor WASM .NET 6 hosted model).
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("App.ServerAPI"));
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("App.Graph"));
builder.Services.AddHttpClient("App.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)).AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
builder.Services.AddHttpClient("App.Graph", client => client.BaseAddress = new Uri("https://graph.microsoft.com/"));
I then go onto configure the Auth Layer like this . The graph api does not need access to the app api. My server api does not call the graph api directly.
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("api://xxx-xxx-xxx-xx/API.Access");
});
builder.Services.AddMsalAuthentication<RemoteAuthenticationState, RemoteUserAccount>(options =>
{
var scopes = builder.Configuration.GetValue<string>("GraphScopes");
if (string.IsNullOrEmpty(scopes))
{
Console.WriteLine("Warning : No scope permissions founds.");
scopes = "User.Read";
}
foreach (var scope in scopes.Split(";"))
{
global::System.Console.WriteLine($"Adding {scope} to requested permissions");
options.ProviderOptions.DefaultAccessTokenScopes.Add(scope);
}
}).AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, RemoteUserAccount, GraphUserAccountFactory>();
I used this example to extend/create the graph api Named client
https://learn.microsoft.com/en-us/graph/tutorials/blazor
I can authenticate with each of the named clients when testing seperately, and make successful calls against both api's, but when I configure the app for capabilites to call both api's and call the ms graph api I get the following error.
Graph api Response: InvalidAuthenticationToken\nMessage: Access token validation failure. Invalid audience.
I do not have any errors on the login/auth process with both of the items listed as above.
I've looked at configuring multiple auth schemes for .NET 6 as suggested here - https://learn.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme?view=aspnetcore-6.0#use-multiple-authentication-schemes
, but the namespace is not recognized in WASM when trying to configure JWT , and the app fails to build when trying this.
I believe the Audience needs to be configured, but I am unware of how to properly achieve this in .NET 6.
Any advice on how to go about properly utilizing two named clients within one solution ? Or how to properly configure authority within the scope of the graph client ? Thanks
I have an ASP.NET Core 3.1 web API application that is protected by Azure Authentication. I also have an Angular 11 application that calls the web API.
They work fine on our integration environment. But authentication fails on the test environment.
The Angular app authenticates fine using msal-angular library, it gets the access token and includes it when calling the web API. But the web API fails to retrieve the identity from the bearer token.
After deleting and recreating both (frontend and backend) Azure APP registrations several times and after reconfiguring them, the authentication still fails. I don't know what is the cause, so this is why I need to diagnose it.
I have enabled JwtBearerMiddlewareDiagnosticsEvents and I can see 'Begin OnAuthenticationFailedAsync' and 'End OnAuthenticationFailedAsync' on the logs. But it isn't enough information. It tells you no more that the events are raised, no additional information.
I've read the following article on documentation that tells you how to configure logging in MSAL.NET, but I don't think it's applicable to web API.
https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-logging-dotnet
So the question is: How can I figure out why the authentication is failing? Is there any way to log the cause?
UPDATE:
I'm closer, I set log level to trace and now I see the following on the log of asp.net core web api app:
IDX10511: Signature validation failed. Keys tried: '[PII is hidden.
For more details, see https://aka.ms/IdentityModel/PII.]'. \nkid:
'[PII is hidden. For more details, see
https://aka.ms/IdentityModel/PII.]
UPDATE:
I get the following error:
Bearer was not authenticated. Failure message: IDX10214: Audience
validation failed. Audiences: '242e8f40-d795-43bc-8ac8-8eed3df71745'.
Did not match: validationParameters.ValidAudience:
'b99e89fc-8a1c-4605-8c18-796d7064037e' or
validationParameters.ValidAudiences: 'null'.
'242e8f40-d795-43bc-8ac8-8eed3df71745' is the AppId on the web api app registration manifest.
'b99e89fc-8a1c-4605-8c18-796d7064037e' is the client id of the web api registration.
The scope is 'api://242e8f40-d795-43bc-8ac8-8eed3df71745/api-access'.
This is how I set the scope:
export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
const protectedResourceMap = new Map<string, Array<string>>();
protectedResourceMap.set('https://api.example.com/*', ['api://242e8f40-d795-43bc-8ac8-8eed3df71745/api-access']);
return {
interactionType: InteractionType.Popup,
protectedResourceMap
};
}
export function MSALGuardConfigFactory(): MsalGuardConfiguration {
return {
interactionType: InteractionType.Popup,
authRequest: {
scopes: ['api://242e8f40-d795-43bc-8ac8-8eed3df71745/api-access']
},
loginFailedRoute: '/login-failed'
};
}
Your problem has been solved. The problem is that the configuration of the web api is wrong. You need to create them again and reconfigure .
According to my experience, you need to create two applications in Azure, one representing the web api application and the other representing the client application (ie Angular 11 application).
Then you need to expose the api of the web api application in Azure and add the Angular 11 application as a client application to the web api application.
Finally, when requesting an access token, you need to set the scope to: api://{web api client id}/{scope name}.
I have ASP.NET 4.5 service with OWIN pipeline that issues OAuth access_token in exchange for username/password. This service is called from ng-app and once it gets a token, stores in the browsers local storage. Then it calls resource api that consumes this token, API is also written in asp.net 4.5 and uses owin. This being OAuth token issued with OWIN it's encrypted/signed to machineKey secrets - so far so good and is happily consumed by the resource API. All this made possible by OAuthAuthorizationServerMiddleware.
Now I need to consume these tokens sent from the same ng-app to asp.net core 2.1 services, nothing fancy, just verify/decode it and get claims inside the token.
This OAuthAuthorizationServerMiddleware was never ported to asp.net core so I am stuck. (OAuth Authorization Service in ASP.NET Core does not help it talks about the full-fledged oidc server I just need to consume them w/o changing the issuing code)
In ConfigureServices() tacked on to service.AddAuthentication():
Tried.AddJwtBearer - but this makes no sense - these are not Jwt tokens really
Tried.AddOAuth but this does not make sense either b/c I am not dealing with full OAuth flow with redirects to obtain a token, I also don't deal with ClientId/ClientSecret/etc, I just receive "Bearer token-here" in the HTTP header from ng app so I need something in the pipeline to decode this and set ClaimsIdentity but this "something in the pipeline" also needs to have access to machinery-like data that is the same as it is in asp.net 4.5 OWIN service
Any ideas?
You could set the OAuthValidation AccessTokenFormat to use a MachineKey DataProtectionProvider and DataProtector which will protect and unprotect your bearer tokens. You will need to implement the MachineKey DataProtector. This guy already did it https://github.com/daixinkai/AspNetCore.Owin/blob/master/src/DataProtection/src/AspNetCore.DataProtection.MachineKey/MachineKeyDataProtectionProvider.cs.
public void ConfigureServices(IServiceCollection services){
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
ConfigureAuth(services);
string machineKey = #"<?xml version=""1.0"" encoding=""utf-8"" ?>
<machineKey decryption=""Auto"" decryptionKey =""DEC_KEY"" validation=""HMACSHA256"" validationKey=""VAL_KEY"" />";
var machineKeyConfig = new XmlMachineKeyConfig(machineKey);
MachineKeyDataProtectionOptions machinekeyOptions = new MachineKeyDataProtectionOptions();
machinekeyOptions.MachineKey = new MachineKey(machineKeyConfig);
MachineKeyDataProtectionProvider machineKeyDataProtectionProvider = new MachineKeyDataProtectionProvider(machinekeyOptions);
MachineKeyDataProtector machineKeyDataProtector = new MachineKeyDataProtector(machinekeyOptions.MachineKey);
//purposes from owin middleware
IDataProtector dataProtector =
machineKeyDataProtector.CreateProtector("Microsoft.Owin.Security.OAuth",
"Access_Token", "v1");
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddOAuthValidation(option=> {
option.AccessTokenFormat = new OwinTicketDataFormat(new OwinTicketSerializer(), dataProtector); })
It's important to keep the same DataProtector "purposes" Owin uses in the OAuthAuthorizationServerMiddleware so the data is encrypted/decrypted correctly. Those are "Microsoft.Owin.Security.OAuth", "Access_Token" and "v1". (see https://stackoverflow.com/a/29454816/2734166).
And finally you will have to migrate the Owin TicketSerializer (and maybe the TicketFormat too) since the one in NetCore is slightly different. You can grab it from here:
https://github.com/aspnet/AspNetKatana/blob/e2b18ec84ceab7ffa29d80d89429c9988ab40144/src/Microsoft.Owin.Security/DataHandler/Serializer/TicketSerializer.cs
I got this working recently. Basically authenticating to a .NET 4.5 Owin API and running a resource API in NET Core using the same token. I'll try to share the code in github as soon as I clean it up.
As far as I know it's not recommended to keep the old machine key data protector, but to migrate to the new ones from NET Core. Sometimes this is not possible. In my case I have too many APIs already in production, so I'm trying some new NET Core APIs to work with the legacy ones.
You should try this Owin.Token.AspNetCore nuget package instead. By following the code example provided in the README file I'm able to decode legacy tokens using the machine keys on .NET Core 3.1. Note: there's also an option to specify encryption method and validation method if the defaults are not working for you.
I created a react app with google auth which is working fine. Now I want to check google token to my dotnet core web API and validate it.
I tried services.AddAuthentication with the AddGoogle method but it's not working.
I also tired a middleware https://github.com/khellang/Middleware/tree/master/src/Authentication.JwtBearer.Google. But this is not working as well
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(x => x.UseGoogle(
clientId: "<google-client-id>",
hostedDomain: "<optional-g-suite-domain>"));
Can anyone tell how we can check and validate google token using dotnet core API in c# with [Authorize] attribute?
My React app running on localhost:3000 and API is on localhost:5001
Solved it finally! I setup middleware as defined in http://koscielniak.me/post/2016/04/accesing-api-with-token-from-google-identity-provider/
This works for valid tokens in the api request. For invalid tokens, it does not return but its fine for me ...
I've got a IDP implemented in IdentityServer 4. My web app client(implemented in Mvc 5) authenticates with the IDP but now I need to get the access token from the request.
A way to do that in .Net Core is to use the Microsoft.AspNetCore.Authentication.AuthenticationTokenExtensions like so:
HttpContext.Authentication.GetTokenAsync("acccess_token")
I would like to be able to do the same in my .net Mvc5 web app client but I can't find any nuget package or namespace that has a similar implementation. It is important to be able to do this in MVC5 and not .net Core. Anyone came across this before?
PS- Also worth to mention that I'm using OpenIdConnect
The recently released 4.1.0 version of Katana now supports the SaveTokens property (backported from ASP.NET Core).
In order to get the access token:
Update the Microsoft.Owin.Security.OpenIdConnect package to 4.1.0 (or newer)
Configure SaveTokens in your Startup class:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
// Other options removed for readability
SaveTokens = true,
// Required for the authorization code flow to exchange for tokens automatically
RedeemCode = true
});
Read the access token in your Controller:
var result = await Request.GetOwinContext().Authentication.AuthenticateAsync("Cookies");
string token = result.Properties.Dictionary["access_token"];
In your controller you can get the token using this code:
var token = ActionContext.Request.Headers.Authorization.Parameter;
I spent some type before I understood, we need to send a string as an argument of AuthenticateAsync which is used in AuthenticationType and SignInAsAuthenticationType.
I hilly recond to use CookieAuthenticationDefaults.AuthenticationType because it will save you from typos.