ServiceStack authentication provider and SOAP conflict - servicestack

I cannot generate proxy class when authentication provider is enabled. All DTOs in ServiceStack must be in the same namespace. I get that. Here's where this becomes a problem. Enabling authentication
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] { new BasicAuthProvider()} ));
adds a few ServiceStack resources - Auth, AssignRoles and so on. These DTOs are in the ServiceStack default namespace http://schemas.servicestack.net/types. Now I am stuck - even with the Config.WsdlServiceNamespace and ContractNamespace tricks described in https://github.com/ServiceStack/ServiceStack/wiki/SOAP-support I cannot get svcutil to build a proxy class because my DTOs and ServiceStack provided (which are required for authentication) are in different namespaces.
Can I have authentication provider and SOAP at the same time?

Related

Utilize JWTBearer from multiple Identity Providers in ServiceStack API

Is it possible for a ServiceStack api to accept jwt tokens from multiple identity providers?
I have one admin application that will be calling all our apis across environments. I need to allow my api's to accept jwt tokens from two different identity providers. This can be accomplished in web api, by calling the .AddJwtBearer help method twice and not providing a default schema in the AddAuthentication() helper. And the providing both in the AddAuthorization helper method. I tested this out in ServiceStack and it is not working for me.
This is in the .net core startup, configure services.
services.AddAuthentication()
.AddJwtBearer("Bearer", options => {
options.Authority = Configuration["IDENTITYSRV_WEB_BASEURL"];
options.RequireHttpsMetadata = Boolean.Parse(Configuration["IDENTITY_HTTPSMETADATA"]);
options.Audience = Configuration["IDENTITY_VALIDAUDIENCE"];
})
.AddJwtBearer("Admin", options =>
{
options.Authority = "Configuration["IDENTITYSRV_WEB2_BASEURL"]";
options.RequireHttpsMetadata = Boolean.Parse(Configuration["IDENTITY_HTTPSMETADATA"]);
options.Audience = Configuration["IDENTITY_VALIDAUDIENCE"];
});
AppHost
AuthFeature auth = new AuthFeature(() => new AuthUserSession(), new IAuthProvider[] { new NetCoreIdentityAuthProvider(AppSettings), })
{
IncludeAssignRoleServices = false,
IncludeRegistrationService = false,
IncludeAuthMetadataProvider = false
};
Plugins.Add(auth);
Any suggestions or work around?
ServiceStack's JWT Auth Provider supports all HMAC and RSA crypto algorithms for its JWT or JWE tokens, but it can only be configured with the 1 algorithm you want it to use. So it's technically not possible for it to verify JWT Tokens from different authorities which would likely be configured with different keys and algorithms.
The next obstacle is that if all Identity providers were configured to use the same Key and Algorithm, they would need to encode the same contents ServiceStack uses in order for the JWT to be correctly deserialized into an Authenticated ServiceStack Session. Most of the names used in ServiceStack's JWT's have well-known names however there are others that don't like roles and perms, in which case ServiceStack JWT's have adopted the Azure Active Directory Conventions. Although there is an opportunity to apply custom logic when inspecting the verified JWT Body and populating the Session using the PopulateSessionFilter.
Ultimately I don't think trying to funnel multiple JWT's into the same implementation is a viable strategy, instead I would be looking at creating Custom JWT Auth Providers like JwtAuthProviderReader.cs which just knows how to handle parsing JWT's from a single provider which would then know how to verify & extract the Authenticated Session Info from each JWT and use it to populate a ServiceStack Session. This could be done in a "single uber JWT Auth Provider" that has knowledge in being able to parse every JWT sent to it, but as JWT Tokens are sent with the same Authorization: Bearer {Token} HTTP Header there would need to be some information in the JWT Headers that determines which crypto algorithms it should use to validate each token. If all Identity Providers use different alg then that might be enough to be able to distinguish the identity provider.
Either way there's no pre-built solution in ServiceStack that you could configure to support multiple identity Auth Providers, the other approach you may want to look at is to maintain all Authentication in a central Identity Server Auth Provider so then your Websites only need to be configured to support Authentication from the central Identity Server.

Extending WebSphere SAML Identity propogation

