Servicestack - Authentication questions - servicestack

I am currently fighting a bit with my custom CredentialsAuthProvider implementation. First it is important to say, that I am writing a WPF client as a reference for my API.
A browser stores cookies and you can configure how to deal with them, e.g. delete when the browser is closed. On windows desktop you have Environment.SpecialFolder.Cookies where Windows stores cookies. But I could not find anything from ServiceStack. So does it not store anything on a Windows Desktop app? I saw there is a client.CookieContainer where I find three cookies after login.
Can I somehow add properties to this cookie during Authentication? If so how? Currently I use AuthenticationResponse.MetaDictionary to transfer additional information:
public override object Authenticate(IServiceBase authService, IAuthSession session, Authenticate request)
{
var authResponse = (AuthenticateResponse)base.Authenticate(authService, session, request);
authResponse.Meta = new Dictionary<string, string>();
authResponse.Meta.Add("Test", "TestValue");
return authResponse;
}
And finally: Is an instance of my derived CredentialsAuthProvider class thread safe? In TryAuthenticate(...) I make a DB connection and retrieve an object which contains all information including hashed password etc. But I can only fill this information to the session object in OnAuthenticated(....) and/or overridden Authenticate(...). If possible I do not want to make another DB call to retrieve the same object again. So is it safe to declare a member user fill it in TryAuthenticate and reuse it in other overwritten methods like so:
public class BediCredentialsAuthProvider : CredentialsAuthProvider
{
private AppUser user = null;
public override object Authenticate(IServiceBase authService, IAuthSession session, Authenticate request)
{
var authResponse = (AuthenticateResponse)base.Authenticate(authService, session, request);
authResponse.Meta = new Dictionary<string, string>();
authResponse.Meta.Add("ValueA", user.ValueA);
// ... add more properties from user object
return authResponse;
}
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
AppUser user = null;
using (var session = NhSessionFactories.OpenSession(TopinConstants.TopInDbFactory))
{
using (var transaction = session.BeginTransaction())
{
try
{
var appUserRepo = new AccountManagementRepository(session);
user = appUserRepo.GetAppUser(userName); // get user from database using NHibernate
transaction.Commit();
session.Close();
}
catch (Exception ex)
{
Log.Error($"Error retrieving user {user} to authenticate. Error: {ex}");
throw;
}
}
}
// do some logic to test passed credentials and return true or false
}
public override IHttpResult OnAuthenticated(IServiceBase authService, IAuthSession session, IAuthTokens tokens,
Dictionary<string, string> authInfo)
{
session.DisplayName = user.DisplayName;
session.FirstName = user.Firstname;
session.LastName = user.Lastname;
session.Email = user.EmailAddress;
// etc.....
return base.OnAuthenticated(authService, session, tokens, authInfo);
}
}

You can populate ServiceStack Service Client Cookies just like you would a browser except it only retains permanent Session Ids where you'll need to authenticate with RememberMe=true, e.g:
var response = client.Post(new Authenticate {
provider = "credentials",
UserName = ...,
Password = ...,
RememberMe = true,
});
Which will save the Authenticated User Session against the ss-pid permanent Cookie in the HttpWebRequest CookieContainer and gets sent on every subsequent request.
You can set your own Permanent Cookies in OnAuthenticated from authService with:
var httpRes = authService.Request.Response;
httpRes.SetPermanentCookie(cookieName, cookieValue);
Is an instance of my derived CredentialsAuthProvider class thread safe?
No the same AuthProvider singleton instance is used to Authenticate each request so you can't maintain any stored variables on the instance itself and will need to remove:
//private AppUser user = null; //Instance variables are not ThreadSafe
If you want to pass items and access them throughout the Request Pipeline you can store them in IRequest.Items Dictionary, e.g:
authService.Request.Items["AppUser"] = user;

Related

ServiceStack Custom Credentials Auth with DB Stored Api Keys

