Search code examples
c#asp.net-mvcauthenticationloopback

How to use loopback IP to retrieve auth token


Recently Google banned embedded browser auth in favor of Loopback IP approach

My site has a desktop client that uses the same login system as the web site. Basically I pop a embedded browser view to my website and grab the cookie after login is done. The auth cookie for my site is then used for API access by the desktop client.

Now that Google bans embedded browser I need a new approach for this (if I want to maintain Google external login).

If I understand it right then the new process would be something like
1. Desktop client listen to localhost::port
2. Desktop client launches Url to the website's login page and opens user's default browser
3. User login to the website using their favorite browser
4. web server detects login success and somehow sends a redirect with the auth token to localhost::port
5. Desktop client reads the token from localhost

But how can I show the login success page to instruct user to close the browser and return to the desktop app if the redirect URL is localhost? How should I implement this on both the server and desktop client?


Solution

  • Found two ways of doing this. One is to use TcpListener and bind to Loopback IP. The response comes back as stream which you would need to further parse it to get the data you want and is a huge pain.

    Another way is to use HttpListener

            using (HttpListener listener = new HttpListener())
            {
                listener.Prefixes.Add("http://localhost:{port}/"); //Only listen to this particular address
                listener.Start();
    
                //blocking call. Feel free to use async version
                HttpListenerContext context = listener.GetContext(); 
                HttpListenerRequest request = context.Request;
    
                HttpListenerResponse response = context.Response;
    
                //Here is the response url. The token should be inside url query param.
                Console.WriteLine(request.Url); 
    
                //redirects user back to your site and show a login success screen
                response.Redirect("{yourdomain}/loginsuccess");
                //Important! call close to send out the response
                response.Close();
    
                //Important! If listener is stopped before response is sent out then it will abort.
                Thread.Sleep(1000);
                listener.Stop();
            }
    

    On the server side simply redirect user to http://localhost:{port}/?token=xxxx after login is completed.

    asp.net implementation:

    In Startup.Auth.cs add a static field

    public static TicketDataFormat AccessTokenFormat;
    

    in ConfigureAuth do

    AccessTokenFormat = new TicketDataFormat(app.CreateDataProtector(typeof(OAuthAuthorizationServerMiddleware).Namespace, "Access_Token", "v1"));
    
    app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
    

    To generate a token

        TimeSpan tokenExpiration = TimeSpan.FromDays(1);
    
        ClaimsIdentity identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);
        identity.AddClaim(new Claim(ClaimTypes.Name, "a@a.com"));
        identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "999"));
    
        AuthenticationProperties props = new AuthenticationProperties()
        {
            IssuedUtc = DateTime.UtcNow,
            ExpiresUtc = DateTime.UtcNow.Add(tokenExpiration),
        };
    
        AuthenticationTicket ticket = new AuthenticationTicket(identity, props);
    
        string accessToken = Startup.AccessTokenFormat.Protect(ticket);
    

    With this method user wouldnt even notice that we have redirected he/she to localhost temperately. The browser's URL would show your site's domain the whole time.