OnAuthenticationRequired question

I Have two project , first project is MVC Project , Registering the authorization provider.

Plugins.Add(new AuthFeature(() => new CustomUserSession(),
          new IAuthProvider[]
          {
              new BasicAuthProvider(AppSettings) {PersistSession = true},    //Sign-in with Basic Auth
              new CredentialsAuthProvider(AppSettings) {SkipPasswordVerificationForInProcessRequests = true},
              new ApiKeyAuthProvider(AppSettings) {
                  RequireSecureConnection =false,
                  SessionCacheDuration = TimeSpan.FromMinutes(10)
              },
              new JwtAuthProvider(AppSettings) {
                  RequireSecureConnection =false,
                  AuthKey = AesUtils.CreateKey(),
              },
          })

second project is winform, used the Gateway to call the first project api.

container.Register<IServiceGateway>(GetNewClient()); 

public JsonServiceClient GetNewClient()
    {
        var authClient = new JsonServiceClient(f.ApiUrl)
        {
            UserName = username,
            Password = password
        };
        //return authClient;
        try
        {
            /*** JWT ***/
            var client = new JsonServiceClient(f.ApiUrl);  // 可以多参考 StatelessAuthTests.cs
            client.OnAuthenticationRequired = () =>
            {
                client.BearerToken = authClient.Send(new Authenticate()).BearerToken;
            };
            return client;
        }
        catch (Exception ex)
        {
            logger.Error(ex, "GetNewClient");
        }
        return null;
    }

First time everything is Ok, once the first project restarted and the second project is not reopen, the winform will called OnAuthenticationRequired, but It return client.BearerToken is null. If I started the winform again, It works fine.
In Servicestack 4.5.6 all is good, after upgrade 4.5.8, It has error. Is it need to use RefreshToken?

If I changed the GetNewClient to the code , It works fine. even if the website is restart. But I Have some old project is based on .net framework 4.03, can’t upgrade to .net framework 4.5, there has no RefreshToken, how to solve it ?

 protected virtual IJsonServiceClient GetClient() => new JsonServiceClient(f.ApiUrl);
    private string GetRefreshToken()
    {
        var authClient = GetClient();
        var refreshToken = authClient.Send(new Authenticate
        {
            provider = "credentials",
            UserName = userName,
            Password = password,
        }).RefreshToken;
        return refreshToken;
    }
    public JsonServiceClient GetNewClient()
    {
        var client = GetClient();
        var serviceClient = client as JsonServiceClient;
        if (serviceClient == null)  //OnAuthenticationRequired not implemented in JsonHttpClient
            return null;
        serviceClient.OnAuthenticationRequired = () =>
        {
            var authClient = GetClient();
            serviceClient.BearerToken = authClient.Send(new GetAccessToken
            {
                RefreshToken = GetRefreshToken(),
            }).AccessToken;
        };
        return serviceClient;
    }

Not sure what the question is, JsonHttpClient doesn’t implement OnAuthenticationRequired but it does implement the new Refresh Tokens support which you can use instead.

The last version of ServiceStack that supports .NET 4.0 Framework is v4.0.62 which did include support for OnAuthenticationRequired but support for .NET 4.0 is frozen at v4.0.62, if you need changes to .NET 4.0 clients you can download the source code for v4.0.62 and make any changes locally.

In the source file StatelessAuthTests.cs has

        [Test]
        public void Can_Auto_reconnect_with_BasicAuth_after_expired_token()
        {
            var authClient = GetClientWithUserPassword(alwaysSend: true);

            var called = 0;
            var client = new JsonServiceClient(ListeningOn)
            {
                BearerToken = CreateExpiredToken(),
            };
            client.OnAuthenticationRequired = () =>
            {
                called++;
                client.BearerToken = authClient.Send(new Authenticate()).BearerToken;
            };

            var request = new Secured { Name = "test" };
            var response = client.Send(request);
            Assert.That(response.Result, Is.EqualTo(request.Name));

            response = client.Send(request);
            Assert.That(response.Result, Is.EqualTo(request.Name));

            Assert.That(called, Is.EqualTo(1));
        }

in the source file JwtAuthProviderTests.cs has

        [Test]
        public void Only_returns_Tokens_on_Requests_that_Authenticate_the_user()
        {
            var authClient = GetClient();
            var refreshToken = authClient.Send(new Authenticate
            {
                provider = "credentials",
                UserName = Username,
                Password = Password,
            }).RefreshToken;

            Assert.That(refreshToken, Is.Not.Null); //On Auth using non IAuthWithRequest

            var postAuthRefreshToken = authClient.Send(new Authenticate()).RefreshToken;
            Assert.That(postAuthRefreshToken, Is.Null); //After Auth
        }

the first code authClient.Send(new Authenticate()).BearerToken can return value ,
why authClient.Send(new Authenticate()).RefreshToken return null, can you tell me the difference?

The BearerToken/RefreshToken are only populated on the request when the User Authenticates, i.e:

var refreshToken = authClient.Send(new Authenticate
{
    provider = "credentials",
    UserName = Username,
    Password = Password,
}).RefreshToken;

Not just when they’re already authenticated, i.e:

var postAuthRefreshToken = authClient.Send(new Authenticate()).RefreshToken;

Essentially it prevents someone who has managed to capture an authenticated session to continually call /auth whilst they’re authenticated to continually get new Bearer/Refresh tokens. They need to re-authenticate in order to get new Bearer/Refresh tokens.

so in StatelessAuthTests.cs, the below code is only run once, if run again , will get null.
In servicestack 4.5.6 the below code run again will get newValue, Is it changed in 4.5.8?

client.BearerToken = authClient.Send(new Authenticate()).BearerToken;

It changed from last release when RefreshTokens were implemented for reason above.

If re-authenticating it would’ve skipped authentication, you can logout to force re-authentication, e.g:

authClient.Send(new Authenticate { provider = "logout" });

How did authClient Authenticate? i.e. what’s the code to create the authClient?

My old code to create the authClient is copy from StatelessAuthTests.cs,
Now I have changed the code was works fine in 4.5.8

 protected virtual IJsonServiceClient GetClient() => new JsonServiceClient(f.ApiUrl);
    private string GetBearerToken()
    {
        var authClient = GetClient();
        var bearerToken = authClient.Send(new Authenticate
        {
            provider = "credentials",
            UserName = userName,
            Password = passWord,
        }).BearerToken;
        return bearerToken;
    }
    /// <summary>
    /// 这个主要用于.net 4.0 版本
    /// </summary>
    /// <returns></returns>
    public JsonServiceClient GetNewClient()
    {
        var client = GetClient();
        var serviceClient = client as JsonServiceClient;
        if (serviceClient == null)  //JsonHttpClient doesn't implement OnAuthenticationRequired but it does implement the new Refresh Tokens support
            return null;
        serviceClient.OnAuthenticationRequired = () =>
        {
            serviceClient.BearerToken = GetBearerToken();
        };
        return serviceClient;
    }
2 Likes