Right now, we're authenticating our users with this:
public class WindowsAuthProvider : CredentialsAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "OurDomain"))
{
// TODO make sure user record exists in custom DB tables as well
return pc.ValidateCredentials(userName, password);
}
}
public override IHttpResult OnAuthenticated(IServiceBase authService, IAuthSession session, IAuthTokens tokens, Dictionary<string, string> authInfo)
{
return base.OnAuthenticated(authService, session, tokens, authInfo);
}
}
Which works great when using the JsonServiceClient.
We have some legacy code written in Visual FoxPro which wants to call some of the authenticated functions in ServiceStack... to accommodate this, we'd like to also allow Api Keys. We want the API Keys to be stored in SQL Server to avoid issues if the process stops / restarts. So, the client would authenticate with domain credentials, then generate an API key for subsequent calls which would be stored in the database (ideally just using the table servicestack can create (dbo.ApiKey).
If we were to set this per the docs:
container.Register<IAuthRepository>(c => new OrmLiteAuthRepository(dbFactory));
We get an error on the OnAuthenticated function above telling us we should call Init()... like its trying to also create the user tables. So I'm not sure how to allow DB stored API Keys, along with custom authentication that relies on both active directory as well as our custom tables for users and roles.
Instead of inheriting from CredentialsAuthProvider, maybe its better to register a custom IUserAuthRepository and IManageRoles?
The API Key AuthProvider needs to be registered in your AuthFeature, e.g:
Plugins.Add(new AuthFeature(...,
new IAuthProvider[] {
new ApiKeyAuthProvider(AppSettings),
new WindowsAuthProvider(AppSettings),
//...
}));
Which requires a IAuthRepository like you're doing:
container.Register<IAuthRepository>(c =>
new OrmLiteAuthRepository(dbFactory));
Any AuthProvider that requires creating a back-end tables or other schema requires that its schema is initialized on Startup which you can do with:
container.Resolve<IAuthRepository>().InitSchema();
It's safe to always call InitSchema() as it only creates missing tables or is otherwise ignored for AuthRepositories that don't require creating a schema.
An issue you're running into is that you've registered an IAuthRepository and are inheriting a CredentialsAuthProvider which you don't want to use it in so you can't call CredentialsAuthProvider.OnAuthenticated() since it will save the User Auth info to the repository if it exists.
So you'll need to provide a custom implement without calling base.OnAuthenticated(), e.g:
public class WindowsAuthProvider : CredentialsAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "OurDomain"))
{
// TODO make sure user record exists in custom DB tables as well
return pc.ValidateCredentials(userName, password);
}
}
public override IHttpResult OnAuthenticated(IServiceBase authService, IAuthSession session, IAuthTokens tokens, Dictionary<string, string> authInfo)
{
try
{
session.IsAuthenticated = true;
session.OnAuthenticated(authService, session, tokens, authInfo);
AuthEvents.OnAuthenticated(authService.Request, session, authService, tokens, authInfo);
}
finally
{
this.SaveSession(authService, session, SessionExpiry);
}
return null;
}
}

Sending custom parameter on authentication

