ServiceStack CredentialAuthProvider with more than User/Password - servicestack

I want to use a custom auth provider, but I don't see how I can make the standard Auth stuff handle more that user and password as parameters.
Can this be done?

Depends what you want to do, if you want to create your own Custom auth provider by inheriting from CredentialsAuthProvider, you can access different request params via the IHttpRequest object:
public virtual bool TryAuthenticate(IServiceBase authService,
string userName, string password)
{
var httpReq = authService.RequestContext.Get<IHttpRequest>();
var fromQueryString = httpRequest.QueryString["CustomField"];
var fromPostData = httpRequest.FormData["CustomField"];
var fromEither = httpRequest.GetParam("CustomField"); //Ext method
}
Here are some other related questions and answers that show different ways to customize ServiceStack's built-in Auth:
How can I extend ServiceStack Authentication
Populating IAuthSession with data from the database

Related

How to put an Api key in the Authenticate message?

I'm trying to combine the api key auth provider with the encrypted messaging plugin.
var client = new JsonServiceClient(home);
client.BearerToken = "somesecret";
works
but i want my apikey to be in the message so i tried
var authResponse = client.Post(new Authenticate
{
provider = ApiKeyAuthProvider.Name,
UserName = "somesecret"
});
This post fails at runtime with a 401 not authenticated.
How do i get this to work?
IAuthWithRequest Auth Providers like the API Key Auth Provider needs to be sent per request with the Authenticated User Session it establishes only lasts for the lifetime of that request. It can't be used with the Authenticate Service to Authenticate the client as your example tried to do, it must be included in each request to an Authenticated Service.
The normal way to call a protected Service with the API Key is to just populate the BearerToken property:
var client = new JsonServiceClient(baseUrl) {
BearerToken = apiKey
};
Which will then let you call your [Authenticate] Service:
var response = client.Get(new Secure { Name = "World" });
Encrypted Messaging Support
Previously you could only embed the User SessionId within an Encrypted Messaging Request but I've just added support for Authenticating Encrypted Messaging Services with a BearerToken in this commit which works similar to populating a SessionId, where you can now populate a BearerToken as used in API Key and JWT Auth Providers by having your Request DTOs implement IHasBearerToken, e.g:
public class Secure : IHasBearerToken
{
public string BearerToken { get; set; }
public string Name { get; set; }
}
This will let you embed the BearerToken when calling the protected Service, e.g:
IEncryptedClient encryptedClient = client.GetEncryptedClient(publicKey);
var response = encryptedClient.Get(new Secure { BearerToken = apiKey, Name = "World" });
Where it will be embedded and encrypted along with all content in the Request DTO.
Alternatively you can also set the BearerToken property on the IEncryptedClient once and it will automatically populate it on all Request DTOs that implement IHasBearerToken, e.g:
encryptedClient.BearerToken = apiKey;
var response = encryptedClient.Get(new Secure { Name = "World" });
The new BearerToken support in Encrypted Messaging is available from v5.1.1 that's now available on MyGet.

Servicestack.Client Namespace Secured could not be found

When I try to make a new request to generate a new access-token I cannot find the type "new Secured".
var authClient = new JsonServiceClient("http://localhost/authentication/")
{
RefreshToken = Request.GetCookieValue("ss-refreshtok"),
RefreshTokenUri = "http://localhost/authentication/access-token"
};
var jwt = authClient.Send(new Secured());
Even thought I have Servicestack.client installed it cannot be found. But using new Authenticate() its ok.
The Secure Request DTO is an example of a Request DTO for a Service that’s protected with the [Authenticate] attribute, it’s not a built in DTO, you should substitute it to use your Request DTO instead.

ServiceStack Ws-Security Auth Provider

