Recommended login flow for .Net desktop/console applications

tonystark
tonystark edited August 2020 in .Net API client
Hi,

Here is an example code for the login flow. This example creates a local http server and uses that for the redirect URL and launches system browser for login. With this you don't have to embed a web browser component inside your app and can also reuse the Kite session in the browser.

This example is based on this google sample project.
using System;
using KiteConnect;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Diagnostics;

namespace KiteConnectLoginExample
{
class Program
{
static void Main(string[] args)
{
login();
Console.ReadKey();
}

public static async void login()
{
Kite kite = new Kite("api_key", Debug: true);

// Generate a redirect url for local http server
string redirectURL = string.Format("http://{0}:{1}/", IPAddress.Loopback, GetRandomUnusedPort());

Console.WriteLine("Redirect URL: " + redirectURL);

// Generate kite login url with the above redirect url
string loginURL = string.Format("{0}&redirect_url={1}", kite.GetLoginURL(), redirectURL);

Console.WriteLine("Login URL: " + loginURL);

// Start local http server for catching the redirect url
var http = new HttpListener();
http.Prefixes.Add(redirectURL);
http.Start();
Console.WriteLine("Waiting for login to finish");

// Launch default browser with login url
Process.Start(new ProcessStartInfo(loginURL) { UseShellExecute = true });

// Wait until login is complete and redirect url is launched
var context = await http.GetContextAsync();

// Send a response back to browser
var response = context.Response;
string responseString = string.Format("Please return to the app");
var buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
var responseOutput = response.OutputStream;
Task responseTask = responseOutput.WriteAsync(buffer, 0, buffer.Length).ContinueWith((task) =>
{
responseOutput.Close();
http.Stop();
Console.WriteLine("HTTP server stopped.");
});

// Collect request token from the redirect url
var requestToken = context.Request.QueryString.Get("request_token");
if (requestToken == null)
{
Console.WriteLine("Login was not completed");
return;
}

// Use request token to generate access token
User user = kite.GenerateSession(requestToken, "app_secret");

// Store this locally and reuse it. This is valid for one day
Console.WriteLine("Access token: " + user.AccessToken);

kite.SetAccessToken(user.AccessToken);
kite.GetHoldings();
}

// Helper function to get a random local port to start an http server
public static int GetRandomUnusedPort()
{
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
listener.Stop();
return port;
}
}
}
  • trade_then
    trade_then edited July 2020
    deleted comment on after thought.
    thanks for the effort thou.

    Thanks
    Regards.
  • ritpatel
    Hello,
    I am trying this, but the process stops after opening the browser. It says "Waiting for login to finish" and doesn't move ahead even after login is complete.
    Can you suggest what could have been the issue?

    PS: I was using default Webbrowser component but it has stopped working and hence trying above mentioned process.
  • tonystark
    To make the app wait until the login is complete I just added Console.ReadKey(); I believe somewhere you might have pressed some key and that made the app close before it gets the redirect.

    This is just one example to do it. You might have to change the strategy to keep the app alive according to your app's behaviour.
  • ritpatel
    @tonystark thank you, I tried but it is not working.
    Tried debugging it, code stops at "var context = await http.GetContextAsync();" line and it doesn't move forward.
  • tonystark
    @ritpatel What does browser show after login is complete? Does it show "Please return to the app"?
  • ritpatel
    @tonystark it doesn't show any message. It stops after login process. (just one popup of saving the password, which is canceled).
    Currently, I am manually taking request token after login and using it for app login.
  • tonystark
    Where are you taking the request token from? Can you provide a screenshot(mask credentials and tokens) ?
  • ritpatel
    Hi @tonystark I meant access token, I am taking it from URL after login.
  • sujith
    @ritpatel,
    After login one gets a request token in the redirect URL. It is not an access token.
    Are you calling generate session with the request token?
  • ritpatel
    @tonystark , yes, I am calling GenerateSession manually using request token from URL after login is complete. (sorry I keep getting confused between token names)
  • tonystark
    @ritpatel It is not clear from your comments what exactly is going wrong. A screenshot of the page after login will be useful.

    Also let us know what are the modifications you did to your code. Above code should work fine just by replacing api key and secret. Also if you have any firewall running make sure it is not blocking your application.
  • ritpatel
    @tonystark Please find below screenshot of login page where it gets stuck. Nothing happens after this page.
    I am using same code, without any modifications.


  • tonystark
    @ritpatel Looks like the above code had no effect in your situation. Can you post the result printed by this line in the above code:

    Console.WriteLine("Login URL: " + loginURL);
  • trade_then
    I could be wrong here.

    What might be appearing as stuck up, could actually be an error.
    and your control could have moved passed too.
    not the hard Exception type, but web operation related.
    Try putting this operation under try{}catch( Exception ex )
    and is this operation being run under full - administrative privileges.

    try running the code as administrator.

    sorry if you are doing it already.


    Thanks
    Regards

  • ritpatel
    hi @tonystark @trade_then
    I have figured out the issues, somehow code was not going till Console.ReadKey(); line. Now it is working fine.

    Thanks for all your support.
  • ameerk313
    I fixed the issue by wrapping the http.GetContextAsync() around a Task.Run
    var context = await http.GetContextAsync();

    i.e.
    var context = Task.Run(async () => await http.GetContextAsync()).Result;
Sign In or Register to comment.