I'm a servicestack newbie. I'm trying to figure out how to send custom parameter on authentication.
As far as I understood, that's the step to authenticate a client and than execute a set of call within a session
var jsonClient = new JsonServiceClient("http://localhost:55679/");
var authResponse = client.Send(new Authenticate
{
provider = "myProvider",
UserName = "user",
Password = "pwd",
RememberMe = true,
});
var jResponse = jsonClient.Get<CountriesResponse>(request);
Console.WriteLine(jResponse.Countries.Count);
So far so good, I configurated my apphost as following and everything works as expected.
Plugins.Add(new AuthFeature(() => new CustomUserSession(), new IAuthProvider[] {
new MyAuthProvider(),
}));
What should I do if, instead of sending ServiceStack.Authenticate, I'd like to send my MyAuthenticate
request that has same custom properties, somenthing like this?
var authResponse = client.Send(new MyAuthenticate
{
provider = "myProvider",
UserName = "user",
Password = "pwd",
RememberMe = true,
AppId = "AppId",
ProjectId = "ProjectId"
});
My goal is to send custom parameter while I'm authenticating the user, not just those allowed by Authenticate built-in request, and than store those extra parameter within my CustomUserSession.
Thanks
Sending additional info on QueryString or HttpHeaders
As you can't change the built-in Authenticate Request DTO, one way to send additional metadata is to add extra info on the QueryString or HTTP Headers.
If you wanted to use the .NET Service Clients to do this you would need to use the RequestFilter, e.g:
var client = new JsonServiceClient(BaseUrl) {
RequestFilter = req => {
req.QueryString["AppId"] = appId;
req.QueryString["ProjectId"] = appId;
}
};
var authResponse = client.Send(new Authenticate { ... });
Otherwise creating custom Request is often more flexible using ServiceStack's built-in HTTP Utils, e.g:
var url = "{0}/auth/myProvider".Fmt(BaseUrl)
.AddQueryParam("AppId", appId)
.AddQueryParam("ProjectId", projectId);
var authResponse = url.PostJsonToUrl(new Authenticate { ... });
On the server the additional data will be available in the QueryString of the current request which you can get from IServiceBase or IRequest args, e.g:
public class CustomCredentialsAuthProvider : CredentialsAuthProvider
{
...
public override IHttpResult OnAuthenticated(IServiceBase authService,
IAuthSession session, IAuthTokens tokens, Dictionary<string, string> authInfo)
{
...
var customSession = (CustomUserSession)session;
customSession.AppId = authService.Request.QueryString["AppId"];
customSession.ProjectId = authService.Request.QueryString["ProjectId"];
return base.OnAuthenticated(authService, session, tokens, authInfo);
}
}
Custom Meta dictionary now available on Authenticate Request DTO
To make this use-case a little easier a new Dictionary<string,string> Meta property was added on the Authenticate DTO which makes calling from the Typed Service Clients a little nicer since you don't have to use a filter, e.g:
var client = new JsonServiceClient(BaseUrl);
var authResponse = client.Send(new Authenticate {
...
Meta = new Dictionary<string, string> { {"AppId", appId}, {"ProjectId", pId} },
});
Which you can access from the Authenticate DTO directly, e.g:
var authRequest = (Authenticate)authService.Request.Dto;
customSession.AppId = authRequest.Meta["AppId"];
customSession.ProjectId = authRequest.Meta["ProjectId"];
The new Meta property is available from v4.0.35+ that's currently available on MyGet.
Use your own Custom Authentication Service
A more disruptive alternative approach to be able to use your own MyAuthenticate DTO is to handle the authentication request in your own Service and then delegate to the AuthService, e.g:
public class MyAuthenticate : Authenticate
{
public string AppId { get; set; }
public string ProjectId { get; set; }
}
public class MyAuthServices : Service
{
public object Any(MyAuthenticate request)
{
using (var auth = base.ResolveService<AuthenticateService>())
{
var response = auth.Post(request);
var authResponse = response as AuthenticateResponse;
if (authResponse != null) {
var session = base.SessionAs<CustomUserSession>();
session.AppId = request.AppId;
session.ProjectId = request.ProjectId;
this.SaveSession(session);
}
return response;
}
}
}

Return a custom auth response object from ServiceStack authentication

