How to veto authentication based on a user property (SS 4.0.34)

The use case is to not allow a login until the user has validated his/hers email. I’d want to redirect the user to another page accessible to anonymous users asking to validate the email address, or request another validation email.

Both using AuthEvents and session.OnAuthenticated I can be notified of the authentication event, but have no way to Veto it.

Looking at the source I also see that there’s no way to break out of the event calls, and afterwards the session is saved.

I’d prefer not to change every auth provider to include this feature. Also, I’d prefer to do this on authentication to avoid running the check on all requests.

Am I missing something?

Why not making use of Roles?

Or even, make use of the Request Filter just like shown on this answer.

P.S. Tens o mesmo nome que eu, eheh Bruno Miguel Alexandre Lopes :wink:

Okay, I just noticed AuthenticateService.ValidateFn , thanks for the pointer. Trying it now.

P.S.: Hah! Bem visto :smile:

Okay, just tried ValidateFn and it is executed very early in the authentication process, so I only have the username, and would have to fetch the user by hand. It might be workable, but it’s a bit of extra work, that will be duplicated afterwards.

Yeah AuthenticateService.ValidateFn is a pre-auth validation filter, there’s also the AuthProvider.CustomValidationFilter on each AuthProvider which may be more appropriate since it allows you to apply post validation to an Authenticated UserSession where you’ll have access to the full AuthContext.

Returning a non-null response removes the UserSession, see: https://github.com/ServiceStack/ServiceStack/blob/b51fa9c67bd718f41e60fecc48c786e1c3aeacec/src/ServiceStack/Auth/AuthProvider.cs#L169-L186

Where you could return a redirect url response to an error page.

Also something else that maybe useful is the UserAuth.LockedDate which allows you to lock a users account. So you could automatically lock all new user accounts and then unlock them when they validate their email.

@mythz, thanks. I think I got this working with ValidateFn. Looking at CustomValidationFilter it seems a like a better way to go. But from what I can see CredentialsAuthProvider doesn’t use it. It overrides OnAuthenticated but doesn’t call base and check for return, nor use the filter.

Also, looking at DigestAuth it looks like it has the same issue, and there’s a session.IsAuthenticated = true; missing near line 138?

Using UserAuth.LockedDate would make it a bit confusing, because we might lock the user due other reasons. If needed we could add a LockedReason and go that way, but I’d prefer to use a check on authentication phase instead of changing state on the user.

Scratch that, ValidateFn won’t work. Since it runs before auth, I don’t even know if the user was correctly authenticated, and would have to do that myself. Means that it runs even if auth failed.

I’ll try subclassing CredentialsAuthProvider and perhaps DigestAuthProvider to take the CustomValidationFilter into consideration, see if that works.

Okay, this seems to barely work.

When subclassing CredentialsAuthProvider, should I call the CustomValidationFilter before or after base.OnAuthenticate?

The OAuth Providers seem to do it before doing the rest of the work.

It should be automatically in OnAuthenticated() if you’re overriding this method you should call the base before, e.g:

public override IHttpResult OnAuthenticated(IServiceBase authService, IAuthSession session, IAuthTokens tokens, Dictionary<string, string> authInfo)
{
    base.OnAuthenticated();
    ...
}

If you’re overriding existing behavior and don’t want to call base.OnAuthenticated(); you can call it at the start of this overridden method.

I might be missing something, but the default CredentialsAuthProvider doesn’t seem to call CustomValidationFilter filter:

Or is it being called from somewhere else?

Ahh yeah, CredentialsAuthProvider uses it’s own custom impl (i.e. different from OAuth providers) and I forgot to add the callbacks when I added it to the base class, which I’ve just added in this commit: https://github.com/ServiceStack/ServiceStack/commit/cd13c428a9eab8def0de94dd8c47646814138360

This change is now on MyGet: https://github.com/ServiceStack/ServiceStack/wiki/MyGet

For now we’ve subclassed CredentialsAuthProvider , but we’ll remove it when we upgrade to a version including that fix. Thanks.