Ahh joy, after a wait few weeks long I an finally typing on by brand new purple type cover 2, snapped to a shiny new Surface Pro 2! I just *have* to use this guy, and what better mixed workload than writing a blog post? That will exercise Visual Studio, the portal, Live Writer… it’s a comprehensive workout!

Today I’d like to talk about some features of the new organizational ASP.NET project templates, which might catch you by surprise if you don’t know about it.

The New Templates Save Validation Settings in a DB

Here there’s the customary backgrounder. If you don’t care about the theory, you can skip to the last section right away.

As many of you know, the essence of authenticating a call boils down to deciding whether the caller is presenting a valid token with his/her/its request. What determines if a token should be considered valid? Well, the usual: some aspects are purely structural (it should be signed and not having been tampered with; should be of the expected format; should be scoped for your app; should not be expired; and so on) while others are expressions of the trust relationship between your application and the authority you are outsourcing your authentication to. In a nutshell, the latters boil down to the following: the incoming token should be signed with the key that you know belongs to the authority you trust (or the infrastructure the authority uses for minting tokens), and should have an Issuer value corresponding to it too.

Traditionally, WIF 1.0 and the corresponding classes in .NET 4.5 kept track of those values by saving them in the web.config. I already spoke about some of the limitations the original classes had in terms of expressive power and maintaining the settings fresh.
Although for relatively stable scenarios you are fine, there are multiple issues in keeping those in the config if your settings change often and if you have to keep tab of a large number of authorities (if you sell SaaS apps, that’s a good place to be).
Given that those are scenarios of great interest, the ASP.NET templates use the WIF extensibility endpoints to save the validation coordinates in a database. The process is pretty straightforward, although a bit verbose: you can trace it back by looking of the autogenerated code in the template. Starting from the web.config, the first change is in the <identityConfiguration> element:

 <system.identityModel>
    <identityConfiguration>
      <issuerNameRegistry type="WebApplication1.Utils.DatabaseIssuerNameRegistry, WebApplication1" />
      <audienceUris>
        <add value="https://contoso7.onmicrosoft.com/WebApplication1" />
      </audienceUris>
      <securityTokenHandlers>
        <add type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
        <remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
      </securityTokenHandlers>
      <certificateValidation certificateValidationMode="None" />
    </identityConfiguration>
  </system.identityModel>

The key line is the one highlighted. There the template specifies that the issuer validation coordinates are handled by a custom class, DatabaseIssuerNameRegistry. Its code is included in the template itself, and in fact it doesn’t do anything especially glamorous, in fact it’s pretty much what you’d find in the custom IssuerNameRegistry here: the main difference is that instead of shoving keys and tenantIDs in an XML file, the template saves it in a DBContext.

That is of course very nice, but it does come with some extra requirements in respect to the web.config-only version: now your app needs to have a database. For many of you this is likely to be an absolute no-brainer, given that chances are that you app already needed a DB for its own purposes: but for some other, who perhaps are just playing with the templates for figuring this identity stuff out, you might simply not have one (yet).
When you run your app on IIS Express that does not really make any difference, but it does when you deploy to Windows Azure Web Sites. If you publish without providing a DB connection string, on first execution (and assuming you have the custom errors off) you’ll get the following, in all its unedited glory:

Server Error in ‘/’ Application.


The system cannot find the file specified

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.ComponentModel.Win32Exception: The system cannot find the file specified
Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[Win32Exception (0x80004005): The system cannot find the file specified]

