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?
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.