1
votes

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, Dictionary2 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.ServiceRunner1.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!

1

1 Answers

1
votes

I'm not familiar with the ServiceStack DI API you've shown here, but it looks to me like you're creating and registering one DocumentStore (which is good) but then also creating one IDocumentSession and registering just that instance. There should be one unique instance of IDocumentSession for every request, so the container needs to be configured that every request will call the DocumentStore's OpenSession method.