googleapis / google-api-dotnet-client

Google APIs Client Library for .NET
https://developers.google.com/api-client-library/dotnet
Apache License 2.0
1.36k stars 528 forks source link

Asp .net core does not prompt for access when user has removed access. #1816

Open LindaLawton opened 3 years ago

LindaLawton commented 3 years ago

I have a simple asp .net core application which was built based upon our sample code.

I authorized the user and accessed the users profile data. Then i went to my account permissions and removed the applciations access.

At this point if i had been using a installed application the library would pop up and request access again. In this case the application crashes.

image

GoogleApiException: Google.Apis.Requests.RequestError Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project. [401] Errors [ Message[Invalid Credentials] Location[Authorization - header] Reason[authError] Domain[global] ] Google.Apis.Requests.ClientServiceRequest.ParseResponse(HttpResponseMessage response)

Clearing the cookies in the web app did fix this.

My question is this an issue with the library that we are forcing a clear, or is this something we need to recommend that users catch some how to force a clear of the cookies so that the user does not get an error in this manner?

Im really not a web dev so i guess i am fishing for opinions on how best to advice our developers.

I have cross posted this here How to catch authorization fail, clear cookies and request access again

amanda-tarafa commented 3 years ago

I'll reproduce later this afternoon and see what options are there to avoid/mitigate this. I'll update here when I know more.

LindaLawton commented 3 years ago

Emailed you a drive link to what i am using.

I tried adding a messy catch around it just to try and clear the cookies it didn't work.

amanda-tarafa commented 3 years ago

Thanks, I'm looking at a couple other things on my plate first. I'll still try to get to this later today.

LindaLawton commented 3 years ago

No rush when you have time, I was just testing this while finalizing the video going up tomorrow and ran across this .

amanda-tarafa commented 3 years ago

Just as a note, the linked you shared earlier, I think that by mistake you just copied the standard VS template for a ASP.NET Core 3 App, no code configuring Google.Apis.Auth.AspNetCore3 that I can see, or a dependency either, etc. But no worries, I'm now reproducing and will post here shortly. Thanks.

LindaLawton commented 3 years ago

Did you check startup.cs? It should all be there. This is the video I mentioned How to get a google users profile information. (ASP .Net Core)

amanda-tarafa commented 3 years ago

Yep, I downloaded it twice in the morning and just now before replying, I just see the standard template, standard Startup etc. But no worries, I've got working code, was flagging mostly in case you are sharing that somehwere else.

amanda-tarafa commented 3 years ago

When a user revokes their permission for an app, code using Google.Apis.Auth.AspNetCore* may see exceptions in any of three different places, and unfortunately the exceptions are different each time. The way to handle them so as to attempt a re-authorization is the same, basically, we want to log out the user.

The first is the one initially reported here, an exception will be thrown when using the revoked tokens to make an API call. That can be dealt with as follows

try
{
    var calendars = await service.CalendarList.List().ExecuteAsync();
}
// You can also check ex.RequestError for filtering down the exception.
catch (GoogleApiException ex) when (ex.HttpStatusCode == System.Net.HttpStatusCode.Unauthorized)
{
    await HttpContext.SignOutAsync();
    // This is not necessary, the exception may be rethrown here and handled in the normal way that your application
    // handles exceptions. Or you may redirect straight to the home page,
    // or to a special page that says "You have been signed out", etc.
    return RedirectToAction();
}

The second one is when fetching the credential itself from IGoogleAuthProvider. If the access token needs refreshing, then an attempt will be made to do so, but since the refresh token has been revoked, it will fail. This can be dealt with as follows:

try
{
    // auth is IGoogleAuthProvider
    GoogleCredential cred = await auth.GetCredentialAsync();
}
catch (InvalidOperationException ex)
{
    await HttpContext.SignOutAsync();
    // This is not necessary, the exception may be rethrown here and handled in the normal way that your application
    // handles exceptions. Or you may redirect straight to the home page,
    // or to a special page that says "You have been signed out", etc.
    return RedirectToAction();
}

