Open Closed

Login From Desktop Client #53


0
ididsbury created

Hi,

We need to create a desktop client (WPF) and we would like to utilise the dynamic HTTP proxies however I can't seem to find a service / proxy that will allow a user login. A login method doesn't seem to exist on the IAcoountAppService. Does a login method exist elsewhere? Am I missing something?

I can obviously login via a HttpClient however I can't see how to do this while using the dynamic proxies (the proxies will use a separate client that hasn't been logged in).

Appreciate any help.

Thanks

Ian


4 Answer(s)
  • 0
    alper created
    Support Team

    hi,

    you can use OpenID Connect. See our demo application (Angular client), https://commercial.abp.io/demo it calls http://9cc87abb646dbc56.demo.commercial.abp.io/.well-known/openid-configuration then makes a request to http://9cc87abb646dbc56.demo.commercial.abp.io/connect/token

    See also https://docs.abp.io/en/abp/latest/Samples/Microservice-Demo#authentication

  • 0
    ididsbury created

    Hi,

    Thanks for the above. I'm still strugglling to piece together how openid connect can be utilised with the dynamic http proxies. Do you have a c# sample?

    i've looked at the sample console client in inhttps://github.com/abpframework/abp/blob/dev/samples/MicroserviceDemo/applications/ConsoleClientDemo/ClientDemoService.cs.

    However, this seems to use a different authenication process. Can you please explain how OpenID connect can be used to login such that subsequent calls using the dynamic proxies to not fail (user not logged in). Do I need to update the underlaying HTTP client headers with the token recieved from OpenID connect?

    I suspect I'm missing something simple or my understanding of the authentication process is flawed.

    Any help greatly appreciated.

    Thanks

    Isn

  • 0
    hikalkan created
    Support Team

    Hi,

    We are using the Identity Server for authentication. There are some different flows, but I think the most convinient way is to use the resource owner password flow for your case.

    The downloaded startup template has a .HttpApi.Client.ConsoleTestApp project which authenticates using this flow by default. However, it assumes that username and password is hard-coded in the appsettings.json. I will explain you how to convert this project to dynamically get username & password from user, then authenticate and get access token.

    1. Create an AccessTokenManager class like that:
    using System;
    using System.Net.Http;
    using System.Threading.Tasks;
    using IdentityModel.Client;
    using Volo.Abp.DependencyInjection;
    
    namespace MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp
    {
        public class AccessTokenManager : ISingletonDependency
        {
            public string AccessToken { get; private set; }
    
            private readonly IHttpClientFactory _httpClientFactory;
    
            public AccessTokenManager(IHttpClientFactory httpClientFactory)
            {
                _httpClientFactory = httpClientFactory;
            }
    
            public async Task ObtainAccessToken()
            {
                Console.Write("Username: ");
                var userName = Console.ReadLine();
    
                Console.Write("Password: ");
                var password = Console.ReadLine();
    
                var discoveryResponse = await GetDiscoveryResponse();
                var tokenResponse = await GetTokenResponse(discoveryResponse, userName, password);
    
                AccessToken = tokenResponse.AccessToken;
            }
    
            protected async Task<DiscoveryDocumentResponse> GetDiscoveryResponse()
            {
                using (var httpClient = _httpClientFactory.CreateClient())
                {
                    return await httpClient.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest
                    {
                        Address = "https://localhost:44301",
                        Policy = { RequireHttps = true }
                    });
                }
            }
    
            protected async Task<TokenResponse> GetTokenResponse(
                DiscoveryDocumentResponse discoveryResponse,
                string userName,
                string password
            )
            {
                using (var httpClient = _httpClientFactory.CreateClient())
                {
                    return await httpClient.RequestPasswordTokenAsync(
                        await CreatePasswordTokenRequestAsync(discoveryResponse, userName, password)
                    );
                }
            }
    
            protected virtual Task<PasswordTokenRequest> CreatePasswordTokenRequestAsync(
                DiscoveryDocumentResponse discoveryResponse,
                string userName,
                string password)
            {
                var request = new PasswordTokenRequest
                {
                    Address = discoveryResponse.TokenEndpoint,
                    Scope = "MyProjectName",
                    ClientId = "MyProjectName_App",
                    ClientSecret = "1q2w3e*",
                    UserName = userName,
                    Password = password
                };
    
                return Task.FromResult(request);
            }
    
        }
    }
    

    ObtainAccessToken() method gets user & pass from console, authenticates to the identity server (just as described in the IDS4 documentation), then sets it to the AccessToken property. This class is singleton, so you can access the token from another service later.

    I've written the configuration hard-coded, you need to refactor the code :)

    1. ABP Framework's dynamic c# proxy system uses the IRemoteServiceHttpClientAuthenticator to get the access token. So, you need to implement it:
    using System.Threading.Tasks;
    using IdentityModel.Client;
    using Volo.Abp.DependencyInjection;
    using Volo.Abp.Http.Client.Authentication;
    
    namespace MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp
    {
        public class MyRemoteServiceHttpClientAuthenticator : IRemoteServiceHttpClientAuthenticator, ITransientDependency
        {
            private readonly AccessTokenManager _accessTokenManager;
    
            public MyRemoteServiceHttpClientAuthenticator(AccessTokenManager accessTokenManager)
            {
                _accessTokenManager = accessTokenManager;
            }
    
            public Task Authenticate(RemoteServiceHttpClientAuthenticateContext context)
            {
                context.Client.SetBearerToken(_accessTokenManager.AccessToken);
                return Task.CompletedTask;
            }
        }
    }
    

    This gets the token from the AccessTokenManager. It is very simple.

    1. Call the AccessTokenManager.ObtainAccessToken() on your login screen of the WPF app. In this console app, I called it inside the ConsoleTestAppHostedService, before the ClientDemoService usage:
    await application.ServiceProvider
        .GetRequiredService<AccessTokenManager>()
        .ObtainAccessToken();
    
    var demo = application.ServiceProvider.GetRequiredService<ClientDemoService>();
    await demo.RunAsync();
    

    That's all! Enjoy with the ABP Framework :)

  • 0
    ididsbury created

    Thanks for the excellent response. Much appreciated.