I'm trying to figure out how to support ws-security as authentication mechanism in SS.
My goal is to have all DTO handled in json,xml,saop11,saop12(that part has been achieved following the SS documentation) and supporting multiple auth providers including one based on ws-security.
DTOs should not be affected at all by the authentication mechanism.
In case the DTO will be sent using saop12, the soap message will be the the call sample generated by the metadata endpoint(soap envelope + soap body) plus a soap header including the ws-security element for the WS-Security Username Authentication. A dedidcated "soap auth provider" should inspect the message, use the soap header -> security element and perform the authentication.
Along with the soap auth provider, I may have other built-in auth mechanism that may used for json message and/or other formats.
Exists a SS auth provider based on ws-security that I'm not aware of?
Any guidelines, suggestions, thoughts to implement it?
At the present than my solution
//AppHost
Plugins.Add(new AuthFeature(() => new CustomAuthUserSession(),
new IAuthProvider[] {
new CustomCredentialsAuthProvider(),
new SoapMessageAuthProvider(),
}
));
// required by the SoapMessageAuthProvider to inspect the message body serching for ws-security element
PreRequestFilters.Add((httpReq, httpRes) =>
{
httpReq.UseBufferedStream = false;
});
I based the SoapMessageAuthProvider on the built-in BasicAuthProvider.
Since the SoapMessageAuthProvider requires to inspect the incoming message on each call serching for ws-security element, I implemented IAuthWithRequest
public void PreAuthenticate(IRequest req, IResponse res)
{
//Need to run SessionFeature filter since its not executed before this attribute (Priority -100)
SessionFeature.AddSessionIdToRequestFilter(req, res, null);
var userPass = ExtractSoapMessageUserNameCredentials(req);//req.GetBasicAuthUserAndPassword();
if (userPass != null)
{
var authService = req.TryResolve<AuthenticateService>();
//var response = authService.Post(new Authenticate
//{
// provider = Name,
// UserName = userPass.Value.Key,
// Password = userPass.Value.Value
//});
authService.Request = req;
var session = authService.GetSession(false);
var userName = userPass.Value.Key;
//Add here your custom auth logic (database calls etc)
var userAuth = new UserAuth();
userAuth.Id = 10;
userAuth.UserName = userName;
var holdSessionId = session.Id;
session.PopulateWith(userAuth); //overwrites session.Id
session.Id = holdSessionId;
session.IsAuthenticated = true;
session.UserAuthId = userAuth.Id.ToString(CultureInfo.InvariantCulture);
session.UserAuthName = userName;
}
}
//called by CustomAuthUserSession.IsAuthorized
// to be reviewed to keep isolated from other providers
public override bool IsAuthorized(IAuthSession session, IAuthTokens tokens, Authenticate request = null)
{
if (request != null)
{
if (!LoginMatchesSession(session, request.UserName))
{
return false;
}
}
return !session.UserAuthId.IsNullOrEmpty();//filled by PreAuthenticate
}
the custom session calls each provider, including the SoapMessageAuthProvider that meanwhile, through the PreAuthenticate method, filled out the session with authenticated user data.
public class CustomAuthUserSession : AuthUserSession
{
public override bool IsAuthorized(string provider)
{
var tokens = ProviderOAuthAccess.FirstOrDefault(x => x.Provider == provider);
return AuthenticateService.GetAuthProvider(provider).IsAuthorizedSafe(this, tokens);
}
...
}
I need to make sure the soap provider will be always invoked for soap message w/ ws-security and the call should not be authenticated by other providers:
- user get authentication through the CustomCredentialsAuthProvider(cookie based)
- user call the service supply json message within the web request that carries the auth cookie
- a further call sends a soap message carrying the same auth cookie: since the message is in soap format and includs the soap header ws-security, the call should be authenticated only using the soap provider using the soap header ws-security within the message.
I understand that a weird scenario, but I'm trying to understand how to accomplish it.
My guess that happends through the ServiceStack.AuthenticateAttribute line 72
matchingOAuthConfigs.OfType<IAuthWithRequest>()
.Each(x => x.PreAuthenticate(req, res));

Extending service stack authentication - populating user session with custom user auth meta data

