Wednesday, January 29, 2014

ASP.NET MVC With Google OpenID and OAuth 2.0

Several days ago I encountered Rick Anderson's great Create an ASP.NET MVC 5 App with Facebook and Google OAuth2 and OpenID Sign-on post.
While reading the blog post, it became apparent to me that there should be a similar example showing how to Sign-in with Google and access Google APIs within an MVC 5 application. I chose to write a simple sample to display the user's files form his or her Google Drive account using Google Drive API.

* All the code of this tutorial is available in https://github.com/peleyal/peleyal.git.

* UPDATE (Dec 2014) *
Google OpenID is deprecated please use OAuth 2.0 for accessing Google APIs.
More information about migrating OpenID to OAuth 2.0 is available at: https://developers.google.com/+/api/auth-migration.

Prerequisite:

The pre-requisites for this tutorial are the same as those in Rick's example.
First, install Visual Studio Express 2013 for Web or Visual Studio 2013.
Next, create a new ASP.NET MVC application (I chose to call my application GoogleApis.Sample.MVC) with the default individual user account.
Continue as described in the article. Make sure to uncomment the following line:  app.UseGoogleAuthentication();
It's located in ConfigureAuth method in App_Start\Startup.Auth.cs file)

Let's start:

1. Install NuGet packages



2. OAuth 2.0

Follow the OAuth 2.0 page for ASP.NET MVC applications. I'll repeat the key instructions in this section.

2.1 Create a new AppAuthFlowMetadata that inherits from FlowMetadata.

In my sample I added the following class to Controllers folder.

using System;
using System.Web.Mvc;
using Microsoft.AspNet.Identity;

using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Mvc;
using Google.Apis.Drive.v2;
using Google.Apis.Util.Store;

namespace Google.Apis.Sample.MVC.Controllers
{
    public class AppAuthFlowMetadata : FlowMetadata
    {
        private static readonly IAuthorizationCodeFlow flow =
            new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
                {
                    ClientSecrets = new ClientSecrets
                    {
                        ClientId = "PUT_CLIENT_ID_HERE",
                        ClientSecret = "PUT_CLIENT_SECRET_HERE"
                    },
                    Scopes = new[] { DriveService.Scope.Drive },
                    DataStore = new FileDataStore("Google.Apis.Sample.MVC")
                });

        public override string GetUserId(Controller controller)
        {
            return controller.User.Identity.GetUserName();
        }

        public override IAuthorizationCodeFlow Flow
        {
            get { return flow; }
        }
    }
}

  • Make sure to replace the ClientID and ClientSecret values with the client secrets from your Google Developers Cloud project.
  • Note: In my next blogpost I'll share a new implementation of EFDataStore to add support for Entity Framework as a storage mechanism.
  • I used the Microsoft.AspNew.Identity.GetUserName() extension method to get the user identity. Because I've configured the application to use the Google flow, the identity returned from this object corresponds to the user who authenticated to this site.

2.2 Create your Auth callback


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Google.Apis.Sample.MVC.Controllers
{
    public class AuthCallbackController
            Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController
    {
        protected override Google.Apis.Auth.OAuth2.Mvc.FlowMetadata FlowData
        {
            get { return new AppAuthFlowMetadata(); }
        }
    }
}

Later on, I'll create the service object for accessing the Google Drive API.

2.3 Register the Auth address into your Cloud project

Get the project URL from your project file inside VS2013. Then register that URI in your Google Developers Console project.



As you can see in the above images, I registered http://localhost:49244/AuthCallback/IndexAsync as a Redirect URI to my cloud project (Visual Studio picked 49244 port to my web application).

Make sure you also enable the Drive API in the APIs tab.

3. Get files from the DriveService

In this step we will create a DriveService instance and fetch all the user's files.

3.1 Create a model for Drive's File

Before we can retrieve information about the user's Drive files, we should specify a "local" model (It's just a best practice).

using System;

namespace Google.Apis.Sample.MVC.Models
{
    public class FileModel
    {
        public string Id { get; set; }
        public string Title getset; }
        public DateTime? CreatedDate getset; }
        public string DownloadUrl getset; }
    }
}

3.2 Add DriveAsync method to the HomeController

Start by adding the following using statements:

using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;