I have an existing financial application which uses an API gateway to authenticate web-based users. This gateway maintains a security session for the user, and proxies SOAP calls on to a WebSphere box. It adds a signed SAML assertion to these SOAP calls.
A series of JAX-WS Services are deployed on WebSphere, and these are protected with WebSphere policies to consume the SAML assertions. The identity and group memberships specified in the SAML assertions are then propagated to the WebSphere security context for the service call. All works very well, all of the security logic is done purely by configuration.
New requirements now require that we propagate the sessionId in the API gateway all the way through to WebSphere , and beyond. This is for reasons of traceability.
Clearly we could change the WSDL for all of the services to include some Meta-data fields, but this is a big change, and would require very extensive testing.
I was hoping there might be a way to map some arbitrary attributes from the SAML assertion (Other than Identity and groupMembership) to the WebSphere security context. Or even to access the SAML XML in the (authenticated) JAX-WS Service.
Has anyone done this?
You can have API gateway to add sessionid as an SAML attribute, then retrieve the attribute from Subject after SAML is processed by WebSphere. Here is sample code to get SAML attribute in WebSphere after SAML is processed.
Subject subject = WSSubject.getRunAsSubject();
SAMLToken samlToken = (SAMLToken) AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction() {
public Object run() throws java.lang.Exception
{
final java.util.Iterator authIterator = subject.getPrivateCredentials(SAMLToken.class).iterator();
if ( authIterator.hasNext() ) {
final SAMLToken token = (SAMLToken) authIterator.next();
return token;
}
return null;
}
});
Map<String, String> attributes = samlToken.getStringAttributes();
List<SAMLAttribute> attributes = samlToken.getSAMLAttributes();
Instead of looping through the creds yourself, you can use the WSSUtilFactory API to do it for you: https://www.ibm.com/support/knowledgecenter/SSAW57_9.0.0/com.ibm.websphere.javadoc.doc/web/apidocs/com/ibm/websphere/wssecurity/wssapi/WSSUtilFactory.html
Assuming that you are using a SAML 2.0 token, you can do:
WSSUtilFactory wssuf = WSSUtilFactory.getInstance();
SAMLToken token = wssuf.getSaml20Token();
The getSaml20Token method was added to WSSUtilFactory in 70043, 80013, 85510 and 9000.

Why does using ServiceStack JwtAuthProviderReader add auth endpoints to my resource API?

I have set up an authentication server that issues JWT Tokens.
I now have setup my first resource service that will authenticate/authorize using the bearer token provided in a request. This service is not my auth server, it is a resource server.
I added the ServiceStack JwtAuthProviderReader to my resource service:
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new JwtAuthProviderReader() {
HashAlgorithm = "HS256",
AuthKeyBase64 = AuthSettings.JwtAuthKeyBase64
},
}));
Why does my resource server now have all the auth server endpoints, I am using the JwtAuthProviderReader, not the JwtAuthProvider that my auth service does. As the documentation states, my resource service is only validating tokens.
These aren't limited to just the JWT AuthProvider, they're ServiceStack's built-in Auth Services for handling any ServiceStack Authentication, i.e. when registering the ServiceStack's AuthFeature plugin.
If you're not using Assign/Unassign Roles Services, they can be disabled with:
Plugins.Add(new AuthFeature(...) {
IncludeAssignRoleServices = false
});
You can also hide Services from showing up in the metadata pages and Services by dynamically adding Exclude attributes in the AppHost's constructor, e.g:
public AppHost() : base("MyApp", typeof(MyServices).Assembly)
{
typeof(Authenticate)
.AddAttributes(new ExcludeAttribute(Feature.Metadata));
}
Which is equivalent to adding the attribute on the Request DTO, e.g:
[Exclude(Feature.Metadata)]
public class Authenticate { ... }

Authenticate server to server communication with API key