I am trying to extend Service Stack's authentication and registration features. I have the authentication and registration working fine, however I need to add some custom data for each user. From Service Stack's documentation and various other posts I found you can add your own data using the MetaData column built into the UserAuth table.
I created a CustomAuthRepository so I can set the meta data property of UserAuth, here is my custom repo:
public class CustomAuthRepository : OrmLiteAuthRepository, IUserAuthRepository
{
public UserAuth CreateUserAuth(UserAuth newUser, string password)
{
newUser.Set(new LoginInfo
{
IsActive = false,
PasswordNeedsReset = true
});
return base.CreateUserAuth(newUser, password);
}
}
This is working great for setting the meta data, I end up with a serialized version of the LoginInfo object in the meta data column of the UserAuth table.
Now what I am trying to do is when a user authenticates I need to change the AuthResponse based on some of that meta data. For example, if a user is not yet activated I want to return an AuthResponse with a property IsActive = get value from custom meta data
I figure I could do this if I can get my custom metadata into the AuthSession. That way in my custom credentials auth provider I could change the response object based on what's in the AuthSession:
public class CustomCredentialsAuthProvider : CredentialsAuthProvider
{
public override object Authenticate(IServiceBase authService, IAuthSession session, Auth request)
{
var customUserAuthSession = (CustomUserAuthSession)session;
if (!customUserAuthSession.LoginInfo.IsActive)
{
return new
{
UserName = customUserAuthSession.UserName,
IsActive = customUserAuthSession.LoginInfo.IsActive
};
}
var isAuthenticated = base.Authenticate(authService, session, request);
return isAuthenticated;
}
}
Am I going about this the right way, or is there a better way to store and retrieve custom meta data?
How can I change the AuthResponse based on a user's custom meta data?
How can I get my custom meta data into the AuthSession?
Edit
I am getting closer to what I am trying to do. In my CustomAuthSession OnAuthenticated() method :
public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo)
{
var customUserAuthSession = (CustomUserAuthSession) session;
var userAuth = authService.ResolveService<IUserAuthRepository>().GetUserAuth(session, null);
customUserAuthSession.LoginInfo = userAuth.Get<LoginInfo>();
authService.SaveSession(customUserAuthSession);
base.OnAuthenticated(authService, session, tokens, authInfo);
}
I am refetching the UserAuth and populating the session with the data that I need. Based on the service stack documentation for a custom user session, you need to save the session after you populate it with some custom data. I am doing that but it doesn't seem to be saving.
In my CustomCredentialsAuthProvider, Authenticate method, I don't see the custom data I've added to the session.
Edit
The problem with my first edit above is that the user gets authenticated, then we get to the CustomAuthSession code where I can check if they are active or not. In the case they are not active I would need to log them out, not ideal.
I found instead that I can do all of this in the Authenticate method of my custom CredentialsAuthProvider.
public override object Authenticate(IServiceBase authService, IAuthSession session, Auth request)
{
var userAuthRepo = authService.ResolveService<IUserAuthRepository>();
var userAuth = userAuthRepo.GetUserAuthByUserName(request.UserName);
var loginInfo = userAuth.Get<LoginInfo>();
if (!loginInfo.IsActive)
{
return new CustomAuthResponse
{
UserName = userAuth.UserName,
ResponseStatus = new ResponseStatus("500"),
IsActive = loginInfo.IsActive
};
}
var authResponse = (AuthResponse)base.Authenticate(authService, session, request);
return authResponse;
}
When the request comes in I can use the username in the request to fetch the UserAuth, and check if the user IsActive or not. If not then I can return some error before Service Stack authenticates them.
I think this works well enough for what I am trying to do. I should be able to return an error to the client saying the user is not active.
If anyone has a cleaner way to do this that would be great.
Here is my answer so far. It works and I can do what I am trying to do, but I would love to hear from some of the Service Stack guys as to whether this is the best way to go about this.
To Save custom meta data
Create a new class that subclasses the OrmLiteAuthRepository. In my case I just want to use Service Stack's built in Sql database persistence and have it create the tables needed.
Re-implement the CreateUserAuth method to save any custom metadata :
public UserAuth CreateUserAuth(UserAuth newUser, string password)
{
newUser.Set(new AccountStatus
{
IsActive = false,
PasswordNeedsReset = true
});
return base.CreateUserAuth(newUser, password);
}
Fetching custom meta data
Create a new class that subclasses the CredentialsAuthProvider. Override the Authenticate method.
public override object Authenticate(IServiceBase authService, IAuthSession session, Auth request)
{
var userAuthRepo = authService.ResolveService<IUserAuthRepository>();
var userAuth = userAuthRepo.GetUserAuthByUserName(request.UserName);
var accountStatus= userAuth.Get<AccountStatus>();
if (!accountStatus.IsActive)
{
throw new InvalidOperationException(string.Format("User {0} is not activated.", request.UserName));
}
if (!accountStatus.PasswordNeedsReset)
{
throw new InvalidOperationException("Your password needs to be reset before you can login.");
}
var authResponse = (AuthResponse)base.Authenticate(authService, session, request);
return authResponse;
}
When an authentication request comes into this method, fetch the UserAuth and the custom meta data. If the user is inactive, or their password needs to be reset throw an InvalidOperationException with an error message.
In my client application's Login controller I can check the error message coming back from the service and redirect the user to some page saying there account isn't active yet, or their password needs to be reset before they can be authenticated.

What's the equivalent of HttpContext.Current.User in an HttpListener-hosted service?

I've written a custom attribute for ServiceStack that has the following code in it:
public override void Execute(IHttpRequest request, IHttpResponse response, object requestDto) {
HttpContext.Current.User = GetUserFromOAuth2BearerToken(request);
}
This works beautifully when hosting in IIS, but when using the built-in HttpListener host, HttpContext.Current returns null.
What's the correct syntax to set, and get, the security principal associated with the current request/response when using HttpListener?
I am not sure the 'security principal/System.Security.Principal' is available with the current request/response when using AppHostHttpListenerBase.
You could pull get the User inforamation doing something like...
var sessionId = request.Cookies["ss-id"].ToString();
var user = request.GetCacheClient().Get<AuthUserSession>(sessionId);

Resources