Simple Web Service Authentication with ServiceStack

Steve Ellwood
4 min readJul 27, 2021

--

I use ServiceStack as the framework for all my web services. It’s a fantastic tool that’s much simpler to get to grips with than some alternatives. It’s also very powerful and I sometimes find that the sheer power confuses me when I am trying to set something up for the first time and when I have worked it out I realise I was overthinking it. One example of this is the Authentication mechanism. The sheer power and variety of options covered is pretty large. In my case I knew I wanted to setup API Key authentication. This is how I got it working for me.

I am in the process of making significant updates to a legacy application that exposes a ServiceStack web service. As part of this I wanted to add authentication for certain operations. The service won’t be called by authenticated users on the whole so an API Key is a good option, in the future I may add in JWT authentication too as ServiceStack makes this easy.

For the whole to work you need a few elements setup. The first of these is the Auth Repository which is your store for the user details etc. ServiceStack offers quite a range of possibilities for this including Redis, Mongo DB, AWS Dynamo DB, various RDBMS (Via Ormlite). I chose to use the in memory auth provider initially. if I need to change this in future it will be a simple process to change.

The next element is the Auth Provider. As already mentioned I chose the API Key provider, this is sufficient for me but in future I may add others. For some Auth providers you also need to ensure you have a caching provider enabled. This doesn’t apply to the API Key provider but I have it in case a future provider needs it. Finally in my case I wanted users to be able to register themselves and this is added in by a Registration Feature.

This is what it looks like in my AppHost

var appSettings = new AppSettings();container.Register<ICacheClient>(new MemoryCacheClient());var authFeature = new AuthFeature(() => new AuthUserSession(),new IAuthProvider[]{new ApiKeyAuthProvider(appSettings),) {IncludeRegistrationService = true};Plugins.Add(authFeature);

This adds a few methods to your service. The auth feature adds /auth, /assignroles and /unassignroles; as I have added in registration I get the /Register route too.

Keys are generated when the user registers, this will by default generate a live and test key for the secret key type, you can add other key types as follows

Plugins.Add(new AuthFeature(
new IAuthProvider[] {
new ApiKeyAuthProvider(AppSettings) {
KeyTypes = new[] { "secret", "publishable" },
}
}
));

You can also reduce the I/O operations and therefore improve the performance of API Key Auth Requests by specifying a Session Cache duration to temporarily store the Authenticated UserSession against the API key. This can be enabled with

Plugins.Add(new AuthFeature(
new IAuthProvider[] {
new ApiKeyAuthProvider(AppSettings) {
SessionCacheDuration = TimeSpan.FromMinutes(10),
}
}));

We need the authRepository configuring too as this is where the data is stored. When this is done, we can generate the schema using

container.Resolve<IAuthRepository>().InitSchema();

Cleverly, ServiceStack makes this available for all Repository types, but it only does something for those that it needs to. In the case of an RDBMS it will create any needed tables.

The simplest way to add authentication to an individual service is simply to add the [Authenticate] attribute. You can limit the operations it applies to by filtering. For example to filter to just put and get you would use

[Authenticate(ApplyTo.Get, ApplyTo.Put)]

Process

The first step is to register which uses the following DTO. When you Post to this your user will be setup and as you will see from above it creates the default api keys. Alternatively you can setup your own users programmatically. You need to set a user name and password and a unique email address.

{
"UserName": "string",
"FirstName": "string",
"LastName": "string",
"DisplayName": "string",
"Email": "string",
"Password": "string",
"ConfirmPassword": "string",
"AutoLogin": true,
"ErrorView": "string",
"Meta": {}
}

Now you have a user you can login using that user to get your apikey. To do this you first use the Auth route. The DTO for this is as follows

{
"provider": "string",
"State": "string",
"oauth_token": "string",
"oauth_verifier": "string",
"UserName": "string",
"Password": "string",
"RememberMe": true,
"ErrorView": "string",
"nonce": "string",
"uri": "string",
"response": "string",
"qop": "string",
"nc": "string",
"cnonce": "string",
"UseTokenCookie": true,
"AccessToken": "string",
"AccessTokenSecret": "string",
"scope": "string",
"Meta": {}
}

In my case it’s simple to login, I just use the UserName and password I set on the Register route above.

When you’re logged in you can use the GetApiKeys route to get your keys. For this you just need to provide the environment, so if you have used the defaults you will need to do it twice, once for live and once for test

{
"Environment": "string",
"Meta": {}
}

This will return your apikey for that environment and you can use it as normal to make calls. e.g.

{
"results": [
{
"key": "l6w923456Jppx4isXL7",
"keyType": "secret"
}
]
}

The RegenerateApiKeys Service will invalidate all current api keys and generate new ones for the specified environment.

If you do a GET or a POST to /auth/logout then the authenticated user will be logged out.

--

--

Steve Ellwood
Steve Ellwood

Written by Steve Ellwood

Senior Integrations Officer at Doncaster Council Any views expressed are entirely my own.

No responses yet