First: I have seen several other threads talking about similar issues, however those threads answers simply pointed to the pages I used for reference in my code, so please do not kill this off simply because it looks similar.
I am getting the error "The type initializer for 'Models.AppFlowMetaData' threw an exception."
BUT only on the production IIS instance. This works fine on local, I have triple checked the return urls (this gives a different error anyway) the only thing I can think of is that the Auth is being blocked somehow. Google dev console shows no errors.
The error is occurring in the below code:
private static string[] scopes = { SheetsService.Scope.Spreadsheets, DriveService.Scope.Drive };
private static readonly IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer {
ClientSecrets = new ClientSecrets {
ClientId = "[App id].apps.googleusercontent.com",
ClientSecret = "[secret]" },
Scopes = scopes,
DataStore = new FileDataStore("Drive.Api.Auth.Store") });
public override IAuthorizationCodeFlow Flow { get { return flow; } }
The above code is based on the authentication example given on google's website here: Google API Auth for MVC
The above code is being called from:
private async Task<UserCredential> GetGoogleCreds()
{
Google.Apis.Auth.OAuth2.Web.AuthorizationCodeWebApp.AuthResult result;
try
{
var log = LogManager.GetCurrentClassLogger();
log.Info("GetGoogleCreds: getting temp appflowmetadata");
var temp = new Models.AppFlowMetaData();
result = await new AuthorizationCodeMvcApp(this, new Models.AppFlowMetaData()).AuthorizeAsync(new CancellationTokenSource().Token);
if (result.Credential == null)
{
LoggingIntoGoogle = null;
throw new ApiException(500, result.RedirectUri);
}
return result.Credential;
}
catch(ApiException ex)
{
throw ex;
}
catch(Exception ex)
{
ERROR(ref ex);
throw ex;
}
return null;
}
The APIException being thrown above tells the calling process Google has not been logged in yet, so it redirects to the Google OAuth result (when it can get that far).
Ok the issue here is the FileDataStore and the lack of Google actually documenting things.
If the filedatastore in the flow is left as is in the web example it will fail on a proper IIS server.
It tries to create a folder (the example indicates it uses google drive, but it doesn't) under the windows restricted folders (programdata etc.), this of course causes all kinds of errors on even a low-security server setup.
You must use Server.MapPath()
and map to a local website folder it will be able to write to such as app_data.
For the example above I got it working by letting google use a folder underneath App_Data by using an additional combine:
private static readonly IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer {
ClientSecrets = new ClientSecrets {
ClientId = "[client id].apps.googleusercontent.com",
ClientSecret = "[secret]" },
Scopes = scopes,
DataStore = new FileDataStore(Path.Combine(HttpContext.Current.Server.MapPath("~/App_Data/"), "FileDataStore")) });