1

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.

Jamie
  • 1,579
  • 8
  • 34
  • 74

1 Answers1

1

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};
        });
}
Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449
  • My web app is set up to use https. I have to use SSL for all web apps because I authenticate through an ADFS server first. The relying party trusts I added for live and localhost don't mention that IP address either, so I'm still unsure where the mismatch is coming from. :( – Jamie Feb 01 '22 at 19:22
  • its not your app is running at http://127.0.0.1:port/authorize/ The library picks up the settings from your app. Just take the exact redirect uri it set and use that as a redirect urin in google cloud console. – Linda Lawton - DaImTo Feb 01 '22 at 19:53
  • I can't set the redirects in Google as IP addresses, it won't let me. `Invalid Redirect: must end with a public top-level domain (such as .com or .org).` `Invalid Redirect: must use a domain that is a valid top private domain. ` – Jamie Feb 01 '22 at 22:32
  • Thats probably because your project is set to production. You need to have your app running up on a web server and use the domain. at which point you will get http:// yourdomain/authorize | localhost only works when you are developing this locally. I think this all comes down to the settings in your application. are you sure you have ASPNETCORE_URLS set properly. – Linda Lawton - DaImTo Feb 02 '22 at 10:42
  • In production I get an access is denied error. I'm sorry I should have mentioned that. I thought it would be easier to get localhost running first before mentioning it doesn't work on the server either. It keeps referencing that IP address. I don't use Core though, I use MVC, so I'm not sure what ASPNETCORE_URLS are. – Jamie Feb 02 '22 at 14:26
  • `[NotSupportedException: Failed to launch browser with "https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&response_type=code&client_id=XXXXX.apps.googleusercontent.com&redirect_uri=http%3A%2F%2F127.0.0.1%3A55186%2Fauthorize%2F&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive" for authorization. See inner exception for details.]` – Jamie Feb 02 '22 at 14:27
  • failed to launch browser means you are using code for an installed app on a server and its trying to open the consent screen on the server were it doesn't have access to do that nor is there anyone to see it. this is a step backwards. use the web authorization code unless this is a desktop app – Linda Lawton - DaImTo Feb 03 '22 at 07:29
  • I updated the web app to use `AppFlowMetadata` and can now get to the Google authorization screen. I still get a URI mismatch because Google thinks I need to go outside of the root folder in the URI I supply them, which is obviously wrong, but I'm not sure where in the code it's telling Google this. `redirect_uri: https://XXXXX.edu/AuthCallback/IndexAsync` when there should be a folder between `.edu` and `AuthCallback` – Jamie Feb 03 '22 at 19:41
  • AuthCallback/IndexAsync has something to do with Asp .net core authorization. Are you sure your following the authorization for this client library? https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#web-applications-asp.net-core-3 – Linda Lawton - DaImTo Feb 04 '22 at 11:52
  • I'm using MVC, not Core. I used this one: https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#web-applications-asp.net-mvc But now I see that I've been trying to use v3 and not v2, so thanks for the link! I'll be updating today to try and get this buttoned up. – Jamie Feb 04 '22 at 14:31
  • You should use drive v3 not drive v2 – Linda Lawton - DaImTo Feb 04 '22 at 15:29
  • But if I'm not using Core, why would I use v3? From what I can tell v3 is for Core and v2 is for MVC. Is that not accurate? – Jamie Feb 04 '22 at 16:13
  • I don't know, I've spent all week trying to figure out where my code is telling Google that the authcallback is outside of the root folder, but I have no idea. I think I'll just give up and try something else next week. This is ridiculous. :( – Jamie Feb 04 '22 at 23:26
  • The version of Google drive api has no relation to the version of .net. Actually its not ridiculous. The issue you are having is you are using a very old version of .net you should consider using .net 5 or .net 6 and then you would have it working out of the box. I haven't personally done what you are trying to do in the last six years because its out dated. I could proably get it working for you but i dont feel like installing a six year old version of .net which will be EOL in three months. – Linda Lawton - DaImTo Feb 05 '22 at 13:25
  • Ok, thank you for all your help and guidance. Once I have time, I'll sit down and figure out how to use Core for future apps and update my pass through authentication. – Jamie Feb 07 '22 at 14:29