Introduction
Recently, i started developing my first ServiceStack.net web service. As part of it, i found a need to add authentication to the service. Since my web service is connecting to a legacy application with its own custom user accounts, authentication, and authorization (roles), i decided to use the ServiceStack Auth model, and implement a custom IAuthProvider.
Oh yeah, the target audience for this post:
- C# / .NET / Mono web developer who is getting started learning how to build a RESTful web api using ServiceStack.net framework
- Wants to add the web API to an existing application with its own proprietary authentication/authorization logic
I tried to dive in and implement in my app, but i got something wrong with the routing to the /auth/{provider} , so i decided to take a step back and do the simplest thing possible, just so i understood the whole process.That’s what i’m going to do today.
I’m using Visual Studio 2012 Professional, but you could also use VS 2010, probably VS 2012 Express as well (or MonoDevelop, that’s another story i haven’t tried).
The simplest thing possible in my mind:
- Create the HelloWorld web service
- Follow the recommendation for adding Authorization and authentication, starting with a simple example
- Modify the working configuration to use a custom authorization and authentication logic
This is not an example of TDD-style development — more of a technology exploration.
OK, let’s get started.
Creating HelloWorld
I’m not going to repeat what’s already in the standard ServiceStack.net docs, but the summary is:
- create an “ASP.NET Empty Web Application” (calling mine SSHelloWorldAuth)
- pull in ServiceStack assemblies via NuGet (not my usual practice, but its easy). In fact, i’m using the “Starter ASP.NET Website Template – ServiceStack”. That will install all the assemblies and create references, and also update Global.asa
Create the Hello , HelloRequest, HelloResponse, and HelloService classes, just like the sample.Scratch that – it is already defined in the template at App_Start/WebServiceExamples.cs
- Run the app locally. You will see the “ToDo” app loaded and working in the default.htm. Also, you can test the Hello function at http://localhost:65227/hello (your port number may vary)
Adding a built-in authentication provider
OK that was the easy part. Now we’re going to add the [Authenticate] attribute to the HelloService class.
[Authenticate] public class HelloService : Service { ...
This will prevent the service from executing unless the session is authenticated already. In this case, it will fail, since nothing is set up.
Enabling Authentication
Now looking in App_Start/AppHost.cs , i found an interesting section:
/* Uncomment to enable ServiceStack Authentication and CustomUserSession private void ConfigureAuth(Funq.Container container) { var appSettings = new AppSettings(); //Default route: /auth/{provider} Plugins.Add(new AuthFeature(this, () => new CustomUserSession(), new IAuthProvider[] { new CredentialsAuthProvider(appSettings), new FacebookAuthProvider(appSettings), new TwitterAuthProvider(appSettings), new BasicAuthProvider(appSettings), })); //Default route: /register Plugins.Add(new RegistrationFeature()); //Requires ConnectionString configured in Web.Config var connectionString = ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString; container.Register<IDbConnectionFactory>(c => new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider)); container.Register<IUserAuthRepository>(c => new OrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>())); var authRepo = (OrmLiteAuthRepository)container.Resolve<IUserAuthRepository>(); authRepo.CreateMissingTables(); } */
Let’s use it. But i want to just enable CredentialsAuthProvider, since that is a forms-based username/password authentication, (the closest to what i want to do customized).
A few notes on the code block above:
The “Plugins.Add(new AuthFeature(() ” stuff was documented.
“Plugins.Add(new RegistrationFeature());” was new to me, but now i see it is to add the /register route and behavior
For this test, i will go along with using the OrmLite for the authentication tables. In order to do that,
- i’m using a new connection string “SSHelloWorldAuth”,
- adding it to Web.config: <connectionStrings><add name=”SSHelloWorldAuth” connectionString=”Data Source=.\SQLEXPRESS;Initial Catalog=SSHelloWorldAuth;Integrated Security=SSPI;” providerName=”System.Data.SqlClient” /></connectionStrings>
- creating a new SQLEXPRESS database locally, called: SSHelloWorldAuth
Finally, we’ll have to add/enable the line to ConfigureAuth(container) , which will initialize the authentication system.
Now we’ll try running the app again: F5 and go to http://localhost:65227/hello in the browser again. I get a new problem:
In a way, it’s good, because the [Authenticate] attribute on the HelloService class worked – the resource was found, but sent a redirect to /login . However, no handler is set up for /login.
Separately, i checked if the OrmLite db got initialized with authRepo.CreateMissingTables(); , and it seems it did (2 tables created).
Understanding /login , /auth/{provider}
This is where i got hung up on my initial try to get it working, so i’m especially determined to get this working.
The only example of a /login implementation i found in the ServiceStack source code tests. It seems like /login would be for a user to enter in a form. It seems if you are a script (javascript or web api client), you would authenticate at the /auth/{provider} URI.
That’s when i thought – is the /auth/* service set up properly? Let’s try going to http://localhost:65227/auth/credentials
So the good news is that is is set up. Why don’t we try to authenticate against /auth/credentials ?
Well, first i should create a valid username/password combination. I can’t just insert into the db, since the password must be one-way hashed. So i’m going to use the provider itself to do that.
I copied a CreateUser() function in the ServiceStack unit tests, and will run in my app’s startup. I modified slightly to pass in the OrmLiteAuthRepository, and call it right after initializing the authRepo.
CreateUser(authRepo, 1, "testuser", null, "Test2#4%");
Run the app with F5 again, and then check the database: select * from userauth — we now have one row with username and hashed password. Suitable for testing. (don’t forget to disable CreateUser() now).
Authenticating with GET
I would never do this on my “real” application. At minimum, i would only expose a POST method. But instead of writing some javascript, i’m going to try the web browser to submit credentials and try to authenticate.
First, i’m going to try and use a wrong password:
http://localhost:65227/auth/credentials?UserName=testuser&Password=wrong
… i get the same “Invalid UserName or Password” error, which is good.
Now i’ll try the correct username/password (url-encoding left as an exercise for the reader):
http://localhost:65227/auth/credentials?UserName=testuser&Password=Test2%234%25
Success! This means my user id has a validated ServiceStack session on the server, and is associated with my web browser’s ss-id cookie.
I can now go to the /hello service on the same browser session, and it should work:
Awesome. So we’ve figured out the /auth/credentials before the /hello service. Just for kicks, i stopped running the app in Visual Studio and terminated my local IIS Express web server instance, in order to try a new session. When i ran the project again and went to /hello , it failed as expected (which we want). Only by authenticating first, do we access the resource.
IAuthProvider vs IUserAuthRepository
Note that i started this saying i wanted to implement my own IAuthProvider. However, ServiceStack also separately abstracts the IUserAuthRepository, which seems to be independently pluggable. Think of it this way:
- IAuthProvider is the authentication service code backing the HTTP REST API for authentication
- IUserAuthRepository is the provider’s .NET interface for accessing the underlying user/role data store (all operations)
Since my initial goal was to use username/password login with my own custom/legacy authentication rules, it seems more appropriate to use subclass CredentialsAuthProvider (creating my own AcmeCredentialsAuthProvider).
I do not expect to have to create my own IUserAuthRepository at this time– but it would be useful if i had to expose my custom datastore to be used by any IAuthProvider. If you are only supporting one provider, you can put the custom code into the provider’s TryAuthenticate() and OnAuthenticated() methods. With a legacy system, you probably already have tools to manage user accounts and roles, so you’re not likely to need to re-implement all the IUserAuthRepository methods. However, if you need to implement Roles, a custom implementation of IUserAuthRepository may be in order (to be revisited).
This is going to be almost directly from the Authentication and Authorization wiki docs.
- Create a new class, AcmeCredentialsAuthProvider.cs
- subclass CredentialsAuthProvider
- override TryAuthenticate(), adding in your own custom code to authenticate username/password
- override OnAuthenticated(), adding any additional data for the user to the session for use by the application
public class AcmeCredentialsAuthProvider : CredentialsAuthProvider { public override bool TryAuthenticate(IServiceBase authService, string userName, string password) { //Add here your custom auth logic (database calls etc) //Return true if credentials are valid, otherwise false if (userName == "testuser" && password == "Test2#4%") { return true; } else { return false; } } public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo) { //Fill the IAuthSession with data which you want to retrieve in the app eg: session.FirstName = "some_firstname_from_db"; //... //Important: You need to save the session! authService.SaveSession(session, SessionExpiry); } }
As you can see, i did it in a trivially stupid way, but any custom logic of your own will do.
Finally, we change AppHost.cs ConfigureAuth() to load our provider instead of the default.
Plugins.Add(new AuthFeature(() => new CustomUserSession(), new IAuthProvider[] { new AcmeCredentialsAuthProvider(appSettings), }));
Run the app again, you should get the same results as before passing the correct or invalid username/password. Except in this case, you can set a breakpoint and verify your AcmeCredentialsAuthProvider code is running.
So at the end of this i’m happy:
- I established how to create a ServiceStack service with a working custom username/password authentication
- I learned some things from the ServiceStack Nuget template which was in addition to the docs
- I understand better where it is sufficient to only override CredentialsAuthProvider for IAuthProvider , and where it may be necessary to implement a custom IUserAuthRepository (probably to implement custom Roles and/or Permissions)
Thanks for your interest. If you are interested in the code/project file created with this post, i’ve pushed it to GitHub.
Hi Raul,
This post is fantastic! it looks like it will be a great help for others going down a similar path, thanks a lot for sharing 🙂
FYI I’ve added it at the end of the Authentication wiki:
https://github.com/ServiceStack/ServiceStack/wiki/Authentication-and-authorization#wiki-community
Thanks for the kind words, Demis! Also, thanks for the rockin’ web service framework! Just doing what i can to help grow the ServiceStack community and help others in the same boat.
-Raul
I’m following your excellent post but keep getting an error that the ServiceAuth is not initialized. You have a line that reads Finally, we’ll have to add/enable the line to ConfigureAuth(container) , which will initialize the authentication system. Can you tell me what the code for that line is please?
Thanks,
Jeff
Jeff,
Sure, that is in the AppHost Configure() section. It is here in the sample code i pushed to github:
https://github.com/nohea/Enehana.CodeSamples/blob/master/SSHelloWorldAuth/SSHelloWorldAuth/App_Start/AppHost.cs#L45
-Raul
This article was really, really helpful to me. This is almost the exact same scenario that I have to deal with and I wasn’t sure where to start. Thanks for taking the time to share this!
This was a huge help for me thanks for sharing.
First, Iet me say a big THANK YOU. This is by far the best tutorial on this subject. Before reading your post I was really doing things the wrong way.
I just would like to point that there is a constructor missing on the AcmeCredentialsAuthProvider (or maybe its my bad). However in case someone happens to have this issue, just add this constructor.
public AcmeCredentialsAuthProvider(AppSettings appSettings)
: base(appSettings)
{
}
Hi Raul,
Very helpful post. I followed it with the version on servicestack Authentication and Authorization doc,
but i am getting an error on servicestack sending a response back to my restconsole. below is my question at stackoverflow:
http://stackoverflow.com/questions/20177208/servicestack-response-after-a-successful-authentication-throws-error-what-is-mi
Pls help. Thanks.
Given the positive comments I am sorry to say that as none of the pictures load the articles is now rather difficult to follow. Any chance they get back ?
Martin, sorry – thanks for the “heads-up”. The images are back now. It seems when i upgraded wordpress yesterday, i changed some symlinks and broke images for the site. Fixed now. -Raul
Hi,thanks for the great article.I would like to ask but it’s out the authorization topic.Did you check out the user registration method?Are there any chance to entend it too? :d Thanks a lot.
I’m considering writing more on ServiceStack Authorization (provider, repository, and authservice), but i have not used the built-in user registration capability – sorry!
I need to Authenticate User with Post only. I need to restrict Get.
Is there any built in Attributes. or I need do this manually. Like this.
if (HttpContext.Current.Request.HttpMethod.ToLower() != “post”)
{
return “Invalid Post request”;
}
I tried these attributes, But it didn’t worked.
[RequiredPermission(ApplyTo.Put | ApplyTo.Post, “CanAdd”)]
[RequiredPermission(ApplyTo.Post)]
Can you please assist.
Kind Regards,
Davinder
By the way, all these endpoints are visible through the metadata page, I am a bit surprised that you needed so many experiments to find out the right request models, they are automatically described on the metadata page as all other endpoints…
Thank you for you comment. I wrote this post years ago as someone new to ServiceStack, and shared my steps on how to explore the new framework from a beginners perspective, especially someone not used to coding RESTful web services and HTTP. The point was not to get the job done fastest, but to look in detail “under the hood”.