The third one is when doing incremental auth, either via attributes or via code. This exception happens before reaching the controller, it happens in the auth pipeline, so it cannot be caught on controller user code directly. In this case user code should include an exception handler page that checks for a Microsoft.Identity.Model.Tokens.SecurityTokenException and calls HttpContext.SignOutAsync() and then shows a message or similar. This approach can also be used for the other two cases, checking for the appropiate exceptions.

This is all of course far from ideal, and it can probably be improved. But it will requiere careful design so as not to cause side effects. We don't have bandwidth to look into it for the next 3 to 4 weeks. I'll leave this issue open as a low priority feature request. Also note that this is a corner case, given that authentication cookies are not persistent cross browser sessions by default, we are talking about a user that has authenticated, goes and revokes the tokens, and comes back to the same session that they authenticated in to try an use the app again.

I also wante to comment a little on this:

At this point if i had been using a installed application the library would pop up and request access again.

That's not exactly the case. When a user has revoked the tokens:

But this is all more easily handled by client code, with a retry for instance that recreates the client service after there's been a failure. If you have such retries already, then it will seem like the auth library is automatically prompting after a token has been revoked.

LindaLawton commented 3 years ago

@amanda-tarafa if we are talking about a rewrite. Can we chat about coming up with a method for storing credentials like the old Idatastore. We need an option for linking it to identity storage, (ef netcore, or home grown) and persisting the refresh token.

amanda-tarafa commented 3 years ago

No I don't think it would be a rewrite, just to provide helper methods and similar to make it easier to handle this case. I'll know more when I have the time to work on it.

Also, I don't see why Google.Apis.Auth.AspNetCore* wouldn't work with Identity. I don't remember having tried it, but I believe these libraries were implemented with that in mind. When I get to this issue, I'll try it and post back about it.

Also, if your main interest is to persist the refresh token, take a look at:

amanda-tarafa commented 3 years ago

(As a side note, if you have questions or run into issues with storing tokens or using in conjunction with Identity, please do create new issues for that to keep this one about revoked tokens. Thanks!)

LindaLawton commented 3 years ago

Also, I don't see why Google.Apis.Auth.AspNetCore* wouldn't work with Identity. I don't remember having tried it, but I believe these libraries were implemented with that in mind. When I get to this issue, I'll try it and post back about it.

I haven't tried it either Its on the list, maybe we can come up with an example together.

amanda-tarafa commented 3 years ago

Sounds good, I'll let you know when I start looking into it. It'll be a few weeks though.

LindaLawton commented 3 years ago

That's fine i have a huge backlog of tutorials and videos i am working on now.

meofiscoding commented 3 years ago

hi, I have the same issue "How to catch authorization fail, clear cookies and request access again" I wonder if is there a solution for it..

amanda-tarafa commented 3 years ago

@meof-coding You need to follow three different approaches as described in my https://github.com/googleapis/google-api-dotnet-client/issues/1816#issuecomment-811769827.

I understand this is far from ideal, but we haven't had the time to improve the error handling side of things. We've kept the issue open though as we would like to revisit if we have the time in the future.

yccidx commented 2 years ago

I've used this by this guide-line on Windows Form SheetService for a while.

It seems like work for personal Google account, but suddenly unavailable for Workspace account.

jskeet commented 2 years ago

@ycidx: It's not clear whether your issue is actually related to this one - it seems unlikely given that you're talking about Windows Forms and this issue is about ASP.NET Core. Please file a new issue with as much information as possible, in particular more details than "suddenly unavailable".

yccidx commented 2 years ago

@jskeet Noted, I'll make a new issue for you reference. Thanks.

Update: I found this article about less secure apps. I tried update my .NEW Framework from 4.5.2 to the latest 4.7.2 and the problems solved.