If you're looking for help with C#, .NET, Azure, Architecture, or would simply value an independent opinion then please get in touch here or over on Twitter.
In my previous post I looked at how to register, login and authenticate using the new OWIN based ASP.Net architecture underpinning MVC 5 and Web API 2. The default website provided was configured to use SQL database which is why we needed to configure a SQL Database within Azure as we deployed our website.
There’s a fair chance, if you’re experienced with Azure, that you’re wondering if you can swap that out and use Table Storage, fortunately one of the improvements in this latest version of ASP.Net is to better abstract storage away from management.
There is however a fair bit of leg work to doing so. I’m going to firstly touch on how you go about this, then look at the NuGet package I’ve put online (source code in GitHub) that means you don’t have to do this leg work!, and finally we’ll look at the changes you would need to make in the Web API 2 sample project we introduced in the previous post. I’m going to make reference to that code so you can quickly grab it from GitHub if that’s useful.
1) Implementing a New Identity Store
The clue to how to go about this can, again, be found at the top of the Startup.Auth.cs file in the App_Start folder:
A factory function is assigned and asked to return a user manager and a user store.
The UserManager is a core class of the identity framework (Microsoft.AspNet.Identity.Core) and works with classes that describe users through the IUser interface (another core class) and persists them with implementations of the IUserStore interface.
The core identity framework provides no storage implementation and the UserStore class that is being instantiated here is provided by Microsoft.AspNet.Identity.EntityFramework as is the IdentityUser class.
In fact if we look at the Microsoft.AspNet.Identity.Core assembly we can see it’s really very focussed on managing abstract interfaces:
It’s not difficult to see where we’re going at this point – to implement our own store we need to provide implementations for a number of interfaces. If we want to replicate full local identity management in the same way as the Entity Framework supplied implementation then realistically we need to implement most of the interfaces shown above – IRole, IRoleStore, IUser, IUserClaimStore, IUserLoginStore, IUserPasswordStore, IUserRoleStore, IUserSecurityStampStore and IUserStore.
That’s not as daunting as it sounds as most of the interfaces are quite simple, for example IUserStore:
The remaining interfaces also follow this asynchronous CRUD pattern and are fairly simple to implement, here’s the Entity Framework implementation for CreateAsync:
And by way of contrast here’s a Table Storage implementation:
Pretty much the same I think you’ll agree, however it’s still a lot of boilerplate code to write so I’ve wrapped it into a NuGet package called AccidentalFish.AspNet.Identity.Azure which can also be found on GitHub.
2) Using AccidentalFish.AspNet.Identity.Azure
To get started either download the package from NuGet using the package manager in Visual Studio or download the source from GitHub and attach the project to your solution. The NuGet package manager console to install the package is:
Install-Package accidentalfish.aspnet.identity.azure
You can use the package in commercial or open source projects as it’s published under the permissive MIT license though as ever I do appreciate an email or GitHub star if it’s useful to you – yes I’m that vain (and I like to hear about my code being used).
Once you’ve got the package installed you’ll find there is still a little work to do to integrate it into your Web API 2 project as although the Microsoft.AspNet.Identity framework is nice and clean it seems that whoever put the Web API 2 project template didn’t think it made sense to keep a nice level of abstraction and have tied it tightly to the Entity Framework implementation in a few places.
However it’s not too onerous (just a couple of steps) and I’ve built the package with replacement in mind. To help I’ve included the Web API 2 project from my previous post and commented out the old Entity Framework code that bleeds into the MVC host site. I’ll walk through these changes below.
Firstly we need to visit the Startup.Auth.cs file and make three changes at the start:
1) Update the factory assignment to return a UserManager that manipulates users of type TableUser and users a data store of type TableUserStore. Pass it your Azure connection string. The constructor is overloaded with parameters for table names and whether or not to create them if they don’t exist – by default it will.
2) Replace the ApplicationOAuthProvider with a generic version of it contained within the package. This code is exactly the same but replaces the fixed IdentityUser types with a generic (you can see what I was referring to in regard to the template – this ought to have been this way out of the box).
3) Update the declaration for the factory to return a UserManager manipulating users of type TableUser.
At this point we’ve done the bulk of the OAuth work but unfortunately the MVC AccountController also has a, needless, hard dependency on the Entity Framework library so we need to sort that out. To do this either go through the class and replace the type declarations of IdentityUser and IdentityUserLogin with TableUser and TableUserLogin respectively. Alternatively you can “cheat” and remove the Microsoft.AspNet.Identity.EntityFramework using reference and add a pair of aliases:
That’s it you’re done. Identity information should now be persisted in Azure Table Storage.
I’ll be doing some more work on this package in the coming weeks – I want to test it at scale and I know I need to build at least one index for one of the IUserStore calls which queries in the reverse way to which the partition and row key are set up: I’ve tried to set up the partition and row keys for the most commonly used methods.
If you find any problems or have any feedback then please let me know.
great article thanks.
any ideas why
public ICollection Logins
returns null when I try to login with Google as an external account? I get a Server 500 error.I can force it to create the user by setting the logins to a list but then no logins are created in azure table storage – am I missing something critical? any comment welcome.
get
{
_logins = new List();
return _logins;
}
Thanks for the feedback – there’s a bug in the current implementation as using Google causes invalid characters to be placed in the partition and row keys.
A fix is on the way.
Thanks for the feedback, there’s a bug in the current implementation as using Google causes invalid characters to be placed in the partition and row keys. A fix is on the way.
With Facebook is workin perfect?
I’ve not needed to use it myself for some time but as far as I’m aware it was yes.
could it be because the google externalLogin.ProviderKey returns as a URL
"https://www.google.com/accounts/o8/id?id=xxxxxxxxxx....."
which is not permitted as a table storage rowkey? what else am i missing? was this tested with any of the external providers or only with the local site registration? I can get that part working but unfortunately not with Google. I will try twitter and fackbook shortly.Thanks again – any comment welcome.
Thats a bug at the moment I’m afraid. Apologies for the delay in replying – been snowed under in Jan. A fix is on the way.
This is exactly what I’m looking for. Many thanks for making this available. I am building an MVC5 application using the very latest (as of Jan 24th) templates and assemblies, all updated from nuget. Although the steps to include the TableUserStore are very different from those detailed above, I have managed to get basic registration working. I am having trouble however associating my Google account with the app. An entry is made into the ‘users’ table, but the app crashes on line 274 of the AccountController.cs which is “result = await UserManager.AddLoginAsync(user.Id, info.Login);”. Any ideas what the problem might be? I have included the full server response below. Many thanks for your help, Kurt
Server Error in ‘/’ Application.
The remote server returned an error: (400) Bad Request.
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.Net.WebException: The remote server returned an error: (400) Bad Request.
Source Error:
Line 272: if (result.Succeeded)
Line 273: {
Line 274: result = await UserManager.AddLoginAsync(user.Id, info.Login);
Line 275: if (result.Succeeded)
Line 276: {
Source File: c:\Users\Kurt\Documents\Visual Studio 2013\Projects\InergySystemsV2\InergySystemsV2\Controllers\AccountController.cs Line: 274
Stack Trace:
[WebException: The remote server returned an error: (400) Bad Request.]
System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult) +6432502
Microsoft.WindowsAzure.Storage.Core.Executor.Executor.EndGetResponse(IAsyncResult getResponseResult) +333
[StorageException: The remote server returned an error: (400) Bad Request.]
Microsoft.WindowsAzure.Storage.Core.Executor.Executor.EndExecuteAsync(IAsyncResult result) +277
Microsoft.WindowsAzure.Storage.Table.CloudTable.EndExecute(IAsyncResult asyncResult) +44
Microsoft.WindowsAzure.Storage.Core.Util.<>c__DisplayClass1`1.<CreateCallback>b__0(IAsyncResult ar) +140
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +93
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +52
System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +24
AccidentalFish.AspNet.Identity.Azure.<AddLoginAsync>d__1d.MoveNext() +586
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +93
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +52
System.Runtime.CompilerServices.TaskAwaiter.GetResult() +21
Microsoft.AspNet.Identity.<AddLoginAsync>d__53.MoveNext() +1702
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +93
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +52
System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +24
InergySystemsV2.Controllers.<ExternalLoginConfirmation>d__35.MoveNext() in c:\Users\Kurt\Documents\Visual Studio 2013\Projects\InergySystemsV2\InergySystemsV2\Controllers\AccountController.cs:274
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +93
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +52
System.Runtime.CompilerServices.TaskAwaiter.GetResult() +21
System.Threading.Tasks.TaskHelpersExtensions.ThrowIfFaulted(Task task) +61
System.Web.Mvc.Async.TaskAsyncActionDescriptor.EndExecute(IAsyncResult asyncResult) +114
System.Web.Mvc.Async.<>c__DisplayClass37.<BeginInvokeAsynchronousActionMethod>b__36(IAsyncResult asyncResult) +66
System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +47
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +136
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +102
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) +49
System.Web.Mvc.Async.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f() +117
System.Web.Mvc.Async.<>c__DisplayClass48.<InvokeActionMethodFilterAsynchronouslyRecursive>b__41() +323
System.Web.Mvc.Async.<>c__DisplayClass33.<BeginInvokeActionMethodWithFilters>b__32(IAsyncResult asyncResult) +44
System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +47
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +136
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +102
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) +50
System.Web.Mvc.Async.<>c__DisplayClass2b.<BeginInvokeAction>b__1c() +72
System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult) +185
System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +42
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +133
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +56
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +40
System.Web.Mvc.Controller.<BeginExecuteCore>b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +34
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +70
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +139
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +59
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +44
System.Web.Mvc.Controller.<BeginExecute>b__15(IAsyncResult asyncResult, Controller controller) +39
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +62
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +139
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +59
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +39
System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +39
System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +39
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +70
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +139
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +59
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +40
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +38
System.Web.CallHandlerExecutionStep.OnAsyncHandlerCompletion(IAsyncResult ar) +129
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.33440
Thanks for the detailed bug report – I’ve got a fix on the way. Including it in MVC 5 is quite different to Web API, hopefully I can get that documented soon.
That’s great. I’ve been rolling my own user management system for all my Azure projects, so it would ideal to be able to rely on the built-in MVC functionality and still use Azure. If you need any help testing than please just let me know.
So I’ve been trying to get this working on and off for a couple of weeks now, and ran into a wall. I’m trying to deploy a simple Azure Cloud web role with asp.net identity login for a web api. Everything seems to run fine locally (after manually updating a few packages and allowing Visual Studios to add some binding redirects), but when I publish to Azure it gets stuck in a constant role recycle loop. After lots of debugging and looking at logs, the only thing I can find that seems like it might be the issue is an error I get when debugging WaIISHost:
— System.IO.FileLoadException: Could not load file or assembly ‘Microsoft.WindowsAzure.Storage, Version=2.1.0.3,
Culture=neutral, PublicKeyToken=31bf3856ad364e35’ or one of its dependencies. The located assembly’s manifest definition
does not match the assembly reference. (Exception from HRESULT: 0x80131040)
File name: ‘Microsoft.WindowsAzure.Storage, Version=2.1.0.3, Culture=neutral, PublicKeyToken=31bf3856ad364e35’
(…)
—> System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types. Retrieve the
LoaderExceptions property for more information.
at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
at System.Reflection.RuntimeModule.GetTypes()
at System.Reflection.Assembly.GetTypes()
at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.GetRoleEntryPoint(Assembly entryPointAssembly)
— End of inner exception stack trace —
at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.GetRoleEntryPoint(Assembly entryPointAssembly)
at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.CreateRoleEntryPoint(RoleType roleTypeEnum)
at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.InitializeRoleInternal(RoleType roleTypeEnum)
Now my project references WindowsAzure.Storage 3.0.3.0, and there are binding redirects in place directing 1.0.0.0-3.0.3.0 to 3.0.3.0, and I can’t figure out where this reference might be coming from.
Now I’ve only been developing C# and .Net applications for about three months now (after several years as a java engineer), and these dll version, binding, and deploy issues are all very new to me, so I’m HOPING I’m missing something obvious.
Anyone have any ideas on resolving this?
I’ve had a couple of similar issues. What fixed it for me was deleting all the output files – the bin and obj folders and the Azure package folder. I had a situation where something, somewhere, was just hanging on to an old DLL.
Many thanks for sharing… Just what I was looking for!!! Any chance you could update your instructions for the latest version of MVC5… 5.1.0?
I’m wondering if anyone else has seen this issue, or could point me in the right direction to fixing it. I’m fairly certain I have things set up the way they were described in this post, though I had to do some package updating to get the AccidentalFish package working. Those updates in turn required some pre-release package updates to work. All of that left me with a warning of mismatched version dependencies which Visual Studio offered to resolve. Since none of that was described in this post, I’m worried something might be off.
After all of that things seemed to run. I can register new users, but when I try to authenticate through /token I get this error:
Method not found: ‘System.String Microsoft.AspNet.Identity.IUser.get_UserName()’.
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.MissingMethodException: Method not found: ‘System.String Microsoft.AspNet.Identity.IUser.get_UserName()’.
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:
[MissingMethodException: Method not found: ‘System.String Microsoft.AspNet.Identity.IUser.get_UserName()’.]
AccidentalFish.AspNet.Identity.Azure.d__0.MoveNext() +0
System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start(TStateMachine& stateMachine) +469
AccidentalFish.AspNet.Identity.Azure.GenericApplicationOAuthProvider`1.GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) +450
Microsoft.Owin.Security.OAuth.d__3a.MoveNext() +1275
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +144
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +84
System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +49
Microsoft.Owin.Security.OAuth.d__1e.MoveNext() +4093
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +144
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +84
Microsoft.Owin.Security.OAuth.d__0.MoveNext() +2611
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +144
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +84
System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +49
Microsoft.Owin.Security.Infrastructure.d__0.MoveNext() +1192
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +144
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +84
Microsoft.Owin.Security.Infrastructure.d__0.MoveNext() +1666
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +144
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +84
Microsoft.Owin.Security.Infrastructure.d__0.MoveNext() +1666
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +144
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +84
Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.d__5.MoveNext() +405
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +144
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +84
Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.d__2.MoveNext() +394
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() +32
Microsoft.Owin.Host.SystemWeb.Infrastructure.ErrorState.Rethrow() +62
Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.StageAsyncResult.End(IAsyncResult ar) +185
Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContext.EndFinalWork(IAsyncResult ar) +55
System.Web.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +443
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +288
I’ve not seen that myself I’m afraid.
I will be sorting out the package issues shortly.
Thanks for this great article 🙂
I wanted to know what is “my connection string ” here.
I have followed your previous post about using Facebook Authentication in Web Api 2 and after that i continue it.
And i have followed everything what you have suggested but when it comes to Connection String then i wonder what it should be :- it is database connection string or it is same as default string mentioned in Web.Config file.
Hi, hopefully my previous comment will help.
Hi james.
Thanks for this great article 🙂
but i am getting confused which string should come in place of “connecting string” in ConfigurationManager.AppSettings[“connecting string”] . I have tried my sql storage string but then i am not able to publish my web site.
Hi,
If I’m understanding your issue correctly the connection string is not a SQL connection string but an app setting you need to specify a storage account connection string. You could hard code this in place of the piece of code you highlight but this is obviously a lot less flexible and will break needing a recompile if you change keys.
hello james,
thanks for this article 🙂
I have set every thing the way you have suggested and everything is fine too except Registering with external Identity Provider( in my case checking with Facebook).When i called RegisterExternalMethod() it throws an error “an error occurred” with 500 response status code.
can you give me any clue why is this happening.
hoping a reply soon.
thanks.
tanuj.
Hi,
Are you using the NuGet package or the source code in GitHub? Try the latter has this has many fixes not yet in the NuGet package.
I am trying to use the latest AccidentalFish.AspNet.Identity.Azure package (available on NuGet as of April 12, 2014) in my ASP.NET Single Page Application projects which contains both Web API and MVC 5.1 elements. When I attempt to register a new user using the default SPA project (with the appropriate Identity changes), the creation of the new user (i.e., the call to api/Account/Register) seems to be successful (an entry appears in the appropriate entity table), but the call to the /Token endpoint is failing. Below is the full error.
Any ideas? Any chance of some detailed instructions on how to use the Azure Identity package with a current ASP.NET SPA Project?
Many thanks, Kurt
Server Error in '/' Application.
--------------------------------------------------------------------------------
Method not found: 'System.String Microsoft.AspNet.Identity.IUser.get_UserName()'.
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.MissingMethodException: Method not found: 'System.String Microsoft.AspNet.Identity.IUser.get_UserName()'.
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:
[MissingMethodException: Method not found: 'System.String Microsoft.AspNet.Identity.IUser.get_UserName()'.]
AccidentalFish.AspNet.Identity.Azure.d__0.MoveNext() +0
System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start(TStateMachine& stateMachine) +469
AccidentalFish.AspNet.Identity.Azure.GenericApplicationOAuthProvider`1.GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) +450
Microsoft.Owin.Security.OAuth.d__3a.MoveNext() +1275
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +144
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +84
System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +49
Microsoft.Owin.Security.OAuth.d__1e.MoveNext() +4093
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +144
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +84
Microsoft.Owin.Security.OAuth.d__0.MoveNext() +2611
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +144
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +84
System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +49
Microsoft.Owin.Security.Infrastructure.d__0.MoveNext() +1192
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +144
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +84
Microsoft.Owin.Security.Infrastructure.d__0.MoveNext() +1666
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +144
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +84
Microsoft.Owin.Security.Infrastructure.d__0.MoveNext() +1666
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() +32
Microsoft.Owin.Host.SystemWeb.Infrastructure.ErrorState.Rethrow() +62
Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.StageAsyncResult.End(IAsyncResult ar) +185
Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContext.EndFinalWork(IAsyncResult ar) +55
System.Web.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +434
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +288
Figured it out… The change was to leave the Provider property of the OAuthOptions as:
Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory)
and NOT change to
Provider = new GenericApplicationOAuthProvider(PublicClientId, UserManagerFactory)
as per the instructions above.
When I try an login in via Google, the authentication with Google works OK, but then the creation of the user fails because user.Logins is null, and so the new IdentityUserLogin cannot be added. If I try to set:
user.Logins = new Collection();
(in the RegisterExternal method) I get an error saying that TableUser.Logins has no setter.
Any ideas?
Are you using the NuGet package or the code in GitHub?
There were some bugs in there that I’ve fixed – one of which sounds very much like what you’re describing. Once I’ve completed my work supporting Identity Provider 2.0 I’ll release a new NuGet package with all the fixes in. In the meantime if you grab the code from GitHub it may help you out.
Give credit where credit is due, so you are not that vain:)
Get work, works like a charm. I cant seem to save an extra property added to the ApplicationUser class which inherits from the TableUser i.e.
public class ApplicationUser : TableUser
{
public async Task GenerateUserIdentityAsync(UserManager manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
public AddRealEstateEmployeeToCompany RealEstateEmployee { get; set; }
}
Then call;
var user = new ApplicationUser { RealEstateEmployee = model, UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
Any ideas?
Thanks
thank you so much for this ! So happy to get rid of EF
What about this project? https://identityazuretable.codeplex.com/
I’m not sure that even existed when I first wrote this post and code and I’ve not looked at it myself but I’d encourage anyone to take an objective look at both and use whichever one best suits your needs.