- Why .AddGoogle() cannot be without .AddCookies()
- .NET Developers answers
In this article, we are going to get answers from the guys developing .NET right now. Since I am working at Microsoft I can just directly ask them inside Teams for what I am really proud of and appreciate.
Every time I need to use Google auth in .NET I encounter unknown spots that are not documented. For example, in docs and tutorials
.AddGoogle() is always used with
.AddCookies(). However Google works through
OAuth protocol with its
own cookies, so I always wondered why we need .NET cookies on top of it at all?
Why .AddGoogle() cannot be without .AddCookies()
RemoteAuthenticationOptions source code
RemoteAuthenticationOptions contains different things for OAuth protocol: HttpClient to communicate to identity provider, data protection provider, callback path, return url, etc.
On top of that it contains SignInScheme here
As you can see from the comments, it is DIFFERENT scheme from Remote Scheme (Google, Github, Facebook) that is used to persist user identity after authentication.
In other words, it saves user identity somewhere on side of your application to NOT trigger
OAuth flow every time. Usually, it is a
Cookies scheme, because once the cookies are set - they carry auth info with each request by the browser.
RemoteAuthenticationOptions contain validate method that ensures that
SignInSheme is not the same as
RemoteScheme (current) here
41nd line: this validation is happening. If our
SignInScheme is the same as
sheme (that is
GoogleScheme) then we are throwing
As explained above it turns out that ANY remote scheme including
Github cannot be used as
Google Scheme uses
OAuth protocol. Under the hood, it performs the whole OAuth process if you are not signed in to Google already (asking for login and password, etc). Then it sets Google Cookies into your browser.
Starting from that point any time a new OAuth process is initiated Google Authorization, the Server will check cookies and return Access/Identity tokens straight away.
It seems like Google cookies are enough for the use case, isn’t it?
1. Verify Access / Id tokens returned from Google
Since inside the handler we have access to Id and Access tokens - we might save them and call some validation endpoint on Google Side, or in case of Google I have heard about this library provided by Google to validate the token.
The problem here is that not all Remote Schemes allow you to validate tokens, so you end up doing some hacks like sending some test requests with this token to Server to check whether you still don’t get 401.
2. Init OAuth process every time
When OAuth flow is happening most of the Authorization Servers save cookies in the browser to not do the Authentication process again (login & password checking). So, the second time you initiate OAuth process - Google Authorization Server will just check cookies and return you Access / Id tokens.
It seems like a relatively inexpensive operation, especially considering most Remote Providers are doing the same way.
For example, try to investigate Github request in the network tab to get your repositories. You will see cookies with the user session going to the request, without the Authorization header. So, why not?
But all workarounds encounter one problem -
generalizability. Different remote authentication providers work in different ways, so the easiest and cheapest way - is to
preserve auth info on your application side.
By doing this:
you will trigger OAuth flow only once, and every time your own cookies are expired or revoked - you will initiate OAuth flow one more time. Since the cookies of the Remote Authentication provider could be valid and it still will not prompt the user login and password.
you do not care much about different implementations of OAuth (encrypted tokens or not, verifiable tokens or not, etc). You just emit OAuth flow according to protocol, get opaque for your tokens, try to grab claims (user data), and save your own cookies.
.NET Developers answers
I just went to aspnetcore repo and searched through the developers. Then I searched them in my teams and MAGIC - I found them. Not all of them answered me, but a few of these really smart guys did.
This is what I got:
Answer from HaoK:
Answer from Tratcher:
Demo is run from this repo.
This is simple .NET application with Razor Pages. We have added
Cookies to demonstrate how they work together here.
We have 1 private endpoint that is under [Authorize] attribute here.
Lets try to access Secret page
Response from request page was
location header that points to Google authorize endpoint.
As you might know from my previous article about OAuth this is the
Authorization Code grant endpoint, the most secure one.
In your browser you should see the Google Login page
If your browser contains Google cookies already, meaning that you are already logged in somewhere in Google Services - then your Authorization Middleware (
.AddGoogle()) will set your application cookies.
In the screen above you see Authorization Callback to your
Return Url with Authorization Code which will be exchanged to get token.
After we got Access Token, Authorization Middleware will do the request to Google to get user claims and call
Context.SignInAsync with those claims to set Cookies for our Server (localhost:7000).
To understand Google Auth inside .NET - you could read this article.
locahost:7000/signin-google endpoint redirects us to our initial
/secret endpoint where we have Cookies set. With those cookies, we can be Authorized and get User Claims that we got from the Google endpoint.
So overall it is NOT allowed to use
- It provides extra overhead of calling OAuth provider each time,
- As stated in this article, Remote Auth handlers don’t have
.SignInAsync()but each of those handlers calls
Context.SignInAsync()here and rely on another Auth Scheme registered for signing principal in. Basically, the code will fail or enter indefinite recursion.
To do so we need to write our own AuthorizationHandler with all supportive classes, and each time the user is not authorized - just emit OAuth flow.