Is it possible to return a custom auth response? I already have my own custom authentication provider that inherits from CredentialsAuthProvider.
I want to return the session expiry date in the response, so that the client knows exactly when their server session will expire:
{
"sessionId": "bG27SdxbRkqJqU6xv/gvBw==",
"userName": "joe.bloggs#letmein.com",
"sessionExpires": "2013-04-29T03:27:14.0000000",
"responseStatus": {}
}
I can override the Authenticate method like so:
public override object Authenticate(IServiceBase authService, IAuthSession session, Auth request)
{
// get base response
var response = base.Authenticate(authService, session, request);
// grab the session
var customSession = authService.GetSession() as CustomUserSession;
// if response can be cast and customSession exists
if (response is AuthResponse && customSession != null)
{
// cast
var authResponse = response as AuthResponse;
// build custom response
var customAuthResponse = new CustomAuthResponse
{
ReferrerUrl = authResponse.ReferrerUrl,
SessionExpiry = customSession.SessionExpires,
SessionId = authResponse.SessionId,
ResponseStatus = authResponse.ResponseStatus,
UserName = authResponse.UserName
};
return customAuthResponse;
}
// return the standard response
return response;
}
This works fine, except in the case where the session already is active. In that case, the AuthService Post method checks for a valid session and automatically returns a standard AuthResponse, and there is no obvious way to override it:
var alreadyAuthenticated = response == null;
response = response ?? new AuthResponse {
UserName = session.UserAuthName,
SessionId = session.Id,
ReferrerUrl = referrerUrl,
};
Following Paaschpa's ideas below, the following forces re-auth to always be re-authenticated, but it seems like there could be risks involved in leaving multiple active sessions open:
public override bool IsAuthorized(IAuthSession session, IOAuthTokens tokens, Auth request = null)
{
// force re-authentication. Not great, but no other obvious way to do this
if (request != null)
{
return false; // auth or re-auth calls
}
return base.IsAuthorized(session, tokens, request);
}
Can anyone think of a better way to do this? I could implement my own AuthenticationService, but I'm not sure how I would override the AuthFeature?
If I understand correctly, you want to return a custom response after a user authenticates against '/auth/credentials'. Since you already have your own CredentialsAuthProvider I think you could just override Authenticate and return your own response.
Subclass of CredentialsAuthProvider
public class MyCredentialsAuthProvider : CredentialsAuthProvider
{
public override object Authenticate(ServiceStack.ServiceInterface.IServiceBase authService, IAuthSession session, Auth request)
{
//let normal authentication happen
var authResponse = (AuthResponse)base.Authenticate(authService, session, request);
//return your own class, but take neccessary data from AuthResponse
return new
{
UserName = authResponse.UserName,
SessionId = authResponse.SessionId,
ReferrerUrl = authResponse.ReferrerUrl,
SessionExpires = DateTime.Now
};
}
}

ServiceStack - Custom CredentialsAuthProvider within .Net MVC app

