#1.はじめに
XamarinのiOS/Androidでアプリを作成するのにGoogleのサービスにアクセスする必要があったが、Xamarin.AuthやNugetを使用してApiのライブラリを組み込むもエラーで動作しない。やり方を調べながらコードを書いたので、要点のみ記録に残しておく。ただし、抜けや間違えがあるかもしれない。また、かなりのやっつけコードである(笑)。
このコードを理解するには、GoogleのOAuth2についての知識がちょっと必要となる。
#2.ログイン
ログインはWebViewを使いGoogleのWeb画面を表示し、http://localhostにリダイレクトしてverificationCodeを取得する。
###2.1 iOSの場合
UIWebViewDelegateから派生したClassを作成してWebViewに設定します。何か処理がされると横取りできるので、そのタイミングでverificationCodeを取得する。
public class MyWebViewDelegate : UIWebViewDelegate
{
public MyWebViewDelegate() {}
public override bool ShouldStartLoad(UIWebView webview, NSUrlRequest requesturl, UIWebViewNavigationType navitype)
{
if (URLがlocalhostか判定(requesturl.Url)) {
var dic = Jsonから辞書形式に変換(requesturl.Url.Query);
if (dic.ContainsKey("code") == true) {
// 正常に認証された
verificationCode = dic["code"];
} else {
// エラー
if (dic.ContainsKey("error") == true) {
error = dic["error"];
}
return true;
}
}
}
}
###2.2 Android
WebViewClientから派生したClassを作成してWebViewに設定する(ほぼ、iOSを同様のコード)。
public class MyWebViewClient : WebViewClient
{
public MyWebViewClient(LoginWebViewControlRenderer owner) {}
public override void OnPageStarted (Android.Webkit.WebView view, string url, Android.Graphics.Bitmap favicon)
{
base.OnPageStarted (view, url, favicon);
if (URLがlocalhostか判定(uri))
{
view.StopLoading ();
var dic = Jsonから辞書形式に変換(uri.Query);
if (dic.ContainsKey("code") == true) {
// 正常に認証された
verificationCode = dic["code"];
} else {
// エラー
if (dic.ContainsKey("error") == true) {
error = dic["error"];
}
return true;
}
}
}
}
#3.AccessToken取得
項番3以降はPortableで作成するので、iOS/Androidで共通のコードとなる。項番2で取得したverify_codeを使用してサービスに必要なAccessTokenなど諸々のデータを取得する。
public static async Task<string> GetAccessToken(string verificationCode)
{
using (var client = new HttpClient())
{
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("client_id", ClientId),
new KeyValuePair<string, string>("client_secret", ClientSecret),
new KeyValuePair<string, string>("code", verificationCode),
new KeyValuePair<string, string>("redirect_uri", RedirectUrl),
new KeyValuePair<string, string>("grant_type", "authorization_code")
});
var result = client.PostAsync(GoogleAuthConsts.TokenUrl, content).Result;
return await result.Content.ReadAsStringAsync();
}
}
#4.AccessTokenのリフレッシュ
AccessTokenは一定時間が経過するとリフレッシュが必要みたいだ(GoogleApiの仕様を見ればのっているだろうけど、まずはアクセスできることが重要だったので後回し)。リフレッシュするタイミングは、項番3で取得する情報に含まれている。リフレッシュにはするには、RefreshTokenが必要である。これは、項番3に含まれている。なお、すでにログインは終わっている場合は、これを使用して最新のAccessTokenを得る。
public static async Task<string> GetRefreshAccessToken(string refresh_token)
{
using (var client = new HttpClient())
{
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("client_id", ClientId),
new KeyValuePair<string, string>("client_secret", ClientSecret),
new KeyValuePair<string, string>("refresh_token", refresh_token),
new KeyValuePair<string, string>("grant_type", "refresh_token")
});
var result = client.PostAsync(GoogleAuthConsts.AuthorizationUrl, content).Result;
return await result.Content.ReadAsStringAsync();
}
}
#5.GoogleApiでアクセス
NugetでGoogleのCalendarApiを追加しても、Googleが公開しているサンプルコードはWindows用なのでほぼ動かない。Googleの.netライブラリを使用してApiにアクセスするにか、ライブラリ固有の変数を初期化し設定する必要がある(UserCredentialの生成)。ヘルパー関数は、Windows用なので手動で設定していく。
class CustomMessageHandler : DelegatingHandler
{
public CustomMessageHandler() : this(new HttpClientHandler())
{
}
public CustomMessageHandler(HttpMessageHandler innerHandler) : base(innerHandler)
{
}
}
public class GoogleHttpClientFactory : HttpClientFactory
{
protected override HttpMessageHandler CreateHandler (CreateHttpClientArgs args)
{
return new CustomMessageHandler();
}
}
var token = new TokenResponse()
{
AccessToken = アクセストークン,
ExpiresInSeconds = 更新期限,
Issued = アクセストークンを取得したDateTime,
RefreshToken = リフレッシュトークン,
Scope = スコープ(カレンターAPI),
TokenType = トークンタイプ,
};
var httpclientfactory = new GoogleHttpClientFactory();
credential = new UserCredential(
new GoogleAuthorizationCodeFlow(
new GoogleAuthorizationCodeFlow.Initializer()
{
HttpClientFactory = httpclientfactory,
ClientSecrets = new ClientSecrets()
{
ClientId = クライアントID,
ClientSecret = クライアントシークレット
}
}
),
"user",
token);
#6.Serviceへアクセス
項番5の変数が生成できたら、Apiに設定して実際にアクセスする(この場合はカレンダーであるが、別のサービスでは別の変数が必要かもしれない)。
App.calservice = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "App Name",
HttpClientFactory = httpclientfactory
});
IList<CalendarListEntry> list = calservice.CalendarList.List().Execute().Items;
foreach (CalendarListEntry item in list)
{
Debug.WriteLine(item.Summary + ". Location: " + item.Location + ", TimeZone: " + item.TimeZone);
}