Comments (13)
@clone1985 thanks for pointing out this issue. I pushed a new version of the library: https://www.nuget.org/packages/AspNet.Identity.RavenDB/2.0.0-pre-06 can you try with the new version and see if the issue continues. It should be solved.
The reason why I don't prefer to implement FindAsync as you did is to avoid unnecessary index creation. In my implementation, I'm doing two queries but all are performed on the document Id.
from aspnet.identity.ravendb.
Thanks, that did the trick.
from aspnet.identity.ravendb.
Nice! Thanks for confirming!
from aspnet.identity.ravendb.
It appears I was too quick to close the issue. It suddenly stopped working again without me having done anything to provoke it, so I think I was still running with my modified version cached for a while.
from aspnet.identity.ravendb.
Can you provide the error message that you get or repro steps so that I can reproduce the exact same problem?
from aspnet.identity.ravendb.
The RavenDB log says:
Document with key 'RavenUserLogins/Google/https://www.google.com/accounts/o8/id?id=MYKEY' was not found
This makes sense to me (and my limited understanding of RavenDB) since there is no document with an id like that (see the original bug description). Note that the id it searches for matches the one in the Logins collection ('Id') perfectly, so that is not the problem. So my original conclusion still stands that the way you look for the login does not work as intended.
I created a repro project that is the default VS 2013 SPA template where I just replaced the persistence to use the newest AspNet.Identity.RavenDB package. You just need to enter a valid connection string in the web.config and try to register a Google account. After registering it you should see the same log in the RavenDB Studio that the document could not be found.
https://onedrive.live.com/redir?resid=48F0E076F4DD5267!19633&authkey=!ALSCar9Ktmtu1NI&ithint=file%2c.zip
from aspnet.identity.ravendb.
@clone1985 thanks, looking into it now.
from aspnet.identity.ravendb.
@clone1985 I figured out the problem.
// POST api/Account/AddExternalLogin
[Route("AddExternalLogin")]
public async Task<IHttpActionResult> AddExternalLogin(AddExternalLoginBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
AuthenticationTicket ticket = AccessTokenFormat.Unprotect(model.ExternalAccessToken);
if (ticket == null || ticket.Identity == null || (ticket.Properties != null
&& ticket.Properties.ExpiresUtc.HasValue
&& ticket.Properties.ExpiresUtc.Value < DateTimeOffset.UtcNow))
{
return BadRequest("External login failure.");
}
ExternalLoginData externalData = ExternalLoginData.FromIdentity(ticket.Identity);
if (externalData == null)
{
return BadRequest("The external login is already associated with an account.");
}
IdentityResult result = await UserManager.AddLoginAsync(User.Identity.GetUserId(),
new UserLoginInfo(externalData.LoginProvider, externalData.ProviderKey));
IHttpActionResult errorResult = GetErrorResult(result);
if (errorResult != null)
{
return errorResult;
}
return Ok();
}
Inside this method, UserManager.AddLoginAsync
is called and UserManager.UpdateAsync
is not called. RavenUserStore
does not save changes on every call. It only does it with UpdateAsync method.
On the other hand, I would suggest to work with the UoW pattern as Ayende described here: http://ayende.com/blog/163170/building-async-unit-of-work-with-mvc-4 By going with this option, you shouldn't make any configuration changes to the AccountController class which comes with the project templates.
from aspnet.identity.ravendb.
I'm going to write a blog post about how to make RavenDB port of ASP.NET identity work with the default templates soon.
from aspnet.identity.ravendb.
I'm afraid that is not the problem I have, since I do not use the AddExternalLogin method at all (though it is nice to know since I will likely be using it in the future). If there should be a problem with my code, it would likely be in RegisterExternal or GetExternalLogin.
from aspnet.identity.ravendb.
I see your problem now:
// POST api/Account/RegisterExternal
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("RegisterExternal")]
public async Task<IHttpActionResult> RegisterExternal(RegisterExternalBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
if (externalLogin == null)
{
return InternalServerError();
}
RavenUser user = new RavenUser(model.UserName);
user.AddLogin(new RavenUserLogin(externalLogin.UserName, new UserLoginInfo(externalLogin.LoginProvider, externalLogin.ProviderKey)));
IdentityResult result = await UserManager.CreateAsync(user);
IHttpActionResult errorResult = GetErrorResult(result);
if (errorResult != null)
{
return errorResult;
}
return Ok();
}
Why are you calling user.AddLogin there? You should call UserManager.AddLoginAsync
instead right after you call UserManager.CreateUserAync
(just like I did here: 2dc4108).
Here is the correctly implemented version of that method:
// POST api/Account/RegisterExternal
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("RegisterExternal")]
public async Task<IHttpActionResult> RegisterExternal(RegisterExternalBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
if (externalLogin == null)
{
return InternalServerError();
}
RavenUser user = new RavenUser(model.UserName);
IdentityResult result = await UserManager.CreateAsync(user);
if (result.Succeeded)
{
result = await UserManager.AddLoginAsync(user.Id, new UserLoginInfo(externalLogin.LoginProvider, externalLogin.ProviderKey));
if (result.Succeeded)
{
return Ok();
}
}
return GetErrorResult(result);
}
from aspnet.identity.ravendb.
Yes, that works. The reason I did it that way is due to the original code which was defined like this:
...
IdentityUser user = new IdentityUser
{
UserName = model.UserName
};
user.Logins.Add(new IdentityUserLogin
{
LoginProvider = externalLogin.LoginProvider,
ProviderKey = externalLogin.ProviderKey
});
IdentityResult result = await UserManager.CreateAsync(user);
...
It only seemed natural to continue the same pattern. If you write up a blog post about it make sure to point this out. While it turned out that there was no bug after all, I think it served to highlight that a bit of guidance would be helpful.
Thanks for looking into my problems :)
from aspnet.identity.ravendb.
Thanks for that code snippet.
That's the wrong way of doing it. I filed an issue on ASP.NET Identity repository: https://aspnetidentity.codeplex.com/workitem/2165 You can vote it up.
from aspnet.identity.ravendb.
Related Issues (20)
- Enfore IAsyncDocumentSession to have UseOptimisticConcurrency set to true
- Add JsonConstructor attribute to RavenUserClaim constructor HOT 2
- IncrementAccessFailedCountAsync HOT 2
- Put UoW need inside the readme.md file
- Make AutoSaveChanges configurable at the RavenUserStore
- Question, use OWIN? HOT 7
- Cannot ConfirmEmail with new 2.0 HOT 2
- Identity store should support IUserRoleStore HOT 4
- Users stored as RavenUsers rather than ApplicationUsers
- Concurrency Issues HOT 5
- Cannot query users HOT 5
- Extending the UserStore?
- CreateAsync duplicate username
- UpdateAsync doesn't update the user in the database
- Optimistic Concurrency AutoFAC HOT 2
- Upgrade to RavenDB3 and PRE6 Issue HOT 1
- UserManger.FindID throws an cast exception HOT 1
- Username disallows using "-" HOT 1
- User data not being refreshed from database HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from aspnet.identity.ravendb.