Closed dcinzona closed 9 years ago
Startup.cs
using System.Collections.Generic;
using System.Linq;
using Custom.Web.Module.IdentityServer.Config;
using Custom.Web.Module.IdentityServer.Services.IdentityServer;
using Orchard;
using Orchard.Logging;
using Orchard.Owin;
using Owin;
using Thinktecture.IdentityServer.Core.Configuration;
using Thinktecture.IdentityServer.Core.Logging;
using Thinktecture.IdentityServer.Core.Models;
using Thinktecture.IdentityServer.Core.Services;
using Thinktecture.IdentityServer.EntityFramework;
using Thinktecture.IdentityServer.WsFederation.Configuration;
using Thinktecture.IdentityServer.WsFederation.Models;
using Thinktecture.IdentityServer.WsFederation.Services;
namespace Custom.Web.Module.IdentityServer
{
public class Startup : IOwinMiddlewareProvider
{
private readonly IWorkContextAccessor _wca;
public ILogger Logger { get; set; }
public Startup(
IWorkContextAccessor wca)
{
_wca = wca;
Logger = NullLogger.Instance;
}
public IEnumerable<OwinMiddlewareRegistration> GetOwinMiddlewares() {
var middleWare = new List<OwinMiddlewareRegistration> {
new OwinMiddlewareRegistration {
Priority = "100",
Configure = app => app.Map("/identity", coreApp => {
var efConfig = new EntityFrameworkServiceOptions
{
ConnectionString = "Website",
Schema = "IdentityServer"
};
// Copy InMememory Clients to EF
ConfigureClients(Clients.Get(), efConfig);
ConfigureScopes(Scopes.Get(), efConfig);
var factory = new IdentityServerServiceFactory();
// Will log to trace provider for Debugging IdentityServer
LogProvider.SetCurrentLogProvider(new DiagnosticsTraceLogProvider());
// Register Orchard User UserService, pass in IWorkContextAccessor so it can access Orchard Pipeline
var userService = new UserService(_wca);
factory.UserService = new Registration<IUserService>(resolver => userService);
// Entity Framework Stores
factory.RegisterOperationalServices(efConfig);
factory.RegisterClientStore(efConfig);
factory.RegisterScopeStore(efConfig);
// Custom View service to override front-end
factory.ViewService = new Registration<IViewService>(typeof(ViewService));
// Set options and register Identity Server with OWIN
var options = new IdentityServerOptions {
SiteName = "Website Single Sign-On",
EnableWelcomePage = false,
SigningCertificate = Certificate.Get(),
Factory = factory,
PluginConfiguration = ConfigurePlugins, // WS-Fed
CorsPolicy = CorsPolicy.AllowAll,
AuthenticationOptions = new AuthenticationOptions
{
IdentityProviders = ConfigureIdentityProviders,
EnableSignOutPrompt = true
}
};
coreApp.UseIdentityServer(options);
})
}
};
return middleWare;
}
public static void ConfigureIdentityProviders(IAppBuilder app, string signInAsType)
{
// TODO: Add Staff config here when WS-Federation enpoint is available
// https://identityserver.github.io/Documentation/docs/configuration/identityProviders.html
}
private void ConfigurePlugins(IAppBuilder pluginApp, IdentityServerOptions options)
{
var wsFedOptions = new WsFederationPluginOptions(options);
// data sources for in-memory services
wsFedOptions.Factory.Register(new Registration<IEnumerable<RelyingParty>>(RelyingParties.Get()));
wsFedOptions.Factory.RelyingPartyService = new Registration<IRelyingPartyService>(typeof(InMemoryRelyingPartyService));
pluginApp.UseWsFederationPlugin(wsFedOptions);
}
public static void ConfigureClients(IEnumerable<Client> clients, EntityFrameworkServiceOptions options)
{
using (var db = new ClientConfigurationDbContext(options.ConnectionString, options.Schema))
{
if (!db.Clients.Any())
{
foreach (var c in clients)
{
var e = c.ToEntity();
db.Clients.Add(e);
}
db.SaveChanges();
}
}
}
public static void ConfigureScopes(IEnumerable<Scope> scopes, EntityFrameworkServiceOptions options)
{
using (var db = new ScopeConfigurationDbContext(options.ConnectionString, options.Schema))
{
if (!db.Scopes.Any())
{
foreach (var s in scopes)
{
var e = s.ToEntity();
db.Scopes.Add(e);
}
db.SaveChanges();
}
}
}
}
}
I'm a little confused. <system.identityModel>
has nothing to do with IdentityServer3's sessions -- we use the cookie authentication middleware. It does rely upon the host's protection (e.g. <machineKey>
) for the cookies, though.
The <system.identityModel>
was something that we were trying out to see if it would resolve the issue, but it obviously didn't. We are also quite a bit confused as to why switching between Azure Web App instances would break the session... Does the machine key value look ok? Any ideas as to why this would be breaking when swapping between instances? I can trigger this 100%, just by changing the ARRAffinity cookie and refreshing the page.
I don't know Azure very well, so I cant' speak to how it works. But our authentication cookie relies upon the host's machineKey -- so if you're switching servers and they have different keys, then it would explain it.
They have the same web.config and machine key. Azure Web Apps use a shared file system, so those are exactly the same in multiple instances. I can verify this by hitting reports, which also uses the machine key to decrypt a signing token, and I can bounce between instances without issue.
Here's an SO describing the Azure Web Apps (formerly known as Azure Websites) file system: http://stackoverflow.com/questions/23136706/is-the-file-system-shared-across-multiple-azure-websites
BTW, we are using the WS Federation module...not sure if that makes a difference.
So we've been experimenting some and something odd came up. I tested changing the machine key and refreshing the /identity/permissions page. Changing the machine key in the web.config file prompted a app pool restart. After refreshing that page, my session stayed active even with a different machine key. I verified that the machine key changed because I am displaying it. At this point, I don't know how it's decrypting the cookie when the site restarted with a completely different machine key.
Just to be sure this was a full site restart, I actually went into the Azure Management portal and told it to restart the site that way as well. This did not affect my session and I remained logged in. But when I swap instances, my session breaks. Is there a setting somewhere to specify auth cookie encryption using the machine key? I'm guessing it's the default, but I'm wondering if we changed it inadvertently somehow.
This is resolved! Had to specify the use of IdentityServer's x509CertificateDataProtector and use that as the machine key was not being used at all. Works like a charm!
var options = new IdentityServerOptions {
SiteName = "QA Single Sign-On",
EnableWelcomePage = false,
SigningCertificate = Certificate.Get(),
Factory = factory,
PluginConfiguration = ConfigurePlugins, // WS-Fed
//CorsPolicy = CorsPolicy.AllowAll, //Handled by CorsPolicyService
AuthenticationOptions = new AuthenticationOptions
{
IdentityProviders = ConfigureIdentityProviders,
EnableSignOutPrompt = true,
CookieOptions = new CookieOptions {
SecureMode = CookieSecureMode.Always
}
},
DataProtector = new X509CertificateDataProtector(Certificate.Get())
};
coreApp.UseIdentityServer(options);
So azure wasn't providing one? Or was it orchard?
Azure runs the instances under different profiles and this was causing the DPAPI to be unique. It was an azure issue since running orchard locally seemed to work fine (although, hard to tell since I can't run multiple instances locally :P )
Ok, so the short of it is that in azure you needed to set the data protector. Ok, thanks for the info/
I believe this is only the case when deploying to Azure Web Apps, not cloud services, as those are independent VMs, where you do have control of IIS, etc. (however, I have not tested this on cloud services, so can't be certain - but given the architecture differences between the two, I think this is the case)
Actually, upon further review, it appears this has to do more specifically with Orchard CMS. Orchard uses System.Web.Hosting
, not Microsoft.Owin.Host.SystemWeb
- so from my understanding this falls back to DpapiDataProtector instead of machine key usage.
Similar to what was posted here https://github.com/IdentityServer/IdentityServer3/issues/1029#issuecomment-77358100
I submitted this question to the gitter forum, but it might be better suited here. We are having an issue where we are unable to maintain a logged in session on multiple page refreshes and can manually trigger a log out by switching between azure web app instances (this is done by changing the value of the ARRAFFINITY cookie to the ID of the other instance. We have the machine key configured under
<system.web>
and have an entry for specifying Identity Model use the machine key:Our machine key entry (cleared for security):
Errors I'm seeing:
and