2

In servicestack OAuth implementation I only saw possibility to automatically login with eg. facebook account.

But is there abbility to support registration process with facebook login. What I wanted is to let users login to facebook app, and then load their Name, Surname and email and prefill needed text boxes for real registration on my site (since I also have to have mobile phone verification etc.) I don't want user to be authorized and authenticated when he logs in with facebook. Only credentials login should be valid one for full site access.

Edit: I found a solution.

In FacebookProvider.cs

public override bool IsAuthorized(IAuthSession session, IOAuthTokens tokens, Auth request = null)
{
    if (request != null)
    {
        if (!LoginMatchesSession(session, request.UserName)) return false;
    }
    return tokens != null && session.UserName!=null && !string.IsNullOrEmpty(tokens.AccessTokenSecret);
}

The catch was the && session.UserName!=null part. So we can check if user is logged in using credentials, this will be !=null and user can use all services. If not, this will be ==null and he can only get facebook info from session.

1 Answers1

4

The SocialBootstrap API project shows an example of handling the callback after a successful Authentication by overriding the OnAuthenticated() hook of its custom user session:

I've pulled out, rewrote some and highlighted some of the important bits:

public class CustomUserSession : AuthUserSession
{
    public override void OnAuthenticated(IServiceBase authService, 
                    IAuthSession session, 
                    IOAuthTokens tokens, 
                    Dictionary<string, string> authInfo)
    {
        base.OnAuthenticated(authService, session, tokens, authInfo);

        //Populate matching fields from this session into your own MyUserTable
        var user = session.TranslateTo<MyUserTable>();
        user.Id = int.Parse(session.UserAuthId);
        user.GravatarImageUrl64 = CreateGravatarUrl(session.Email, 64);

        foreach (var authToken in session.ProviderOAuthAccess)
        {
            if (authToken.Provider == FacebookAuthProvider.Name)
            {
                user.FacebookName = authToken.DisplayName;
                user.FacebookFirstName = authToken.FirstName;
                user.FacebookLastName = authToken.LastName;
                user.FacebookEmail = authToken.Email;
            }
            else if (authToken.Provider == TwitterAuthProvider.Name)
            {
                user.TwitterName = authToken.DisplayName;
            }
        }

        //Resolve the DbFactory from the IOC and persist the user info
        using (var db = authService.TryResolve<IDbConnectionFactory>().Open())
        {
            //Update (if exists) or insert populated data into 'MyUserTable'
            db.Save(user);
        }

    }

    //Change `IsAuthorized` to only verify users authenticated with Credentials
    public override bool IsAuthorized(string provider)
    {
        if (provider != AuthService.CredentialsProvider) return false;
        return base.IsAuthorized(provider);
    }
}

Basically this user-defined custom logic (which gets fired after every successful authentication) extracts data from the UserSession and stores it in a custom 'MyUserTable'.

We've also overridden the meaning of IsAuthorized to only accept users that have authenticated with CredentialsAuth.

You can use this data to complete the rest of the registration.

Other possible customizations

ServiceStack's built-in Auth persists the AuthData and populates the Session automatically for you. If you want to add extra validation assertions you can simply use your own custom [Authentication] attribute instead containing additional custom logic. Look at the implementation of the built-in AuthenticateAttribute as a guide.

mythz
  • 141,670
  • 29
  • 246
  • 390
  • mythz, thanks for answer, but my real problem is that facebook user is automatically authorized for using all [Authenticate()] services. I want only credentials authorized users to be able to access this kind of services. – Bojan VukasovicTest Sep 14 '12 at 22:55
  • I've Updated to CustomUserSession to override meaning of IsAuthenticated. – mythz Sep 14 '12 at 23:53
  • I tried your solution, but it seems that IsAuthorized method in my custom session is never called. – Bojan VukasovicTest Sep 15 '12 at 00:16
  • Are you overriding it? It's called by the AuthAttribute here: https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.ServiceInterface/AuthenticateAttribute.cs#L66 – mythz Sep 15 '12 at 00:17
  • It works now. But your solution still has a problem that later you cannot login with facebook (after registration). My solution works for that case also :) – Bojan VukasovicTest Sep 15 '12 at 00:28
  • //Resolve the DbFactory from the IOC and persist the user info using (var db = authService.TryResolve().Open()) { //Update (if exists) or insert populated data into 'MyUserTable' db.Save(user); } ...... This bit doesn't seem to be correct as when I put in full namespace of the IDbConnectionFactory the db object doesnt have 'Save' method – fractal Sep 07 '13 at 22:26