using Google.Apis.Sample.MVC.Models;
using Google.Apis.Auth.OAuth2.Mvc;
using Google.Apis.Download;
using Google.Apis.Drive.v2;
using Google.Apis.Services;

Next, add the following code to make an API call to Google Drive:

    [Authorize]
    public async Task<ActionResult> DriveAsync(CancellationToken cancellationToken)
    {
        ViewBag.Message = "Your drive page.";

        var result = await new AuthorizationCodeMvcApp(this, new AppAuthFlowMetadata()).
                AuthorizeAsync(cancellationToken);

        if (result.Credential == null)
            return new RedirectResult(result.RedirectUri);

        var driveService = new DriveService(new BaseClientService.Initializer
            {
                HttpClientInitializer = result.Credential,
                ApplicationName = "ASP.NET Google APIs MVC Sample"
            });

        var listReq = driveService.Files.List();
        listReq.Fields = "items/title,items/id,items/createdDate,items/downloadUrl,items/exportLinks";
        var list = await listReq.ExecuteAsync();
        var items = 
            (from file in list.Items
             select new FileModel
             {
                  Title = file.Title,
                  Id = file.Id,
                  CreatedDate = file.CreatedDate,
                  DownloadUrl = file.DownloadUrl ?? 
                                            (file.ExportLinks != null ? file.ExportLinks["application/pdf"] : null),
             }).OrderBy(f => f.Title).ToList();
        return View(items);
    }

  • Notice that the DriveAsync operation is marked with the Authorize attribute. In this case we use this attribute because we require users to log in before they granted access to this content.
  • The result of AuthorizationCodeMvcApp.AuthorizeAsync is an AuthResult. The RedirectUri property is set in case the end-user needs to authorize. Otherwise the Credential property will be set with the user's credential.
  • Then I create the service and it's list request. Notice that I set the Fields property of the request to fetch only the fields that I'm going to use in the view.
  • I convert the Drive API file model into my sample model, order the list by title, and send it to the client. 

4. UI changes

4.1 Add a new DriveAsync view under Views/Home


@model IEnumerable<Google.Apis.Sample.MVC.Models.FileModel>

@{
    ViewBag.Title = "Drive";
}

<table>
    <tr>
        <th>
            Title
        </th>
        <th>
            Created Date
        </th>
        <th>
        </th>
    </tr>
    @foreach (var file in Model)
    {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => file.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => file.CreatedDate)
            </td>
            <td>
                @Html.ActionLink("Download", "DownloadAsync", new 
                     { 
                         title = file.Title,
                         downloadUrl = file.DownloadUrl 
                     })
            </td>
        </tr>
    }
</table>

4.2 Change _Layout.cshtml to include a link to the new page


                    <li>@Html.ActionLink("Home", "Index", "Home")</li>
                    <li>@Html.ActionLink("About", "About", "Home")</li>
                    <li>@Html.ActionLink("Drive", "DriveAsync", "Home")</li>
                    <li>@Html.ActionLink("Contact", "Contact", "Home")</li>

Add only the highlighted line. All the rest lines of code are already exist in the file.

5. Run


The first page you are going to see when running the web application is the following:


To sign in, click on the Drive tab. The following screen shows he login page.


Choose Google as a service login.


Now you should login.


Approve the site to use the required scopes to view your email address and basic information.


Choose a user name.


The above screen might look weird to you.
You already approved the application to use your email and basic information, so you might wonder why do you need to approve again other scopes?

The reason you need to approve the other scopes when the user sign in the first time is to authenticate the user. In the above screen the user needs to authorize this site to access his or her Drive data.

We might want to avoid those duplicate steps in the future and try making both of them as a one step, but I'll leave it to the future.


We are DONE!

6. Coming Next
  • Download a file using MediaDownloader
  • Implement a new EFDataStore - Entity Framework implementation of IDataStore.
  • Any suggestions?

And I know that you are curious about the photo of this post... It's Prague! And unfortunately not in a sunny day.

Prague Oct 2009

