2016-07-07 5 views
3
var auth = new OAuth2Authenticator(clientId: "my client id", 
     scope: "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/calendar", 
     authorizeUrl: new Uri("https://accounts.google.com/o/oauth2/auth"), 
     redirectUrl: new Uri("https://www.googleapis.com/plus/v1/people/me"), 
     getUsernameAsync: null); 
     auth.AllowCancel = allowCancel; 
     List<Event> events = new List<Event>(); 

     auth.Completed += async (sender, e) => 
     { 
      if (!e.IsAuthenticated) 
      { 
       Toast.MakeText(_activity, "Fail to authenticate!", ToastLength.Short).Show(); 
       return; 
      } 

      string access_token; 
      e.Account.Properties.TryGetValue("access_token", out access_token); 

アクセストークンを取得するのと同じ方法でリフレッシュトークンを取得しようとすると、動作しません。xamarin.authを使用してgoogle apiの更新トークンを取得する

アクセストークンを取得できましたが、リフレッシュトークンを取得できませんでした。アクセス権を初めて取得するときに更新トークンを取得するだけで、アクセストークンは引き続き取得できないことを読んでいるため、Googleアカウントからの承認を削除しようとしました。私はまた、access_type = offlineとapproval_prompt = forceをリクエストに追加する必要があることを読んだが、xamarin.authを使ってどこに追加するか分からない。

答えて

0

VSで検索するときに表示されるデフォルトのパッケージは、「Xamarin Auth 1.3.0」です。これはリフレッシュトークンの取得をサポートしていません。

私はOAuth2Authenticatorの独自のバージョンを作成することでこれを解決し、非常に限られた努力で同じ目標に達しました。安全に保存する必要がリフレッシュトークンとAccountStoreで

var auth = new RefreshOAuth2Authenticator(
       clientId: "client ID", 
       clientSecret: "client Secret", 
       scope: "scope", // just 'openid' in my case 
       authorizeUrl: new Uri("https://accounts.google.com/o/oauth2/v2/auth"), 
       redirectUrl: new Uri("your redirect url"), 
       accessTokenUrl: new Uri("https://www.googleapis.com/oauth2/v4/token")); 

auth.GetUI(); 

この呼び出しの結果を次のようにLoginPageRendererこのことから

public class RefreshOAuth2Authenticator : WebRedirectAuthenticator 

{ 
    string clientId; 
    string clientSecret; 
    string scope; 
    Uri authorizeUrl; 
    Uri accessTokenUrl; 
    Uri redirectUrl; 
    GetUsernameAsyncFunc getUsernameAsync; 

    string requestState; 
    bool reportedForgery; 

    public string ClientId 
    { 
     get { return clientId; } 
    } 

    public string ClientSecret 
    { 
     get { return clientSecret; } 
    } 

    public string Scope 
    { 
     get { return scope; } 
    } 

    public Uri AuthorizeUrl 
    { 
     get { return authorizeUrl; } 
    } 

    public Uri RedirectUrl 
    { 
     get { return redirectUrl; } 
    } 

    public Uri AccessTokenUrl 
    { 
     get { return accessTokenUrl; } 
    } 

    public RefreshOAuth2Authenticator(string clientId, string scope, Uri authorizeUrl, Uri redirectUrl, GetUsernameAsyncFunc getUsernameAsync = null) 
     : this (redirectUrl) 
    { 
     if (string.IsNullOrEmpty(clientId)) 
     { 
      throw new ArgumentException("clientId must be provided", nameof(clientId)); 
     } 
     this.clientId = clientId; 

     this.scope = scope ?? ""; 

     if (authorizeUrl == null) 
     { 
      throw new ArgumentNullException(nameof(authorizeUrl)); 
     } 
     this.authorizeUrl = authorizeUrl; 

     this.getUsernameAsync = getUsernameAsync; 

     this.redirectUrl = redirectUrl; 

     accessTokenUrl = null; 
    } 

    public RefreshOAuth2Authenticator(string clientId, string clientSecret, string scope, Uri authorizeUrl, Uri redirectUrl, Uri accessTokenUrl, GetUsernameAsyncFunc getUsernameAsync = null) 
     : this (redirectUrl, clientSecret, accessTokenUrl) 
    { 
     if (string.IsNullOrEmpty(clientId)) 
     { 
      throw new ArgumentException("clientId must be provided", nameof(clientId)); 
     } 
     this.clientId = clientId; 

     if (string.IsNullOrEmpty(clientSecret)) 
     { 
      throw new ArgumentException("clientSecret must be provided", nameof(clientSecret)); 
     } 
     this.clientSecret = clientSecret; 

     this.scope = scope ?? ""; 

     if (authorizeUrl == null) 
     { 
      throw new ArgumentNullException(nameof(authorizeUrl)); 
     } 
     this.authorizeUrl = authorizeUrl; 

     if (accessTokenUrl == null) 
     { 
      throw new ArgumentNullException(nameof(accessTokenUrl)); 
     } 
     this.accessTokenUrl = accessTokenUrl; 

     this.redirectUrl = redirectUrl; 

     this.getUsernameAsync = getUsernameAsync; 
    } 

    RefreshOAuth2Authenticator(Uri redirectUrl, string clientSecret = null, Uri accessTokenUrl = null) 
     : base (redirectUrl, redirectUrl) 
    { 
     this.clientSecret = clientSecret; 

     this.accessTokenUrl = accessTokenUrl; 

     this.redirectUrl = redirectUrl; 

     // 
     // Generate a unique state string to check for forgeries 
     // 
     var chars = new char[16]; 
     var rand = new Random(); 
     for (var i = 0; i < chars.Length; i++) 
     { 
      chars[i] = (char)rand.Next('a', 'z' + 1); 
     } 
     requestState = new string(chars); 
    } 

    bool IsImplicit { get { return accessTokenUrl == null; } } 

    public override Task<Uri> GetInitialUrlAsync() 
    { 
     var url = new Uri(string.Format(
      "{0}?client_id={1}&redirect_uri={2}&response_type={3}&scope={4}&state={5}&access_type=offline&prompt=consent", 
      authorizeUrl.AbsoluteUri, 
      Uri.EscapeDataString(clientId), 
      Uri.EscapeDataString(RedirectUrl.AbsoluteUri), 
      IsImplicit ? "token" : "code", 
      Uri.EscapeDataString(scope), 
      Uri.EscapeDataString(requestState))); 

     var tcs = new TaskCompletionSource<Uri>(); 
     tcs.SetResult(url); 
     return tcs.Task; 
    } 

    public async virtual Task<IDictionary<string, string>> RequestRefreshTokenAsync(string refreshToken) 
    { 
     var queryValues = new Dictionary<string, string> 
     { 
      {"refresh_token", refreshToken}, 
      {"client_id", this.ClientId}, 
      {"grant_type", "refresh_token"} 
     }; 

     if (!string.IsNullOrEmpty(this.ClientSecret)) 
     { 
      queryValues["client_secret"] = this.ClientSecret; 
     } 

     try 
     { 
      var accountProperties = await RequestAccessTokenAsync(queryValues).ConfigureAwait(false); 

      this.OnRetrievedAccountProperties(accountProperties); 

      return accountProperties; 
     } 
     catch (Exception e) 
     { 
      OnError(e); 

      throw; // maybe don't need this? this will throw the exception in order to maintain backward compatibility, but maybe could just return -1 or something instead? 
     } 
    } 

    protected override void OnPageEncountered(Uri url, IDictionary<string, string> query, IDictionary<string, string> fragment) 
    { 
     var all = new Dictionary<string, string>(query); 
     foreach (var kv in fragment) 
      all[kv.Key] = kv.Value; 

     // 
     // Check for forgeries 
     // 
     if (all.ContainsKey("state")) 
     { 
      if (all["state"] != requestState && !reportedForgery) 
      { 
       reportedForgery = true; 
       OnError("Invalid state from server. Possible forgery!"); 
       return; 
      } 
     } 

     // 
     // Continue processing 
     // 
     base.OnPageEncountered(url, query, fragment); 
    } 

    protected override void OnRedirectPageLoaded(Uri url, IDictionary<string, string> query, IDictionary<string, string> fragment) 
    { 
     // 
     // Look for the access_token 
     // 
     if (fragment.ContainsKey("access_token")) 
     { 
      // 
      // We found an access_token 
      // 
      OnRetrievedAccountProperties(fragment); 
     } 
     else if (!IsImplicit) 
     { 
      // 
      // Look for the code 
      // 
      if (query.ContainsKey("code")) 
      { 
       var code = query["code"]; 
       RequestAccessTokenAsync(code).ContinueWith(task => 
       { 
        if (task.IsFaulted) 
        { 
         OnError(task.Exception); 
        } 
        else { 
         OnRetrievedAccountProperties(task.Result); 
        } 
       }, TaskScheduler.FromCurrentSynchronizationContext()); 
      } 
      else { 
       OnError("Expected code in response, but did not receive one."); 
       return; 
      } 
     } 
     else { 
      OnError("Expected access_token in response, but did not receive one."); 
      return; 
     } 
    } 

    Task<IDictionary<string, string>> RequestAccessTokenAsync(string code) 
    { 
     var queryValues = new Dictionary<string, string> { 
      { "grant_type", "authorization_code" }, 
      { "code", code }, 
      { "redirect_uri", RedirectUrl.AbsoluteUri }, 
      { "client_id", clientId }, 
     }; 
     if (!string.IsNullOrEmpty(clientSecret)) 
     { 
      queryValues["client_secret"] = clientSecret; 
     } 

     return RequestAccessTokenAsync(queryValues); 
    } 

    protected Task<IDictionary<string, string>> RequestAccessTokenAsync(IDictionary<string, string> queryValues) 
    { 
     var query = queryValues.FormEncode(); 

     var req = WebRequest.Create(accessTokenUrl); 
     req.Method = "POST"; 
     var body = Encoding.UTF8.GetBytes(query); 
     req.ContentLength = body.Length; 
     req.ContentType = "application/x-www-form-urlencoded"; 
     using (var s = req.GetRequestStream()) 
     { 
      s.Write(body, 0, body.Length); 
     } 
     return req.GetResponseAsync().ContinueWith(task => 
     { 
      var text = task.Result.GetResponseText(); 

      // Parse the response 
      var data = text.Contains("{") ? WebEx.JsonDecode(text) : WebEx.FormDecode(text); 

      if (data.ContainsKey("error")) 
      { 
       throw new AuthException("Error authenticating: " + data["error"]); 
      } 
      else if (data.ContainsKey("access_token")) 
      { 
       return data; 
      } 
      else { 
       throw new AuthException("Expected access_token in access token response, but did not receive one."); 
      } 
     }); 
    } 

    protected virtual void OnRetrievedAccountProperties(IDictionary<string, string> accountProperties) 
    { 
     // 
     // Now we just need a username for the account 
     // 
     if (getUsernameAsync != null) 
     { 
      getUsernameAsync(accountProperties).ContinueWith(task => 
      { 
       if (task.IsFaulted) 
       { 
        OnError(task.Exception); 
       } 
       else { 
        OnSucceeded(task.Result, accountProperties); 
       } 
      }, TaskScheduler.FromCurrentSynchronizationContext()); 
     } 
     else { 
      OnSucceeded("", accountProperties); 
     } 
    } 

} 

が呼び出されます。

私は新しいid_tokenを取得したいたびに、私はコールする必要があります。

await auth.RequestRefreshTokenAsync("Refresh Token") 

あなたはexpire_tsをチェックして、トークンの値を格納するの周りにいくつかのより多くのロジックが必要になります、しかし、上記のコードは、あなたを取得する必要があります行く。

注1:Xamarin.Authのブランチがあり、今では試したことのないリフレッシュトークン https://github.com/xamarin/Xamarin.Auth/tree/portable-bait-and-switchをサポートしています。

注2:私は、クライアントの秘密がセキュリティ上の理由から、アプリ内に保存されていることを認識しています。しかし、Googleはインストールされた資格情報の使用をサポートしていないため、現時点ではこれを回避する方法はありません。

関連する問題