ProtectKeysWithAzureKeyVault is not pulling key from Azure KeyVault using .net 4.7.2.. But it works well in .netcore 2.2
I do the following:
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Identity.Application",
CookieName = ".AspNet.SharedCookie",
LoginPath = new PathString("/Account/Login"),
TicketDataFormat = new AspNetTicketDataFormat(
new DataProtectorShim(
DataProtectionProvider.Create(new DirectoryInfo(#"E:\SBH"), (builder) => {
builder
.ProtectKeysWithAzureKeyVault(
"https://test.vault.azure.net/keys/jwt",
"ClientID",
"ClientScret")
.PersistKeysToFileSystem(new DirectoryInfo(#"E:\SBH"));
}).CreateProtector(
"Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware",
"Identity.Application",
"v2"))),
CookieManager = new ChunkingCookieManager()
});
}
The code runs without any errors / exceptions. But not making call to azure key vault.
What is the Problem?
I have installed these two nuget packages
Microsoft.Owin.Security.Interop
Microsoft.AspNetCore.DataProtection.AzureKeyVault
As the article said, the AzureDataProtectionBuilderExtensions.ProtectKeysWithAzureKeyVault Method only apply to asp.net core 2.1 and 2.2. So it will not working in .net4.7.2.
I could solve the issue by using AspNetCore with framework pointing to .NetFramework while creating the solution.
With this arrangement, I could use the .AspNetCore functions along with .Net 4.7.2 assemblies for the backward compatibility.
And Used the Regular .NetCore middleware functions for dataprotection like below
services.AddDataProtection()
.PersistKeysToAzureBlobStorage(container, blobName)
.ProtectKeysWithAzureKeyVault("KeyUri","ClientId", "ClientSecret")
.SetApplicationName("Name");
Related
I got bitten by the SameSite cookie attribute enforcement in Google Chrome few days ago. My problem is that I am on .NET 4.7.1 and upgrading the .NET Framework is "ticket based" so the lead time is prohibitive. The site itself uses ASPNET Identity to issue an authentication cookie. Configuration looks like below:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/"),
CookieName = "cookie-name",
CookieSameSite = SameSiteMode.None,
CookieManager = new SystemWebCookieManagerInNet471(),
CookieSecure = CookieSecureOption.SameAsRequest,
Provider = new CookieAuthenticationProvider
{
OnApplyRedirect = ctx =>
{
if (!IsAjaxRequest(ctx.Request)) // Fail handler for HTTP request
{
// Stuff here
}
else
{
ctx.Response.Redirect(ctx.RedirectUri); // Fail handler for AJAX request
}
}
}
});
The workaround to set the SameSite attribute for my case is not ideal at all (URL Rewrite etc).
As per this, this and this I have set the CookieSameSite attribute (see above). However this does not work due to the static constructor acrobatics in here which binds the implementation to later versions of the .NET Framework.
In order to get around this Framework limitation I extended (sort of hacked) the SystemWebCookieManager. Gist (line no 107). Has anyone gone down this road? and hit strange behaviour in production.
I am aware this solution will not work for CSRF or SessionId cookie, but for my case this is ok. I also noticed that there is a Chunked cookie manager too, I think (+ hope) I'll not hit a chunking use case.
I'm trying to implement Microsoft Authentication (owin) on an existing WebForms web application. I can't use the official guides from Microsoft, since they are made for modern solutions with MVC.
Any tips on where to start? I can't find any guides.
Please follow the document which helps you in old .NET forms to use OWIN Forms middleware and to use this middleware for Azure AD authentication please follow Docs.
Install the package for Microsoft.Owin.Security.Cookies
Right click the App_Start folder on your project Add -> New Item - > "OWIN Startup Class"
then create the following folder
public void Configuration(IAppBuilder app)
{
CookieAuthenticationOptions opt = new CookieAuthenticationOptions();
opt.AuthenticationType = CookieAuthenticationDefaults.AuthenticationType;// "Identity.Application";
opt.CookieName = ".SSO";
opt.CookieDomain = "localhost";
opt.SlidingExpiration = true;
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("192.168.1.96:6379");
IDataProtector proc = DataProtectionProvider.Create(new DirectoryInfo(#"C:\test\core"), buildAction =>
buildAction.SetApplicationName("MyApp").SetDefaultKeyLifetime(TimeSpan.FromDays(9000)).ProtectKeysWithDpapi().PersistKeysToStackExchangeRedis(redis)).CreateProtector("Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", "Cookies", "v2");
DataProtectorShim shim = new DataProtectorShim(proc);
opt.TicketDataFormat = new AspNetTicketDataFormat(shim);
app.UseCookieAuthentication(opt);
}
I am currently looking into Orleans as a backend for Asp.net core web api project, and was wondering if anyone has any experience with its new feature - "direct client". The orleans docs say "it allows co-hosting a client and silo in a way that let the client communicate more efficiently with not just the silo it's attached to, but the entire cluster", and I am aware that you can code something like this (and it works just fine in a console app):
var silo = new SiloHostBuilder()
.UseLocalhostClustering()
.EnableDirectClient()
.Build();
await silo.StartAsync();
var client = silo.Services.GetRequiredService<IClusterClient>();
I am struggling trying to figure out where to put this type of code in an asp.net project that has its own webhost builder in "Main" (should it go to Startup class in "ConfigureServices"?). In the end, we are aiming for a separate client/server setup, but for faster development it would be useful to play with a simple setup, which direct client appears to allow for. Any pointers to resources and/or sample solutions containing direct client with asp.net core would be appreciated. Thanks.
EDIT: Here's the code that kinda works for me now, but I am not happy with he way the DI is set up
public static async Task Main(string[] args)
{
var silo = new SiloHostBuilder()
.UseLocalhostClustering()
.ConfigureServices(services =>
{
services.AddDbContext<UserDbSet>(o => o.UseSqlite("Data Source=UserTest.db"));
services.AddMediatR(typeof(Startup).Assembly);
})
.EnableDirectClient()
.Build();
await silo.StartAsync();
var client = silo.Services.GetRequiredService<IClusterClient>();
await WebHost.CreateDefaultBuilder(args)
.UseConfiguration(new ConfigurationBuilder()
.AddCommandLine(args)
.Build())
.ConfigureServices(services =>
services
.AddSingleton<IGrainFactory>(client)
.AddSingleton<IClusterClient>(client))
.UseStartup<Startup>()
.Build()
.RunAsync();
}
If I put registration of the DbContext and Mediatr in the StartUp class, grain code fails with an exception indicating failure to instantiate the required dependencies. Maybe I am doing something wrong when setting up the Webhost?
For ASP.NET 2.x & Orleans below 2.3, I recommend creating & starting the silo before the Web host. When configuring the Web host, inject the IGrainFactory & IClusterClient instances from the silo (obtained via silo.Services):
var silo = new SiloHostBuilder()
.UseLocalhostClustering()
.EnableDirectClient()
.Build();
await silo.StartAsync();
var client = silo.Services.GetRequiredService<IClusterClient>();
var webHost = new WebHostBuilder()
.ConfigureServices(services =>
services
.AddSingleton<IGrainFactory>(client)
.AddSingleton<IClusterClient>(client))
.UseStartup<Startup>()
// Other ASP.NET configuration...
.Build();
For ASP.NET 3.0 & Orleans 2.3 or greater, the integration code becomes simpler due to the addition of Microsoft.Extensions.Hosting support in both frameworks:
var host = new HostBuilder()
.ConfigureWebHost(builder =>
{
// Adding IGrainFactory, etc, is not necessary, since Orleans
// and ASP.NET share the same dependency injection container.
builder.UseStartup<Startup>();
})
.UseOrleans(builder =>
{
// EnableDirectClient is no longer needed as it is enabled by default
builder.UseLocalhostClustering();
})
.Build();
await host.StartAsync();
In my application I use MVC and Web.API.
The MVC portion handles the admin front-end, serving the cshtml pages, communication with the back-end, regular authentication using cookies, etc:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
The Web.API one handles the REST requests made by the iOS and Android applications. For that one I want to use token based authentication:
var oAuthServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true, //todo-err: change in prod
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
My question is, what do I need to make this work? Regular and token authentication. Will I need to create a custom AuthorizeAttribute for the API controllers?
I appreciate any help :)
Your SimpleAuthorizationServerProvider needs to be implemented.
Generating a WebAPI project in VS2015 with authentication gives you something like this.
The article Applying Cookie-Stored Sessions With ASP.NET and OpenID Connect 1.0 by Kelvin Amorim is very useful for understanding multiple factors for supporting multiple authentication middlewares.
Some main points are:
Each authentication options middleware should use a different AuthenticationType (it is just a string key and there are some defaults to choose from)
You can set the cookie path and use corresponding MVC Areas (see RouteAreaAttribute) to control which cookies are used for which requests
I have a self-hosted OWIN application configured as an authorization server and a signalr resource server.
My clients are successfully obtaining the bearer token and presenting it for authorization in subsequent calls to the signalR hub.
My next step is to decouple the authorization service so that it can run on its own host. To get started I created a separate self-hosted app that contains only the authorization service code. It's still all in one solution on my development machine, but the authorization service and signalR resources are hosted in separate processes.
The auth flow is still working properly. The token is getting to my resource server, but now getting 401 unauthorized from the signalR hub.
There is alot of support out there for solving this in ASP.Net Web API, in which you would sync up a machine.config value in your web.config files. But that is not my architecture. Running as a self-hosted app under HttpListener uses different encryption, DPAPI by default.
There doesn't seem to be much discussion out there about solving this in a self-hosted architecture. My theory is that even under different processes on the same machine, the DPAPI decryption is failing and so I get 401.
I'm trying to figure out if there is some minimal approach to solving this or if I have to completely refactor maybe to use JWT instead.
EDIT: adding some code to help display my setup
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = false,
TokenEndpointPath = new PathString("/account/login"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
public void ConfigureOAuth(IAppBuilder app)
{
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
Provider = new ApplicationOAuthBearerAuthenticationProvider(),
});
}
Posting my own solution hopefully to help someone else down the road.
I did decide to implement a JWT solution rather than use the default. I think this is the better architecture anyway, decoupling your token encryption from the OS. I used this tutorial http://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/
The crucial bits were creating your custom OAuthAuthorizationServerProvider and ISecureDataFormat for encrypting the token as shown in the tutorial. This just shows the OWIN config.
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = false,
TokenEndpointPath = new PathString("/account/login"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new JwtAuthorizationServerProvider(),
AccessTokenFormat = new CustomJwtFormat("https://foo.test.com")
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
Another issue you might face is getting the token to SignalR, where setting the Authorization header is not as straight forward as you might think. As it happens the cookie based implementation in this tutorial worked beautifully with JWT as well! http://blog.marcinbudny.com/2014/05/authentication-with-signalr-and-oauth.html#.VmWgMXarSCd
Again here is the OWIN config example.
public void ConfigureOAuth(IAppBuilder app)
{
//app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
//{
// Provider = new ApplicationOAuthBearerAuthenticationProvider()
//});
var issuer = "https://foo.test.com";
var audience = "client_id";
var secret = TextEncodings.Base64Url.Decode("ABCDEF");
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audience },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
},
Provider = new ApplicationOAuthBearerAuthenticationProvider()
});
}
FWIW, consider that self-hosted OWIN authorization servers use DPAPI protection, but ASP.NET apps default to MachineKey data protection.
If you need to make those two collaborate, in the OWIN config you can specify a provider like this:
app.SetDataProtectionProvider(new DpapiDataProtectionProvider("myApp"));
Just make sure to add it in both Configuration methods for the IAppBuilder (Both projects)
HTH