Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
LindaLawton opened this issue Mar 30, 2021 · 19 comments
Open
Assignees
Labels
priority: p3 Desirable enhancement or fix. May not be included in next release. type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.

Comments

@LindaLawton
Copy link
Collaborator

LindaLawton commented Mar 30, 2021

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

@jskeet jskeet added the type: question Request for information or clarification. Not an issue. label Mar 30, 2021
@amanda-tarafa
Copy link
Contributor

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
Copy link
Collaborator Author

LindaLawton commented Mar 30, 2021

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
Copy link
Contributor

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

@LindaLawton
Copy link
Collaborator Author

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

@amanda-tarafa
Copy link
Contributor

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
Copy link
Collaborator Author

LindaLawton commented Mar 31, 2021

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
Copy link
Contributor

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
Copy link
Contributor

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:

  • All credentials that had been created (via GoogleWebAuthorizationBroker.AuthorizeAsync) before will keep holding the old tokens, so they will fail access token refreshes and client services that were created with these credentials will fail all operations.
  • The IDataStore will hold on to the old tokens until the first attempt at using them (refreshing the access token or calling an API) has failed, at which point, they will be removed from the IDataStore effectively "signing the user out". This means that even credentials (and depedent service clients) created after the user has revoked the tokens may be created with old tokens and thus be invalid.
  • After the old tokens have been removed from IDataStore, the first credential created (via GoogleWebAuthorizationBroker.AuthorizeAsync) will trigger the normal auth flow, prompting the user etc. This credential and all subsequent ones will be valid.
  • Old credentials can be individually reauthorized by calling GoogleWebAuthorizationBroker.ReauthorizeAsync on them.

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.

@amanda-tarafa amanda-tarafa added priority: p3 Desirable enhancement or fix. May not be included in next release. type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design. and removed type: question Request for information or clarification. Not an issue. labels Apr 1, 2021
@LindaLawton
Copy link
Collaborator Author

@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
Copy link
Contributor

amanda-tarafa commented Apr 8, 2021

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
Copy link
Contributor

(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
Copy link
Collaborator Author

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
Copy link
Contributor

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

@LindaLawton
Copy link
Collaborator Author

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

@meofiscoding
Copy link

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
Copy link
Contributor

@meof-coding You need to follow three different approaches as described in my #1816 (comment).

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
Copy link

yccidx commented Mar 30, 2022

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
Copy link
Collaborator

jskeet commented Mar 30, 2022

@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
Copy link

yccidx commented Mar 30, 2022

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
priority: p3 Desirable enhancement or fix. May not be included in next release. type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.
Projects
None yet
Development

No branches or pull requests

5 participants