문제

I'm stuck trying to access a specific Google drive account from a MVC app. All I need is for the MVC web app to access my google drive scan for a few files and alter the database based on the contents of the google drive. The problem is when running in IIS the drive cannot be authenticated as GoogleWebAuthorizationBroker tries to open browser if its a windows app but doesn't seem to be able to do that through IIS and even if it did it would be server side.

Ideally I would not have to authenticate this app at all, but if it has do go through that then how do I make it work in IIS?

UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
    new ClientSecrets
            {
                ClientId = "MY_ID",
                ClientSecret = "My_Secret"
            },
            new[] { DriveService.Scope.Drive },
            "user",
            CancellationToken.None, dataStore: new FileDataStore(Server.MapPath("~/app_data/googledata"))).Result;
도움이 되었습니까?

해결책

I got this to work, was able to enable the web site to access Google drive using my account without asking users to login or authorize.

First of all, follow this link to get Google API work with MVC:

https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#web_applications

There is a problem in the Sample code, in HomeController

 public async Task IndexAsync(CancellationToken cancellationToken)

Should be:

 public async Task<ActionResult> IndexAsync(CancellationToken cancellationToken)

After that, I created a MemoryDataStore (see code at the end) that is a slightly modification from the MemoryDataStore posted here:

http://conficient.wordpress.com/2014/06/18/using-google-drive-api-with-c-part-2/

Once you do that, capture the refresh token of the account you are using, and replace the store with this store when authenticate:

    private static readonly IAuthorizationCodeFlow flow =
        new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
            {
                ClientSecrets = new ClientSecrets
                {
                    ClientId = clientID,
                    ClientSecret = clientSecret
                },
                Scopes = new[] { DriveService.Scope.Drive },
                //DataStore = new FileDataStore("Drive.Api.Auth.Store")
                DataStore = new GDriveMemoryDataStore(commonUser, refreshToken)
            });

Here commonUser is a predefined user id of your chosen. Please make sure to modify the GetUserID() method to return the same commonUser:

 public override string GetUserId(Controller controller)
    {
        return commonUser;
    }

Once this is done, Google drive will stop asking user to login and authorize the app.

Here is my MemoryDataStore code:

 /// <summary>
 /// Handles internal token storage, bypassing filesystem
 /// </summary>
internal class GDriveMemoryDataStore : IDataStore
 {
     private Dictionary<string, TokenResponse> _store;
     private Dictionary<string, string> _stringStore;

     //private key password: notasecret

     public GDriveMemoryDataStore()
     {
         _store = new Dictionary<string, TokenResponse>();
         _stringStore = new Dictionary<string, string>();
     }

     public GDriveMemoryDataStore(string key, string refreshToken)
     {
         if (string.IsNullOrEmpty(key))
             throw new ArgumentNullException("key");
         if (string.IsNullOrEmpty(refreshToken))
             throw new ArgumentNullException("refreshToken");

         _store = new Dictionary<string, TokenResponse>();

         // add new entry
         StoreAsync<TokenResponse>(key,
             new TokenResponse() { RefreshToken = refreshToken, TokenType = "Bearer" }).Wait();
     }

     /// <summary>
     /// Remove all items
     /// </summary>
     /// <returns></returns>
     public async Task ClearAsync()
     {
         await Task.Run(() =>
         {
             _store.Clear();
             _stringStore.Clear();
         });
     }

     /// <summary>
     /// Remove single entry
     /// </summary>
     /// <typeparam name="T"></typeparam>
     /// <param name="key"></param>
     /// <returns></returns>
     public async Task DeleteAsync<T>(string key)
     {
         await Task.Run(() =>
         {
            // check type
             AssertCorrectType<T>();

             if (typeof(T) == typeof(string))
             {
                 if (_stringStore.ContainsKey(key))
                     _stringStore.Remove(key);                 
             }
             else if (_store.ContainsKey(key))
             {
                 _store.Remove(key);
             }
         });
     }

     /// <summary>
     /// Obtain object
     /// </summary>
     /// <typeparam name="T"></typeparam>
     /// <param name="key"></param>
     /// <returns></returns>
     public async Task<T> GetAsync<T>(string key)
     {
         // check type
         AssertCorrectType<T>();

         if (typeof(T) == typeof(string))
         {
             if (_stringStore.ContainsKey(key))
                 return await Task.Run(() => { return (T)(object)_stringStore[key]; });
         }
         else if (_store.ContainsKey(key))
         {
             return await Task.Run(() => { return (T)(object)_store[key]; });
         }
         // key not found
         return default(T);
     }

     /// <summary>
     /// Add/update value for key/value
     /// </summary>
     /// <typeparam name="T"></typeparam>
     /// <param name="key"></param>
     /// <param name="value"></param>
     /// <returns></returns>
     public Task StoreAsync<T>(string key, T value)
     {
         return Task.Run(() =>
         {
             if (typeof(T) == typeof(string))
             {
                 if (_stringStore.ContainsKey(key))
                     _stringStore[key] = (string)(object)value;
                 else
                     _stringStore.Add(key, (string)(object)value);
             } else
             {
                 if (_store.ContainsKey(key))
                     _store[key] = (TokenResponse)(object)value;
                 else
                     _store.Add(key, (TokenResponse)(object)value);
             }
         });
     }

     /// <summary>
     /// Validate we can store this type
     /// </summary>
     /// <typeparam name="T"></typeparam>
     private void AssertCorrectType<T>()
     {
         if (typeof(T) != typeof(TokenResponse) && typeof(T) != typeof(string)) 
             throw new NotImplementedException(typeof(T).ToString());
     }
 }

다른 팁

I'm not familiar with C#, but the generic OAuth answer is that you need to request a refresh token (once only and you can do this in the OAuth playground), then store/embed that token somewhere in your server so you can use it to request an access token whenever your server app needs to access Drive.

See How do I authorise an app (web or installed) without user intervention? (canonical ?) for details on how to do this. You will of course either need to reverse engineer how the C# library stores its tokens, or create/modify an equivalent which uses the manually obtained refresh token.

I suspect what you are looking for is a Service account. A service account will allow you to set up your application to access your google drive with out requiring you to Autenticate.

Google APIs also support Service accounts. Unlike the scenario in which a client application requests access to an end-user's data, service accounts provide access to the client application's own data.

You can find Google's documentation on how to implement a service account here. Google APIs Client Library for .NET : Service Account

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top