56 comments:

  1. Thanks for the detailed post. I was trying to do this on a MVC4 application, but couldn't get it to work. When I initiate the OAuth workflow, it throws the exception ... Could you help me figure out the issue?
    Thanks in advance.

    [KeyNotFoundException: The given key was not present in the dictionary.]
    System.Collections.Generic.Dictionary`2.get_Item(TKey key) +14379623
    Google.Apis.Auth.OAuth2.Mvc.Filters.AuthorizationCodeActionFilter.OnActionExecuting(ActionExecutingContext actionContext) in c:\code\google.com\google-api-dotnet-client\default\Tools\Google.Apis.Release\bin\Debug\output\default\Src\GoogleApis.Auth.Mvc4\OAuth2\Mvc\Filters\AuthActionFilter.cs:34
    System.Web.Mvc.Async.AsyncInvocationWithFilters.InvokeActionMethodFilterAsynchronouslyRecursive(Int32 filterIndex) +296
    System.Web.Mvc.Async.AsyncInvocationWithFilters.InvokeActionMethodFilterAsynchronouslyRecursive(Int32 filterIndex) +1137
    System.Web.Mvc.Async.<>c__DisplayClass33.b__31(AsyncCallback asyncCallback, Object asyncState) +112
    System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +150
    System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate`1 endDelegate, Object tag, Int32 timeout) +132
    System.Web.Mvc.Async.<>c__DisplayClass21.b__19(AsyncCallback asyncCallback, Object asyncState) +1449
    System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +150
    System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate`1 endDelegate, Object tag, Int32 timeout) +96
    System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeAction(ControllerContext controllerContext, String actionName, AsyncCallback callback, Object state) +487
    System.Web.Mvc.Controller.b__1c(AsyncCallback asyncCallback, Object asyncState, ExecuteCoreState innerState) +45
    System.Web.Mvc.Async.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +111
    System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +150
    System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object callbackState, BeginInvokeDelegate`1 beginDelegate, EndInvokeVoidDelegate`1 endDelegate, TState invokeState, Object tag, Int32 timeout, SynchronizationContext callbackSyncContext) +203
    System.Web.Mvc.Controller.BeginExecuteCore(AsyncCallback callback, Object state) +879
    System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +150
    System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object callbackState, BeginInvokeDelegate`1 beginDelegate, EndInvokeVoidDelegate`1 endDelegate, TState invokeState, Object tag, Int32 timeout, SynchronizationContext callbackSyncContext) +154
    System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +527
    System.Web.Mvc.MvcHandler.b__4(AsyncCallback asyncCallback, Object asyncState, ProcessRequestState innerState) +108
    System.Web.Mvc.Async.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +111
    System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +150
    System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object callbackState, BeginInvokeDelegate`1 beginDelegate, EndInvokeVoidDelegate`1 endDelegate, TState invokeState, Object tag, Int32 timeout, SynchronizationContext callbackSyncContext) +203
    System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +681
    System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +12270483
    System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +288

    ReplyDelete
  2. Figured out the problem. I had made the controller that started the OAuth dance to derive from Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController - which it should not. And we should have the AuthCallbackController as described in the blog.

    Also the sample code in GitHub also helped.

    Thanks again for the post!

    ReplyDelete
  3. Did you get a chance to write about EFDataStore??

    ReplyDelete
  4. Not yet, but I'll try to add such an implementation for the next release.
    Remember that this project is an open source, so you can contribute to this library as well :)

    Anyway I created a new issue for this task in our issue tracker - https://code.google.com/p/google-api-dotnet-client/issues/detail?id=453

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. Hi, I'm trying to implement AuthorizationCodeMvcApp but sometimes I've got the error:



    Server Error in '/' Application.

    Error:"State is invalid", Description:"", Uri:""

    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

    Exception Details: Google.Apis.Auth.OAuth2.Responses.TokenResponseException: Error:"State is invalid", Description:"", Uri:""




    I've noticed that when the this occurs the key stored in DataStore looks like:

    System.String-oauth_3755e307-a676-4775-919b-bd15932e105e

    instead of:
    Google.Apis.Auth.OAuth2.Responses.TokenResponse-95e60596-f8a1-4f54-892f-36c4438e2516


    Any help?


    Thanks for all

    ReplyDelete
    Replies
    1. Did you follow all the steps above?

      We generate a state parameter, so the server will be able to know that it was the one the redirect the user and it isn't just some malicious site.

      Delete
    2. I've followed all the steps, the only difference is that I'm trying to implement an EF Data Store

      Delete
    3. Can you first check that the file data store works correctly, and by that we will eliminate the possibility that the EF data store is doing something wrong?
      Thanks

      Delete
    4. I have exactly the same issue trying to implement my custom IDataStore. Can you please share one example how to do it Peled? Thanks a lot

      Delete
    5. @sfyenterprise sfyenterprise Did you succeed in the end? If so can you please share how to implement the IDataStore with Entity Framework ? Thanks

      Delete
  7. Hi, thanks for wonderful tutorial, but I my program is stuck at "return new RedirectResult(result.RedirectUri);".
    It is not redirecting to Google Login page. What could be the possible reasons for this???

    ReplyDelete
    Replies
    1. What is the content of result.RedirectUri?
      What do you mean by saying it stuck? The program is stuck?

      Please double check that you followed all the instructions :)

      Delete
    2. Stuck means...there was no response And the result.RedirectUri is correct but now...its stuck at "var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).AuthorizeAsync(cancellationToken);"

      Delete
    3. This comment has been removed by the author.

      Delete
    4. It's hard to tell, do you use async-await correctly?
      Do you want to share your code with me, so I'll be able to advice better. Try to create a small app that contains only your error and send it to me.

      Delete
    5. I have shared my project folder here https://onedrive.live.com/redir?resid=5AADC9FDACC3AC51!1700&authkey=!ALS_3jJl1hDEtbs&ithint=file%2czip

      It is VS 2010 solution...

      Delete
    6. I'm getting several warning and errors like those ones: (consider using VS2012 or higher)

      Warning 6 The primary reference "Microsoft.Threading.Tasks.Extensions" could not be resolved because it has an indirect dependency on the framework assembly "System.Threading.Tasks, Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" which could not be resolved in the currently targeted framework. ".NETFramework,Version=v4.0". To resolve this problem, either remove the reference "Microsoft.Threading.Tasks.Extensions" or retarget your application to a framework version which contains "System.Threading.Tasks, Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a". MyMail

      Warning 14 The primary reference "Microsoft.Threading.Tasks.Extensions" could not be resolved because it has an indirect dependency on the framework assembly "System.Runtime, Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" which could not be resolved in the currently targeted framework. ".NETFramework,Version=v4.0". To resolve this problem, either remove the reference "Microsoft.Threading.Tasks.Extensions" or retarget your application to a framework version which contains "System.Runtime, Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a". MyMail

      Error 6 The type or namespace name 'Util' does not exist in the namespace 'Google.Apis' (are you missing an assembly reference?) C:\Users\peleyal\Downloads\MyMail\MyMail - Copy\MyMail\Controllers\DriveAppFlowMetadataController.cs 7 19 MyMail

      Error 12 The type or namespace name 'Util' does not exist in the namespace 'Google.Apis' (are you missing an assembly reference?) C:\Users\peleyal\Downloads\MyMail\MyMail - Copy\MyMail\Controllers\AppAuthFlowMetadata.cs 9 19 MyMail

      Error 20 The type or namespace name 'Util' does not exist in the namespace 'Google.Apis' (are you missing an assembly reference?) C:\Users\peleyal\Downloads\MyMail\MyMail - Copy\MyMail\Controllers\DriveController.cs 13 19 MyMail


      I'm pretty sure that moving to VS2012 will solve most of those issues for me, and we might be able to continue from there..
      Makes sense?

      Sorry,
      Eyal

      Delete
    7. I dont have VS 2012 and can not install VS2012 on my machine....could you plz try guide me to successfully run this project in VS2010....thanx :)

      Delete
  8. Hi Eyal, very much thankful for this great post.
    when probably you will post to download file.
    And is it possible to download file which is PRIVATE in share.

    ReplyDelete
    Replies
    1. Take a look in this sample: https://code.google.com/p/google-api-dotnet-client/source/browse/Drive.Sample/Program.cs?repo=samples#154.

      You can also read more about media download here: https://developers.google.com/api-client-library/dotnet/guide/media_download

      I believe that if the file exists in your drive folders you will be able to download it...
      Let me know if anything is missing...

      Delete
  9. as it were with a web application hosted in iis?
    only works on localhost?

    ReplyDelete
    Replies
    1. This sample should work with IIS Express or any other host :)

      Delete
    2. when I run the brand apliacacion access denied error and does not open the window permits

      Delete
    3. Can you attach the stacktrace or more information?
      Take a look in this issue:https://code.google.com/p/google-api-dotnet-client/issues/detail?id=462&can=1&q=Milestone%3DRelease1.8.2&colspec=ID%20Type%20Component%20Status%20Priority%20Milestone%20Owner%20Summary.
      This was a result of http://stackoverflow.com/questions/23405767/system-unauthorizedaccessexception-access-to-the-path-google-apis-auth-is-deni/23430287?noredirect=1#comment36108622_23430287 SO thread.

      Hope that this helps you.

      Delete
    4. Access to the path 'Google.Apis.Sample.MVC' is denied.

      Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

      Exception Details: System.UnauthorizedAccessException: Access to the path 'Google.Apis.Sample.MVC' is denied.

      ASP.NET is not authorized to access the requested resource. Consider granting access rights to the resource to the ASP.NET request identity. ASP.NET has a base process identity (typically {MACHINE}\ASPNET on IIS 5 or Network Service on IIS 6 and IIS 7, and the configured application pool identity on IIS 7.5) that is used if the application is not impersonating. If the application is impersonating via , the identity will be the anonymous user (typically IUSR_MACHINENAME) or the authenticated request user.

      To grant ASP.NET access to a file, right-click the file in File Explorer, choose "Properties" and select the Security tab. Click "Add" to add the appropriate user or group. Highlight the ASP.NET account, and check the boxes for the desired access.

      Delete
    5. The links that I attach above should help you.
      Take a look in the SO thread that contains the following:

      1.Go to the IIS
      2.Under sites select the Default site
      3.Add Permission
      4.choose I_User object and give read/write access.
      5.later you can automate this setting using a batch file and startup task.

      Delete
    6. no results, get the same error

      Delete
    7. Default site, refer to my site or the Default IIS site?

      Delete
    8. New error

      XMLHttpRequest cannot load https://accounts.google.com/o/oauth2/auth?access_type=offline&response_type…:%2F%2Frh.joffroy.com%2FEmpleados%2FIndexAsync%3FIDEmpleado%3D313880436114. No. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.

      Delete
    9. Let's track that down in SO, feel free to open a new thread with the google-api-dotnet-client tag. Hope that you already resolved that one :)

      Delete
  10. when I run the brand apliacacion access denied error and does not open the window permits

    ReplyDelete
  11. Please show us an example of how to use refresh tokens. Asking the user for consent every hour is not good. Thanks

    ReplyDelete
    Replies
    1. You don't need to get the user consent every hour. This code already supports refreshing the token, as you can read in https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#credentials. Makes sense?

      Delete
  12. Hi

    Great Post. Implemeting the code for my google calendar API works fine on my localhost but after hosting it to server it given me following error :

    OpenID auth request contains an unregistered domain:

    After googling I came to know that Google has deprecated the use of OAuth 2.0.

    Please help me how can I resolve this error using in your above code and signin through Google+

    I hope you will reply me asap.

    Thanks for the Post.

    ReplyDelete
    Replies
    1. What do you mean by saying "Google has deprecated the use of OAuth 2.0". I don't think it's true :)

      Delete
  13. This comment has been removed by a blog administrator.

    ReplyDelete
  14. Hi Eyal, I need your personal advice on choosing between Service account and Web Account for user authentication. I have a number of users who want to access Google apps and I need to authenticate them. Please suggest me the best Outh 2.0 mechanism. My application will be developed in Mvc5.

    best regards,

    ReplyDelete
  15. It looks like you should use web authentication. Follow up the instructions on https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#web_applications.

    More details about the different OAuth 2.0 protocols (not .NET specific) are available at: https://developers.google.com/accounts/docs/OAuth2.

    ReplyDelete
  16. hi eyal.thanks for this post.please help me for below question:
    I am creating a website by mvc5.is there a way that let users upload file's to my google drive storage?


    ReplyDelete
  17. I think this Mvc5 example needs an update as I am getting "400: unregistered domain" error. Google has deprecated the use of openId 2.0 and this example is using the same. I request the author to confirm this and help us fix the error.

    best regards,

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
    2. I didn't run the sample lately... I'll find some time in the next days to update it if it is required. Anyway...

      Google has deprecated OpenID 1.0 and given developers using OpenID 2.0 until April 2015 to migrate to OAuth 2.0.
      For more information, see https://developers.google.com/+/api/auth-migration
      The gist in OpenID is that Google is not allowing new registrations and the existing registrations will be turned down.

      An alternative solution is to implement the GetUserId in the FlowMetadata class differently (without using OpenID).

      Delete
    3. @Eyal: thanks for quick reply. :) Actually I tried using googleplus sign-in as recommended by google but it fails with invalid redirect-uri. When I register my app with http://localhost:port/signin-google, no errors but the calls never reaches to AuthCallback Controller that means I am not authenticated to make call against drive api. please tell me how to fix this ? Where should I put AuthCallBack and FlowMetadata code ?

      Delete
    4. Did you follow our OAuth 2.0 page for web applications?
      https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#web_applications

      "
      Google APIs support OAuth 2.0 for Web Server Applications. In order to run the following code successfully, you must first add a redirect URI to your project in the Google Developers Console. Since you will use FlowMetadata and its default settings, set the redirect URI to your_site/AuthCallback/IndexAsync.
      ......"

      Delete
    5. I did follow the page, so did follow your tutorial. Here the code of my startup.cs

      app.UseGoogleAuthentication(
      clientId: ".myid.apps.googleusercontent.com",
      clientSecret: "secret ");

      The redirect Uri "mysite/AuthCallback/IndexAsync" is registered in my developer console. Still I get the same error , that is "invalid redirect-uri". I also looked into Rick's updated blog..no help.

      Delete
  18. Hi Eyal : I managed to get worked Service account authentication in my web application project instead of a server-side account ... I hope I won't face any problems because of this. Please tell me if this will cause any problem.

    best regards,

    ReplyDelete
    Replies
    1. I'm not an expert of the API. Keep me tuned and worst case I'll refer you to the right people.
      Also.. if something doesn't work, consider creating a new thread in SO using google-api-dotnet-client tag. Thanks!

      Delete
    2. I meant - I'm not an expert of the specific API you're using (which I'm not sure yet - what it is - Drive, Youtube, Calendar, etc.).
      I'm the right guy on issues about the core .NET library and its API.

      Delete
    3. I have solved the previous Redirect URI problem in this tutorial but a new error came up..
      result.Credential always returns null and sends me to execute the redirect uri.. I get re-authenticated again , receive the token, the program gets stuck. Why "result.Credential" is returning null , even if , I am getting authenticated successfully. ? I followed your tutorial word by word and even get logged in but result.Credential is null. please help me.

      Delete

  19. Really nice and informative thanks for sharing this.

    Web Development Company

    ReplyDelete
  20. thanks for the post this worked fine at my mvc web application. but in case user click cancel on google auth request page it gives Error:"access_denied", Description:"", Uri:""

    (http://localhost:*****/AuthCallback/IndexAsync?error=access_denied&state=http://localhost:*****/Home/gmailAsync15538256)

    it tried to override the authcallback class method IndexAsync but i an un able to handle it. please suggest me if i am wrong some where or give some reference for google Oauth implementation sample using mvc.

    ReplyDelete
  21. I have downloaded this but it doesn't run in Visual Studio 2013?

    Error below

    [NullReferenceException: Object reference not set to an instance of an object.]
    Microsoft.Owin.Security.Cookies.d__0.MoveNext() +664
    System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +93
    System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +52
    System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +24
    Microsoft.Owin.Security.Infrastructure.d__2.MoveNext() +860
    System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +93
    System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +52
    System.Runtime.CompilerServices.TaskAwaiter.GetResult() +21
    Microsoft.Owin.Security.Infrastructure.d__0.MoveNext() +427
    System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() +22
    Microsoft.Owin.Host.SystemWeb.Infrastructure.ErrorState.Rethrow() +33
    Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.StageAsyncResult.End(IAsyncResult ar) +150
    Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContext.EndFinalWork(IAsyncResult ar) +42
    System.Web.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +415
    System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155

    ReplyDelete
  22. This comment has been removed by the author.

    ReplyDelete
  23. I know this has been out there for awhile, but I downloaded your project from GitHub. I was able to get it setup and running, but I'm having trouble on the Drive page. When I click on the "Google" button to login it throws a 404 error from Google. Did I miss a setting, or has this link changed?

    ReplyDelete