I'm trying to use ServiceStack authentication plugins out of the box along with RavenDB and the RavenUserAuthRepository package.
AppHost
var store = new DocumentStore()
{
ConnectionStringName = "ServiceStackAuthSample"
}
.Initialize();
IndexCreation.CreateIndexes(typeof(RavenUserAuthRepository).Assembly, store);
container.Register(store);
var session = container.Resolve<IDocumentStore>().OpenSession();
container.Register(p => session).ReusedWithin(ReuseScope.Request);
container.Register<IUserAuthRepository>(p => new RavenUserAuthRepository(p.Resolve<IDocumentStore>(), p.Resolve<IDocumentSession>()));
The first authentication attempt via Facebook, GoogleOAuth, Twitter--works as expected. However, if I attempt to re-authenticate, RavenDB doesn't seem to like it and I get the following:
error CodeNonUniqueObjectExceptionmessageAttempted to associate a different object with id 'UserAuths/1'.stack Trace[Auth: 10/21/2013 6:51:04 PM]: [REQUEST: {provider:facebook}] Raven.Client.Exceptions.NonUniqueObjectException: Attempted to associate a different object with id 'UserAuths/1'. at Raven.Client.Document.InMemoryDocumentSessionOperations.AssertNoNonUniqueInstance(Object entity, String id) in c:\Builds\RavenDB-Stable\Raven.Client.Lightweight\Document\InMemoryDocumentSessionOperations.cs:line 778 at Raven.Client.Document.InMemoryDocumentSessionOperations.StoreInternal(Object entity, Etag etag, String id, Boolean forceConcurrencyCheck) in c:\Builds\RavenDB-Stable\Raven.Client.Lightweight\Document\InMemoryDocumentSessionOperations.cs:line 670 at Raven.Client.Document.InMemoryDocumentSessionOperations.Store(Object entity) in c:\Builds\RavenDB-Stable\Raven.Client.Lightweight\Document\InMemoryDocumentSessionOperations.cs:line 608 at ServiceStack.Authentication.RavenDb.RavenUserAuthRepository.CreateOrMergeAuthSession(IAuthSession authSession, IOAuthTokens tokens) at ServiceStack.ServiceInterface.Auth.AuthProvider.OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary
2 authInfo) at ServiceStack.ServiceInterface.Auth.FacebookAuthProvider.Authenticate(IServiceBase authService, IAuthSession session, Auth request) at ServiceStack.ServiceInterface.Auth.AuthService.Authenticate(Auth request, String provider, IAuthSession session, IAuthProvider oAuthConfig) at ServiceStack.ServiceInterface.Auth.AuthService.Post(Auth request) at lambda_method(Closure , Object , Object ) at ServiceStack.ServiceHost.ServiceRunner
1.Execute(IRequestContext requestContext, Object instance, TRequest request)
RavenDB searches tell me it may have something to do with my session(s), but I'm not sure what I'm doing wrong here. It could be my understanding of how to manage RavenDB sessions in ServiceStack.
UPDATE
It turns out there was a bug in the v3 branch of ServiceStack.AuthenticationRavenDb.RavenUserAuthRepository::GetUserAuth method. While all other methods were using a private IDocumentSession member, injected by constructor, GetUserAuth was creating a brand new instance of IDocumentSession.
private readonly IDocumentStore _documentStore;
public RavenUserAuthRepository(IDocumentStore documentStore,IDocumentSession session)
{
_documentStore = documentStore;
_session = session;
}
Old
public UserAuth GetUserAuth(string userAuthId)
{
using (var session = documentStore.OpenSession())
{
int intAuthId;
return int.TryParse(userAuthId, out intAuthId)
? session.Load<UserAuth>(intAuthId)
: session.Load<UserAuth>(userAuthId);
}
}
Fixed
public UserAuth GetUserAuth(string userAuthId)
{
using (_session)
{
int intAuthId;
return int.TryParse(userAuthId, out intAuthId)
? _session.Load<UserAuth>(intAuthId)
: _session.Load<UserAuth>(userAuthId);
}
}
This lead to CreateOrMergeAuthSession getting the UserAuth with one session instance (via GetUserAuth), then using the private session member to save the UserAuth, causing the conflict.
Thanks for the tip, David!