[SqlException (0x80131904): A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 52 - Unable to locate a Local Database Runtime installation. Verify that SQL Server Express is properly installed and that the Local Database Runtime feature is enabled.)]
   System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) +5296071
   System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) +558
   System.Data.SqlClient.TdsParser.Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, Boolean ignoreSniOpenTimeout, Int64 timerExpire, Boolean encrypt, Boolean trustServerCert, Boolean integratedSecurity, Boolean withFailover) +5308555
   System.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, Boolean withFailover) +145
   System.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString connectionOptions, SqlCredential credential, TimeoutTimer timeout) +920
   System.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectionOptions, SqlCredential credential, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance) +307
   System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions) +434
   System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions) +225
   System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions) +37
   System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnectionOptions userOptions) +558
   System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnectionOptions userOptions) +67
   System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection) +1052
   System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection) +78
   System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection) +167
   System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions) +143
   System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry) +83
   System.Data.SqlClient.SqlConnection.Open() +96
   System.Data.Entity.SqlServer.<>c__DisplayClass2f.<UsingConnection>b__2d() +73
   System.Data.Entity.SqlServer.<>c__DisplayClass1.<Execute>b__0() +10
   System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute(Func`1 operation) +189
   System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute(Action operation) +78
   System.Data.Entity.SqlServer.SqlProviderServices.UsingConnection(DbConnection sqlConnection, Action`1 act) +229
   System.Data.Entity.SqlServer.SqlProviderServices.UsingMasterConnection(DbConnection sqlConnection, Action`1 act) +376
   System.Data.Entity.SqlServer.SqlProviderServices.GetDbProviderManifestToken(DbConnection connection) +201
   System.Data.Entity.Core.Common.DbProviderServices.GetProviderManifestToken(DbConnection connection) +85

[ProviderIncompatibleException: The provider did not return a ProviderManifestToken string.]
   System.Data.Entity.Core.Common.DbProviderServices.GetProviderManifestToken(DbConnection connection) +269
   System.Data.Entity.Utilities.DbProviderServicesExtensions.GetProviderManifestTokenChecked(DbProviderServices providerServices, DbConnection connection) +33

[ProviderIncompatibleException: An error occurred while getting provider information from the database. This can be caused by Entity Framework using an incorrect connection string. Check the inner exceptions for details and ensure that the connection string is correct.]
   System.Data.Entity.Utilities.DbProviderServicesExtensions.GetProviderManifestTokenChecked(DbProviderServices providerServices, DbConnection connection) +193
   System.Data.Entity.Infrastructure.<>c__DisplayClass1.<ResolveManifestToken>b__0(Tuple`3 k) +32
   System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory) +72
   System.Data.Entity.Infrastructure.DefaultManifestTokenResolver.ResolveManifestToken(DbConnection connection) +180
   System.Data.Entity.Utilities.DbConnectionExtensions.GetProviderInfo(DbConnection connection, DbProviderManifest& providerManifest) +56
   System.Data.Entity.DbModelBuilder.Build(DbConnection providerConnection) +43
   System.Data.Entity.Internal.LazyInternalContext.CreateModel(LazyInternalContext internalContext) +62
   System.Data.Entity.Internal.RetryLazy`2.GetValue(TInput input) +123
   System.Data.Entity.Internal.LazyInternalContext.InitializeContext() +611
   System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType) +18
   System.Data.Entity.Internal.Linq.InternalSet`1.Initialize() +53
   System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext() +15
   System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Provider() +38
   System.Linq.Queryable.Where(IQueryable`1 source, Expression`1 predicate) +63
   WebApplication1.Utils.DatabaseIssuerNameRegistry.ContainsKey(String thumbprint) +347
   WebApplication1.Utils.DatabaseIssuerNameRegistry.RefreshKeys(String metadataLocation) +89
   WebApplication1.IdentityConfig.RefreshValidationSettings() +31
   WebApplication1.IdentityConfig.ConfigureIdentity() +8
   WebApplication1.MvcApplication.Application_Start() +18

[HttpException (0x80004005): An error occurred while getting provider information from the database. This can be caused by Entity Framework using an incorrect connection string. Check the inner exceptions for details and ensure that the connection string is correct.]
   System.Web.HttpApplicationFactory.EnsureAppStartCalledForIntegratedMode(HttpContext context, HttpApplication app) +9863473
   System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) +118
   System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) +172
   System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) +336
   System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext) +296

[HttpException (0x80004005): An error occurred while getting provider information from the database. This can be caused by Entity Framework using an incorrect connection string. Check the inner exceptions for details and ensure that the connection string is correct.]
   System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +9877804
   System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +101
   System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +254


Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.19064

 

That clearly cannot stand. In the next section I’ll look at one way of making that error go away. Thanks to Dan Roth for the helpful chat about how to do so with the shortest path! Smile

Adding a DB to an Existing Windows Azure Web Site and Using It at Publication Time

The fix for the issue is simple, you need to have a DB in your Web Site and you need to specify it at publication time. Luckily, the Windows Azure portal make adding a DB to an existing Web Site a walk in the park!

Start by navigating to https://manage.windowsazure.com, zero on the Web Sites section and pick the site you want to target. Once there,  head to the Linked Resources tab. If you originally set up the site via Quick Create, you’ll see something like the following:

image

Hit the “link a resource” button.

image

Choose “Create a new resource”.

image

Lots of choices there! I’ll pick “SQL Database”.

image

In my case, I am happy with the default name… also, I have an existing server I can latch to, but you can create one on the fly if you don’t (very neat).

Once you are done choosing your options, hit the “done” button.

image

Back on the linked resources screen, you’ll see that you now have your new DB linked to your web site.

Go back to the dashboard. Once here, download the publish profile for your web site (you’ll the link under “quick glance”).

Switch to Visual Studio and start another Publish operation; this time, import the new publish profile. Go to Settings.

image

As you can see, you now have your newly created/linked DB available in the Databases section. Select it in the dropdown and hit Publish. Wait for it and…

image

…instead of that nasty error, you get the shiny default bootstrap themed app UI! Smile

 

That’s it. Pretty straightforward, right?

Note, this applies only to the Web UX (MVC/Web Form) projects when used with Organizational Accounts options in the cloud (Single Organization and Multiple Organizations, cloud). For Single Organization –On Premises the template still sticks with the good ol’ValidatingIssuerNameRegistry.

The use of a DB for validation coordinates is great for multi organization apps (SaaS, multi-tenancy, etc). Personally, I am less convinced about it when it comes to single organization projects (e.g. LOB apps) given that the validation coordinates amount to 2 strings, one of which will change sparingly during the app lifetime; however I fully trust my friends in ASP.NET, who tell me that every app will need a DB anyway hence this is not going to really add any requirement that wasn’t already there. In any case, if you stumble in that hopefully the big verbatim dump with the error text will lure your search engine here to the solution. Let us know how that goes!

 

Leave a Reply

Your email address will not be published. Required fields are marked *