I am attempting to authenticate against MVC and ServiceStack following the example here - https://github.com/ServiceStack/ServiceStack.UseCases/tree/master/CustomAuthenticationMvc.
My issue is that I am unable to authenticate successfully against ServiceStack on my initial request to Account/LogOn.
ServiceStack related code in LogOn method of AccountController:
var apiAuthService = AppHostBase.Resolve<AuthService>();
apiAuthService.RequestContext = System.Web.HttpContext.Current.ToRequestContext();
var apiResponse = apiAuthService.Authenticate(new Auth
{
UserName = model.UserName,
Password = model.Password,
RememberMe = false
});
I have a custom Authentication Provider that subclasses CredentialsAuthProvider. I Configure as follows in the AppHost class:
var appSettings = new AppSettings();
Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[] {
new ActiveDirectoryAuthProvider(),
}));
public override bool TryAuthenticate(ServiceStack.ServiceInterface.IServiceBase authService, string userName, string password)
{
//class to authenticate against ActiveDirectory
var adAuthentication = new ActiveDirectoryAuthenticationService();
if (!adAuthentication.Authenticate(userName, password))
return false;
var session = (CustomUserSession)authService.GetSession(false);
session.IsAuthenticated = true;
session.UserAuthId = session.UserAuthName;
authService.SaveSession(session, SessionExpiry);
return true;
}
I think my issue is that session.Id is null at this point and saving the session persists 'urn:iauthsession:' to the 'SessionCache'. However, I'm not sure how to correctly populate session.Id. Also, this may or may not be an issue, but the initial LogOn request is to Account/Logon which is handled by MVC. So, there is no request to ServiceStack prior to the AuthService.Authenticate() call in the AccountController.
A possible solution I came up with has been added below in my subclass of CredentialsAuthProvider.
public override bool TryAuthenticate(ServiceStack.ServiceInterface.IServiceBase authService, string userName, string password)
{
//class to authenticate against ActiveDirectory
var adAuthentication = new ActiveDirectoryAuthenticationService();
if (!adAuthentication.Authenticate(userName, password))
return false;
var session = (CustomUserSession)authService.GetSession(false);
//A possible solution???
if(session.Id == null)
{
var req = authService.RequestContext.Get<IHttpRequest>();
var sessId = HttpContext.Current.Response.ToResponse().CreateSessionIds(req);
session.Id = sessId;
req.SetItem(SessionFeature.SessionId, sessId);
}
//end possible solution
session.IsAuthenticated = true;
session.UserAuthId = session.UserAuthName;
authService.SaveSession(session, SessionExpiry);
return true;
}
Is there a configuration or call I'm missing to 'wire up' ServiceStack Authentication within MVC?
Thanks.
The only thing I am doing in my TryAuthenticate is validating the user name, password and returning true if valid.
I have another override method called OnAuthenticated where I am saving the session information. OnAuthenticated passes the Auth Service and the Session as parameter so you only have to:
public override void OnAuthenticated(IServiceBase authService, IAuthSession session,.....
{
session.IsAuthenticated = true;
session.....
authService.SaveSession(session, SessionExpiry);
}
This seems to store my session information as long as I registered the ICacheClient.

Populating IAuthSession with data from the database

So I've created a custom CredentialsAuthProvider using ServiceStack as per the examples located here:
https://github.com/ServiceStack/ServiceStack/wiki/Authentication-and-authorization
I have the authentication side of things working but I'm not sure how I populate the session with data from the database in the OnAuthenticated method. In the example they show the following:
//Fill the IAuthSession with data which you want to retrieve in the app eg:
session.FirstName = "some_firstname_from_db";
In the TryAuthenticate method I have the username/password, which I can use to authenticate the user against the database, but once it goes to the OnAuthenticated method, how/what do I use to access/retrieve the user information from the database?
I know this is an older thread but it may still be relevant because unfortunately not much has improved since Sep of 2012 in terms of availability of ServiceStack documentation, clarity of examples or even comments in the code. (#mythz: It would be very helpful if you guys could add meaningful summaries to all your classes and methods.)
I struggled with the same dilemma until I looked at the actual code of CredentialsAuthProvider (which in general is pretty much the only way to understand how things work in ServiceStack). The OnAuthenticated is called right after TryAuthenticate inside the Authenticate method, so I figured it's not necessary to make all your DB calls in OnAuthenticated as #mythz suggests in his examples. Instead I placed the code that populates the IAuthSession object right into my implementation of TryAuthenticate, like so:
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
try
{
// Use my own repo to authenticate the user.
var userRepo = authService.TryResolve<IUserRepository>();
var user = userRepo.Authenticate(userName, password);
// Populate session properties with data from my user POCO.
var session = authService.GetSession();
session.Id = user.CurrentSession.ID.ToString();
session.IsAuthenticated = true;
session.CreatedAt = DateTime.UtcNow;
session.DisplayName = session.FirstName = session.LastName = user.FullName;
session.UserAuthName = session.UserName = user.Username;
session.UserAuthId = user.ID.ToString();
}
catch (Exception ex)
{
// Log the exception, etc....
return false;
}
return true;
}
However, you still have to override OnAuthenticated in order to save the cookie in HTTP response (which I assume is required for subsequent requests from the same browser to be authenticated) because the base implementation only sets the cookie if it finds IUserAuthRepository in the IOC container, which in my case won't happen because I use my own repository. So my implementation now looks like this:
public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo)
{
try
{
// Save the browser cookie.
var httpRes = authService.RequestContext.Get<IHttpResponse>();
if (httpRes != null)
{
httpRes.Cookies.AddPermanentCookie(HttpHeaders.XUserAuthId, session.UserAuthId);
}
// Save the user session object (ServiceStack stores it in the in-memory cache).
authService.SaveSession(session, SessionExpiry);
}
catch (Exception ex)
{
// Log the exception, etc....
}
}
#mythz: Please let me know if the above makes sense or not.
Another good example of a ServiceStack's CustomUserSession is in the SocialBootstrapApi project. Rather than pulling information out of the data, it extracts the information out of the UserSession and populates its own Custom User Table using the registered DB Factory resolved from the AppHost IOC:
authService.TryResolve<IDbConnectionFactory>().Run(db => db.Save(user));
Rather than using it to extract and save data from the user's session, you can also use any of your registered dependencies to fetch data and populate the session with:
public override void OnAuthenticated(
IServiceBase authService,
IAuthSession session,
IOAuthTokens tokens,
Dictionary<string, string> authInfo)
{
using (var db = authService.TryResolve<IDbConnectionFactory>().OpenDbConnection())
{
var user = db.Id<MyUser>(session.UserId);
session.FirstName = user.FirstName;
}
}

Resources