I have a couple of self-hosted windows services running with ServiceStack. These services are used by a bunch of WPF and WinForms client applications.
I have written my own CredentialsAuthProvider. My first implementation of the user database was on MSSQL server using NHibernate. Now since the system is growing I reorganize things a bit. I have created a central 'infrastructue' service which uses Redis as data store and is responsible for account management, central configuration and preferences management. Later it will also contain central logging and RedisMQ. All accounts, roles etc are now stored there (instead of MSSQL). Account migration was successfuly and authentication works fine so far.
Now I have the problem, that clients and servers need to get and set their configurations / preferences. This means that my servers are also clients since they not only serve client requests for their specific business domain but itself need to call the 'infrastructure' server to load / update its own configuration and later log entries and messages.
To authenticate such requests I thought an API key is a good way to go. These requests are not user related and therefore do not need a gateway functionality, they simply need some communication with the central infrastructure server. So I was reading the ServiceStack docs about the API Key Provider, but unfortunately for me a lot remains unclear.
Here first some relevant code from my 'infrastructure' server's Configure method:
private PooledRedisClientManager RedisBusinessPool { get; set; }
//...
container.Register<IRedisClientsManager>(c => new PooledRedisClientManager(connStrBus));
container.Register(c => new AppUserRepository(RedisBusinessPool));
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new BediCredentialsAuthProvider(),
}
));
// For the API keys I tried:
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new ApiKeyAuthProvider(AppSettings)
{
KeyTypes = new []{"secret", "publishable"},
},
}
));
Since I enabled the API Key plugin I get an error on the client when I try to login:
ERROR; AccountManagerWinDesktop; [LoginViewModel+<Login>d__50.MoveNext]; - <username> failed to login to server <myInfrastructureServer>. Exception: 404 NotFound
Code: NotFound, Message: No configuration was added for OAuth provider 'credentials'
Does this mean, that I have to implement my own ApiKeyProvider to cooperate with my implementation of the CredentialAuthProvider? If so, what do I need to add?
In my CredentialAuthProvider implementation I have overwritten Logout, Authenticate, OnAuthenticated and TryAuthenticate. A WPF client offers a UI to store users and roles. They are stored on the Redis database including hashed passwords etc. In my TryAuthenticate implementation I simply have:
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
AppUser user = null;
try
{
//the repository handles all Redis database access
var userRepo = HostContext.TryResolve<AppUserRepository>();
user = userRepo.GetAppUser(userName);
if (user == null)
throw HttpError.NotFound("User '{0}' not found. Please try again.".Fmt(userName));
authService.Request.Items.Add("AppUser", user);
var pwdMgr = new PwdManager();
var hpwd = pwdMgr.GetHashedPassword(password, user.Salt);
if (hpwd == user.Password)
{
//do stuff
}
else
{
// do other stuff
}
return hpwd == user.Password;
}
catch (Exception ex)
{
Log.Error($"Error retrieving user {user} to authenticate. Error: {ex}");
throw;
}
}
What I do not understand right now - Questions:
How are API keys related to my own implementation of CredentialsAuthProvider?
How can I issue API keys to an application server? (I read that ServiceStack creates keys automatically when a user is created, but I do not need this in my scenario)
Do I also have to implement my own ApiKeyAuthProvidersimilar to the CredentialsAuthProvider I have overwritten? If so, is there a sample somewhere?
Is there any object / data model for API keys?
Do I need to implement something like the TryAuthenticate method above to verify my API Keys?
You should only ever register 1 of any Plugin type, so change your AuthFeature plugin to register all Auth Providers you want to enable, e.g:
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new BediCredentialsAuthProvider(),
new ApiKeyAuthProvider(AppSettings)
{
KeyTypes = new []{"secret", "publishable"},
},
}
));
How are API keys related to my own implementation of CredentialsAuthProvider?
An API Key is assigned to a User, i.e. when a request is received with an API Key, they're authenticated as the user the API Key is assigned to. API Keys are created for each new user that's registered, the above configuration creates a secret and publishable key for a new register created with the /register service.
API Keys requires using a User Auth Repository
Your users need to be persisted in an AuthRepository in order to use the API Key AuthProvider. The list of support Auth Repositories are listed on in the documentation. Although you can use your own custom User Auth Repository if it implements IUserAuthRepository and IManableApiKeys interfaces.
How can I issue API keys to an application server? (I read that ServiceStack creates keys automatically when a user is created, but I do not need this in my scenario)
An API Key is assigned to a User - all of ServiceStack AuthProviders revolves around Authenticating Users. One idea is to create a "User" to represent that App Server. You can use the IManageApiKeys API to create your own API Keys, there's an example of using this in the code-snippet for creating API Keys for existing Users.
Do I also have to implement my own ApiKeyAuthProvider similar to the CredentialsAuthProvider I have overwritten? If so, is there a sample somewhere?
You wont need to implement anything to use the existing ApiKeyAuthProvider but if it doesn't do what you need you can take ApiKeyAuthProvider.cs and create a customized version that does what you need.
Is there any object / data model for API keys?
The ApiKey class is the model that contains the API Key itself, which is persisted in all supported Auth Repositories.
Do I need to implement something like the TryAuthenticate method above to verify my API Keys?
No.

Authorization extensions for ServiceStack

Is there any (or going to be any) built in support for declaratively securing (i.e. using attributes) REST services for oAuth2?
I would like to specify that the REST services of my SS web service can only be accessed by a client if they specify the oAuth2 'Authorization' header in their request to the service.
I don't want my service to provide authentication to my clients (i.e. no AuthFeature). Clients need to have already done authentication with a oAuth service (i.e. facebook etc.).
Using the [Authenticate] attribute on your Service ensures that only authenticated clients have access.
The Authentication wiki explains how to initialize ServiceStack's built-in AuthFeature to specify only the providers you want to allow clients to authenticate with, e.g. You can ensure clients can only Authenticate with either LinkedIn or Google OAuth2 providers with:
var appSettings = new AppSettings(); //Access Web.Config AppSettings
Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[] {
new GoogleOAuth2Provider(appSettings), //Sign-in with Goolge OAuth2
new LinkedInOAuth2Provider(appSettings), //Sign-in with LinkedIn OAuth2
}));
Note: OAuth2 requires the additional ServiceStack.Authentication.OAuth2 NuGet package and Web.Config settings, see Auth docs for more info.
Using Request Filters
You can also enforce specific requirements for client requests by a Global Request Filter or opt-in Request Filter Attributes, e.g:
this.RequestFilters.Add((httpReq, httpRes, requestDto) => {
var authHeader = httpReq.Headers[HttpHeaders.Authorization];
if (!IsValidAuthHeader(authHeader)) {
httpRes.StatusCode = (int)HttpStatusCode.Unauthorized;
httpRes.StatusDescription = "Authentication is required";
httpRes.EndRequest();
}
});
More Service Restrictions
Also related are the Security docs describes how you can declaratively restrict services using the [Restrict] attribute.

Resources