Search code examples
c#asp.net-mvcgoogle-drive-api

ASP.NET MVC website application to upload files with Google Drive API URI mismatch


I'm trying to upload files using the Google Drive API and am getting a URI mismatch error from Google when clicking the upload button on my page. The URI that Google shows isn't even a part of the website, nor is a URI that I supplied to Google, so I have no idea where it's coming from.

enter image description here

Here is the APIHelper class I created based off of this tutorial (which shows that the code should work on a website)

public class GoogleDriveAPIHelper
{
    //add scope
    public static string[] Scopes = { DriveService.Scope.Drive };

    //create Drive API service.
    public static DriveService GetService()
    {
        //get Credentials from client_secret.json file 
        UserCredential credential;
        //Root Folder of project
        var CSPath = System.Web.Hosting.HostingEnvironment.MapPath("~/");
        using (var stream = new FileStream(Path.Combine(CSPath, "client_secret.json"), FileMode.Open, FileAccess.Read))
        {
            string FolderPath = System.Web.Hosting.HostingEnvironment.MapPath("~/"); 
            string FilePath = Path.Combine(FolderPath, "DriveServiceCredentials.json");
            credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                GoogleClientSecrets.Load(stream).Secrets,
                Scopes,
                "user",
                CancellationToken.None,
                new FileDataStore(FilePath, true)).Result;
        }
        //create Drive API service.
        DriveService service = new DriveService(new BaseClientService.Initializer()
        {
            HttpClientInitializer = credential,
            ApplicationName = "Documents Uploader",
        });
        return service;
    }


    //file Upload to the Google Drive.
    public static void UploadFile(string folderID, HttpPostedFileBase file)
    {
        if (file != null && file.ContentLength > 0)
        {
            //create service
            DriveService service = GetService();
            string path = Path.Combine(HttpContext.Current.Server.MapPath("~/GoogleDriveFiles"),
            Path.GetFileName(file.FileName));
            file.SaveAs(path);
            var FileMetaData = new Google.Apis.Drive.v3.Data.File
            {
                Name = Path.GetFileName(file.FileName),
                MimeType = MimeMapping.GetMimeMapping(path),
                //id of parent folder 
                Parents = new List<string>
                {
                    folderID
                }
            };
            FilesResource.CreateMediaUpload request;
            using (var stream = new FileStream(path, FileMode.Open))
            {
                request = service.Files.Create(FileMetaData, stream, FileMetaData.MimeType);
                request.Fields = "id";
                request.Upload();
            }
        }
    }
}

And the post

[HttpPost]
    public ActionResult Index(HttpPostedFileBase file)
    {
        string folderID = "1L9QUUgmtg8KUdNvutQ1yncIwN_uLz4xs";
        if (TempData["Success"] == null)
        {
            // show all fields
            ViewBag.ShowForm = true;
            ViewBag.ShowButtons = false;
        }
        else
        {
            // hide all elements on the page for success message
            ViewBag.ShowForm = false;
            ViewBag.ShowButtons = true;
        }
        GoogleDriveAPIHelper.UploadFile(folderID, file);
        TempData["Success"] = "File successfully uploaded";
        return View();
    }

I have heard that the tutorial is referencing code that only works for standalone apps and not web apps, so it's odd that the screenshots in the tutorial are from a website. shrug I'll keep looking for tips and tricks, but in the meantime, I'm posting this to see if anyone else has written a site to upload through the Google drive to a specific folder, not the root. TIA!

Edit: Here are screenshots of the redirect URI I set up in the Google Cloud Console. Prod & localhost

enter image description here

enter image description here

Edit: Startup.Auth.cs - this is used for pass through ADFS authentication and has nothing to do with the Google Drive API

 private void ConfigureAuth(IAppBuilder app)
    {
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
        app.UseCookieAuthentication(
            new CookieAuthenticationOptions
            {
                // TempData and Owin don't get along, use this workaround to force a custom cookie manager
                // https://stackoverflow.com/questions/28559237/intermittent-redirection-loops-during-adfs-authentication
                CookieManager = new SystemWebCookieManager()
            });
        app.UseWsFederationAuthentication(
            new WsFederationAuthenticationOptions
            {
                Wtrealm = ConfigurationManager.AppSettings["ida:Wtrealm"],
                MetadataAddress = ConfigurationManager.AppSettings["ida:ADFSMetadata"]
            });
    }

The realm matches the URI in the Google console and the metadata is the same xml link I use in all my web apps that use ADFS pass through auth, which has worked flawlessly. Nothing in my web.config file mention the IP address that Google says is my redirect URI either.


Solution

  • The URI that Google shows isn't even a part of the website, nor is a URI that I supplied to Google, so I have no idea where it's coming from.

    The redirect uri is built buy the client library you are using. Your app is set to run http not https its running localhost and not hosted so its 127.0.0.1 the port is also either being randomly generated by your app or something that you have set up statically. the /authorize is attached again by the client library.

    The redirect uri is the location your code is prepared to accept the response from the authorization server. This URI needs to be configured in Google cloud console. The easiest solution is to copy it exactly and add it as a redirect uri in Google cloud console. Just make sure that your app is set to use a static port if the port changes its not going to work.

    This video will show you how to add it. Google OAuth2: How the fix redirect_uri_mismatch error. Part 2 server sided web applications.

    Web applications

    public void ConfigureServices(IServiceCollection services)
    {
        ...
    
        // This configures Google.Apis.Auth.AspNetCore3 for use in this app.
        services
            .AddAuthentication(o =>
            {
                // This forces challenge results to be handled by Google OpenID Handler, so there's no
                // need to add an AccountController that emits challenges for Login.
                o.DefaultChallengeScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
                // This forces forbid results to be handled by Google OpenID Handler, which checks if
                // extra scopes are required and does automatic incremental auth.
                o.DefaultForbidScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
                // Default scheme that will handle everything else.
                // Once a user is authenticated, the OAuth2 token info is stored in cookies.
                o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            })
            .AddCookie()
            .AddGoogleOpenIdConnect(options =>
            {
                options.ClientId = {YOUR_CLIENT_ID};
                options.ClientSecret = {YOUR_CLIENT_SECRET};
            });
    }