문제

Our app needs to let users sync sync their Google calendar on to our internal events module but I'm stuck on doing the authorization and storing it on to our DB. I was following this sample Google OAuth for reference but in that sample it was stated to implement your own DataStore that uses EF, I went ahead and tried it but I can't seem to make it to work and I've been stuck for a couple of weeks now, any help will be greatly appreciated. Thanks!

I had something like this before:

public class TokenDataStore : IDataStore
{
    private readonly IUnitOfWork _unitOfWork;
    private readonly IUserRepository _userRepository;
    private readonly IBusinessRepository _businessRepository;

    public TokenDataStore(IUnitOfWork unitOfWork, IUserRepository userRepository, IBusinessRepository businessRepository)
    {
        this._unitOfWork = unitOfWork;
        this._userRepository = userRepository;
        this._businessRepository = businessRepository;
    }

    //Todo: Add IdataStore Members

    public static string GenerateStoredKey(string key, Type t)
    {
        return string.Format("{0}-{1}", t.FullName, key);
    }
}

Some more information: - MVC 4 -.Net 4.5 - EF 5 - Google APIs

EDIT

Ok, so I made some progress this past couple of hours, I was finally able to make my own AppFlowMetaData

private readonly IAuthorizationCodeFlow flow;
    private readonly GoogleAPI _API;
    private readonly IGoogleAPIRepository _googleAPIRepository;
    private readonly IUnitOfWork _unitOfWork;

    public AppFlowMetadata(GoogleAPI API, IGoogleAPIRepository googleAPIRepository, IUnitOfWork unitOfWork)
    {
        this._API = API;
        this._googleAPIRepository = googleAPIRepository;
        this._unitOfWork = unitOfWork;

        this.flow =
        new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
        {
            ClientSecrets = new ClientSecrets
            {
                ClientId = ConfigurationManager.AppSettings["GP_Key"],
                ClientSecret = ConfigurationManager.AppSettings["GP_Secret"]
            },
            Scopes = new[] { CalendarService.Scope.Calendar },
            DataStore = new TokenDataStore(_API, _googleAPIRepository, _unitOfWork)
        });
    }

also my own data store class:

private readonly IGoogleAPIRepository _googleAPIRepository;
    private readonly IUnitOfWork _unitOfWork;
    private readonly GoogleAPI _model;

    public TokenDataStore(GoogleAPI model, IGoogleAPIRepository googleAPIRepository, IUnitOfWork unitOfWork)
    {
        this._googleAPIRepository = googleAPIRepository;
        this._unitOfWork = unitOfWork;
        this._model = model;
    }

Now I encounter a problem during the call back. So, when a user signs in using their account, in my data store class, I save the token as a string in the database and when the code gets to the part where I get the token from my model the data type passed is a string and not the usual Token Response. Here is the complete code for my data store:

public class TokenDataStore : IDataStore
{
    private readonly IGoogleAPIRepository _googleAPIRepository;
    private readonly IUnitOfWork _unitOfWork;
    private readonly GoogleAPI _model;

    public TokenDataStore(GoogleAPI model, IGoogleAPIRepository googleAPIRepository, IUnitOfWork unitOfWork)
    {
        this._googleAPIRepository = googleAPIRepository;
        this._unitOfWork = unitOfWork;
        this._model = model;
    }

    public System.Threading.Tasks.Task StoreAsync<T>(string key, T value)
    {
        var serialized = NewtonsoftJsonSerializer.Instance.Serialize(value);
        if(serialized.Contains("access_token"))
        {
            _model.TokenString = serialized;

            _googleAPIRepository.Save(_model, EntityState.Modified);
            _unitOfWork.Commit();
        }
        return TaskEx.Delay(0);
    }

    public System.Threading.Tasks.Task DeleteAsync<T>(string key)
    {
        _model.TokenString = "";

        _googleAPIRepository.Save(_model, EntityState.Modified);
        _unitOfWork.Commit();

        return TaskEx.Delay(0);
    }

    public Task<T> GetAsync<T>(string key)
    {
        TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
        try
        {
            tcs.SetResult(NewtonsoftJsonSerializer.Instance.Deserialize<T>(_model.TokenString));
        }
        catch(Exception ex)
        {
            tcs.SetException(ex);
        }

        return tcs.Task;
    }

    public System.Threading.Tasks.Task ClearAsync()
    {
        return TaskEx.Delay(0);
    }
}
도움이 되었습니까?

해결책

Kevin, it is not clear what isn't working..

Could be:

  1. Tokens are not being stored in your database
  2. Authentication is not happening when accessing users resources at later time

I assume it is the latter, so there are two challenges you need to fix:

  1. Token expiration is making Database invalid
  2. Tokens are not being fed back into googleauthenticator when accessing users resources

Things you need to do are to ensure you allow the access to be offline so the server has access to the resources while the user is not present or session out of scope. Read chapter "offline access" here how to do this: Using oAuth for WebApplications

Make sure you validate that the tokens are still valid when feeding them back into the authenticator. If not you need to use refresh token to renew authentication. Validating token

You will need to ensure the googleauthenticator is called with the tokens from you Database to ensure your code has access.

Hope I assumed the correct problem... ;)

다른 팁

You can use the FileDataStore (https://code.google.com/p/google-api-dotnet-client/source/browse/Src/GoogleApis.DotNet4/Apis/Util/Store/FileDataStore.cs), that stores its data in files and not EntityFramework.

Regarding your question - I didn't get it, what is your exact problem? You need more details on how to inject that data store into your flow object? Or you help with EF? ... Please provide more information.

Try storing an object with the following parameters in your Entity Framework Repository.

    public string access_token { get; set; }
    public string token_type { get; set; }
    public long? expires_in { get; set; }
    public string refresh_token { get; set; }
    public string Issued { get; set; }

I'm retrieving my tokens from an Web Api which contains an Entity Framework Repository. I retrieve my object as a JSON string. Use Google.Api.Json.NewtonsoftJsonSerializer to deserialize the Json string.

    public Task<T> GetAsync<T>(string key)
    {
        TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
        try
        {
            string dataStoreView = this.httpClient.Get(uri).ResponseBody;

            tcs.SetResult(Google.Apis.Json.NewtonsoftJsonSerializer.Instance.Deserialize<T>(dataStoreView));
        }
        catch (Exception ex)
        {
            tcs.SetException(ex);
        }

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