Skip to content

Instantly share code, notes, and snippets.

@myroot
Created July 11, 2019 05:58
Show Gist options
  • Save myroot/d998b80dbf52ad7632dfe519ca992fdc to your computer and use it in GitHub Desktop.
Save myroot/d998b80dbf52ad7632dfe519ca992fdc to your computer and use it in GitHub Desktop.
How to use google api client libraries in Tizen.NET with Xamarin.forms
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="GoogleOAuthTest.EmbeddedBrowser">
<ContentPage.Content>
<WebView x:Name="Broswer" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"/>
</ContentPage.Content>
<ContentPage.ToolbarItems>
<ToolbarItem Text="Cancel" Order="Secondary" Clicked="OnCancel"/>
</ContentPage.ToolbarItems>
</ContentPage>
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace GoogleOAuthTest
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class EmbeddedBrowser : ContentPage, IEmbeddedBrowser
{
public EmbeddedBrowser(string title)
{
Title = title;
InitializeComponent();
}
public event EventHandler Cancelled;
public void Start(string url)
{
Broswer.Source = url;
}
protected override bool OnBackButtonPressed()
{
return true;
}
private void OnCancel(object sender, EventArgs e)
{
Cancelled?.Invoke(this, EventArgs.Empty);
}
}
}
var clientSecrets = new ClientSecrets
{
ClientId = "client id",
ClientSecret = "client secret"
};
var browser = new EmbeddedBrowser("Login to google");
browser.Cancelled += (s, evt) => { Navigation.PopAsync(); };
var unused = Navigation.PushAsync(browser);
var userCredential = await GoogleWebAuthorizationBroker.AuthorizeAsync(clientSecrets, new[] { DriveService.Scope.Drive }, "user", CancellationToken.None,
codeReceiver : new TizenLocalServerCodeReceiver { EmbeddedBrowser = browser });
unused = Navigation.PopAsync();
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Requests;
using Google.Apis.Auth.OAuth2.Responses;
using Google.Apis.Logging;
using System;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace GoogleOAuthTest
{
public interface IEmbeddedBrowser
{
event EventHandler Cancelled;
void Start(string url);
}
class ConsoleLogger : ILogger
{
public bool IsDebugEnabled => false;
public void Debug(string message, params object[] formatArgs)
{
Console.WriteLine(message, formatArgs);
}
public void Error(Exception exception, string message, params object[] formatArgs)
{
Debug(message, formatArgs);
}
public void Error(string message, params object[] formatArgs)
{
Debug(message, formatArgs);
}
public ILogger ForType(Type type)
{
return this;
}
public ILogger ForType<T>()
{
return this;
}
public void Info(string message, params object[] formatArgs)
{
Debug(message, formatArgs);
}
public void Warning(string message, params object[] formatArgs)
{
Debug(message, formatArgs);
}
}
public class TizenLocalServerCodeReceiver : ICodeReceiver
{
private static readonly ILogger Logger = new ConsoleLogger();
/// <summary>The call back request path.</summary>
internal const string LoopbackCallbackPath = "/authorize/";
/// <summary>Localhost callback URI, expects a port parameter.</summary>
internal static readonly string CallbackUriTemplateLocalhost = $"http://localhost:{{0}}{LoopbackCallbackPath}";
/// <summary>127.0.0.1 callback URI, expects a port parameter.</summary>
internal static readonly string CallbackUriTemplate127001 = $"http://127.0.0.1:{{0}}{LoopbackCallbackPath}";
/// <summary>Close HTML tag to return the browser so it will close itself.</summary>
internal const string DefaultClosePageResponse =
@"<html>
<head><title>OAuth 2.0 Authentication Token Received</title></head>
<body>
Received verification code. You may now close this window.
<script type='text/javascript'>
// This doesn't work on every browser.
window.setTimeout(function() {
this.focus();
window.opener = this;
window.open('', '_self', '');
window.close();
}, 1000);
//if (window.opener) { window.opener.checkToken(); }
</script>
</body>
</html>";
private static object s_lock = new object();
// Current best-guess as to most suitable callback URI.
private static string s_callbackUriTemplate;
// Has a successful callback been received?
private static bool s_receivedCallback;
/// <summary>
/// Create an instance of <see cref="TizenLocalServerCodeReceiver"/>.
/// </summary>
public TizenLocalServerCodeReceiver() : this(DefaultClosePageResponse) { }
/// <summary>
/// Create an instance of <see cref="TizenLocalServerCodeReceiver"/>.
/// </summary>
/// <param name="closePageResponse">Custom close page response for this instance</param>
public TizenLocalServerCodeReceiver(string closePageResponse)
{
_closePageResponse = closePageResponse;
lock (s_lock)
{
// Listening on 127.0.0.1 is recommended, but can't be done in non-admin Windows 7 & 8.
// So use some tests/heuristics so maybe listen on localhost instead.
if (s_callbackUriTemplate != null && !s_receivedCallback)
{
// On non-first runs, if a successful callback has not been received
// then force callback to use localhost rather than 127.0.0.1
// This is to heuristically avoid errors on browsers that can't connect to 127.0.0.1
// E.g. IE11 in enhanced protection mode.
s_callbackUriTemplate = CallbackUriTemplateLocalhost;
}
if (s_callbackUriTemplate == null)
{
s_callbackUriTemplate = CallbackUriTemplate127001;
// No check required on NETStandard, it uses TcpListener which can only use IP adddresses, not DNS names.
}
// Set the instance field of which callback URI to use.
// An instance field is used to ensure any one instance of this class
// uses a consistent callback URI.
_callbackUriTemplate = s_callbackUriTemplate;
}
}
// Callback URI used for this instance.
private string _callbackUriTemplate;
// Close page response for this instance.
private readonly string _closePageResponse;
public IEmbeddedBrowser EmbeddedBrowser { get; set; }
// There is a race condition on the port used for the loopback callback.
// This is not good, but is now difficult to change due to RedirectUri and ReceiveCodeAsync
// being public methods.
private string redirectUri;
/// <inheritdoc />
public string RedirectUri
{
get
{
if (string.IsNullOrEmpty(redirectUri))
{
redirectUri = string.Format(_callbackUriTemplate, GetRandomUnusedPort());
}
return redirectUri;
}
}
/// <inheritdoc />
public async Task<AuthorizationCodeResponseUrl> ReceiveCodeAsync(AuthorizationCodeRequestUrl url,
CancellationToken taskCancellationToken)
{
var authorizationUrl = url.Build().AbsoluteUri;
// The listener type depends on platform:
// * .NET desktop: System.Net.HttpListener
// * .NET Core: LimitedLocalhostHttpServer (above, HttpListener is not available in any version of netstandard)
using (var listener = StartListener())
{
Logger.Debug("Open a browser with \"{0}\" URL", authorizationUrl);
bool browserOpenedOk;
try
{
browserOpenedOk = OpenBrowser(authorizationUrl);
}
catch (Exception e)
{
Logger.Error(e, "Failed to launch browser with \"{0}\" for authorization", authorizationUrl);
throw new NotSupportedException(
$"Failed to launch browser with \"{authorizationUrl}\" for authorization. See inner exception for details.", e);
}
if (!browserOpenedOk)
{
Logger.Error("Failed to launch browser with \"{0}\" for authorization; platform not supported.", authorizationUrl);
throw new NotSupportedException(
$"Failed to launch browser with \"{authorizationUrl}\" for authorization; platform not supported.");
}
var ret = await GetResponseFromListener(listener, taskCancellationToken).ConfigureAwait(false);
// Note that a successful callback has been received.
s_receivedCallback = true;
return ret;
}
}
/// <summary>Returns a random, unused port.</summary>
private static int GetRandomUnusedPort()
{
var listener = new TcpListener(IPAddress.Loopback, 0);
try
{
listener.Start();
return ((IPEndPoint)listener.LocalEndpoint).Port;
}
finally
{
listener.Stop();
}
}
private HttpListener StartListener()
{
var listener = new HttpListener();
listener.Prefixes.Add(RedirectUri);
listener.Start();
return listener;
}
private async Task<AuthorizationCodeResponseUrl> GetResponseFromListener(HttpListener listener, CancellationToken ct)
{
HttpListenerContext context;
// Set up cancellation. HttpListener.GetContextAsync() doesn't accept a cancellation token,
// the HttpListener needs to be stopped which immediately aborts the GetContextAsync() call.
using (ct.Register(listener.Stop))
{
// Wait to get the authorization code response.
try
{
context = await listener.GetContextAsync().ConfigureAwait(false);
}
catch (Exception) when (ct.IsCancellationRequested)
{
ct.ThrowIfCancellationRequested();
// Next line will never be reached because cancellation will always have been requested in this catch block.
// But it's required to satisfy compiler.
throw new InvalidOperationException();
}
}
NameValueCollection coll = context.Request.QueryString;
// Write a "close" response.
var bytes = Encoding.UTF8.GetBytes(_closePageResponse);
context.Response.ContentLength64 = bytes.Length;
context.Response.SendChunked = false;
context.Response.KeepAlive = false;
var output = context.Response.OutputStream;
await output.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
await output.FlushAsync().ConfigureAwait(false);
output.Close();
context.Response.Close();
// Create a new response URL with a dictionary that contains all the response query parameters.
return new AuthorizationCodeResponseUrl(coll.AllKeys.ToDictionary(k => k, k => coll[k]));
}
private bool OpenBrowser(string url)
{
EmbeddedBrowser.Start(url);
return true;
}
}
}
@myroot
Copy link
Author

myroot commented May 6, 2020

Tizen TV is not support webview

@Filliny
Copy link

Filliny commented May 6, 2020

Cool ) Have tried find where it has been described...
Next -> Oh! I will use Oauth2 natively! .... Hahahah - Deprecated!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment