C# GCP auth2.0 403 mismatch redirect - c#

I'm using this code to try to get the auth from Google:
await HttpContext.ChallengeAsync("Google", new AuthenticationProperties() {RedirectUri = "/"});
The thing is the redirect always take the HTTP url even if I'm running it from https url.
The url with https://something-sss.com is already whitelisted on the GCP API identifiers.
But it always take the Uri with http

To expose my docker app I used to use Expose http://*:80 and even if the associated domain was started with an HTTPS, inside the app it was always an HTTP call, so all the redirects were done through the http protocol.
I have changed the way I expose my app from http://*:80 to https://*:443 in the code I used kestrel to launch the app and it's working perfectly now.

Related

.Net 6 Middleware UrlRewrite Redirect from https to http

I am trying to redirect a URL using Urlrewite middleware but the issue is it always redirected to HTTPS rather than HTTP even though I explicitly defined it to redirect to HTTP.
My application is using HTTPS but the script I am loading form using HTTP.
My application URL is:
https://www.testlocal.com/
While the URL of the application from where I am trying to load the script is:
http://www.localhost:3000/static/js/bundle.js
Here is my code:
var rewriteOptions = new RewriteOptions()
.AddRedirect("static/js/(.*)", "http://www.localhost:3000/static/js/$1");
app.UseRewriter(rewriteOptions);
Redirection works good but the issue is it does not redirect to HTTP rather it redirects to HTTPS. Because that Script is not loaded.

How to authenticate an HttpClient connection with an external provider (Google)

Edit:
Here is my question reformulated:
I have a web server with secured api endpoints - one must have been authenticated with Google prior to using them. I implemented Challenge and Callback endpoints for that.
This works well from a browser with my SPA web front-end. The user gets redirected to the Google website to sign-in and then gets redirected back to my webapp; the browser then has the authenticated cookies and the webapp can use the endpoints to update its state.
I also have a WPF application that will communicate with the web server.
I want the WPF application to do the same as the web front-end: Use the web api endpoints after being authenticated with Google. The connection between the WPF application and my web server is done through an HttpClient.
My problem is I don't know how to authenticate that HttpClient connection between the WPF app and the web server.
I tried using the same Challenge endpoint but the response I get is of course the HTML from the Google Sign-In page, so I guess I can't use that with an HttpClient...
I also tried authenticating with GoogleApis from the WPF app and use the authenticated token to set cookies in the HttpClient but apparently this is not compatible.
How to authenticate an HttpClient connection to a web api with an external provider such as Google?
Original question:
From a WPF application, the user authenticates with Google with this code:
using Google.Apis.Auth.OAuth2;
...
public void Authenticate()
{
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "myClientId",
ClientSecret = "myClientSecret"
},
new[] { "email", "openid" },
"user",
CancellationToken.None).Result;
}
This works and the UserCredential object contains the authenticated token:
How to embed this token information in a web request made with an HttpClient in order to call my webapi endpoint?
I think the request must include some cookies to inform the server that it has been authenticated, but I don't know which ones exactly.
The endpoint on the server-side validates that the user is authenticated with the help of IdentityServer:
var result = await HttpContext.AuthenticateAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme);
if (result?.Succeeded != true)
{
throw new Exception("External authentication error");
}
If I got your question right, you just have to set the Authorization header
var credentials = await GoogleWebAuthorizationBroker.AuthorizeAsync(
clientSecrets,
new[] { "email", "openid" },
"user",
CancellationToken.None);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
credentials.Token.TokenType,
credentials.Token.IdToken);
Maybe you'll find below a helpful hint to better understand OpenID :)
The confusion stems from mixing GoogleApis and IdentityServer frameworks.
Authentication/authorization can be achieved using either of them.
Objects from Google.Apis.Auth.OAuth2 and IdentityServer4 namespaces are not designed to interact.
No manual cookie handling is necessary, for sure.
Ask yourself to whom does Google provide trust for the user. If it calls back to WPF, then webapi trusting WPF is a separate issue.
You answer your own question in the question:
the browser then has the authenticated cookies and the webapp can use
the endpoints to update its state
HttpClient needs to send those same cookies.
How do I set a cookie on HttpClient's HttpRequestMessage
If I understood your question right, then I faced the same problem not too long ago.
The way I implemented it is that in the backend, no matter who tries to access the endpoint, they had to send a Bearer X authorization token.
The token contained the identity of the client that wanted to access the resource, and I checked if he was permitted.
No matter what kind of client wants to access the endpoint, it just has to have that authroziation header in the request that he sends and the backend will treat it the same.
In my scenario, I used an authentication service that returns a cookie to the client with a certain JWT that contains the identity information.
Then from the client I send in every request the JWT received from the authentication service as an authorization header to the backend.
The reason I had to put the JWT that I receive from the service in a header, is that the authentication service and the backend service are not in the same domain, so cookies cant be shared.
This results in such design that no matter how you authenticate the client, the end result must be some sort of token that the backend can receive and read.
Hope this helps.

C# Owin OAuth2 - Provider login redirect goes to incorrect base URL

I have a C# application running as the backend API for a website.
It is using Microsoft.Owin for external social logins. Everything works fine deployed on localhost. In production, when the backend endpoint is requested for ExternalLogin via popup window with url like:
"mywebsiteurl/api//ExternalLogin?provider=Google&redirect_uri=...", instead of redirecting the URL to something like:
"https://accounts.google.com/o/oauth2/auth?..." (in the case of Google+),
it redirects to: "mywebsiteurl/o/oauth2/auth?..."
which obviously returns a 404.
Any ideas what is happening here?

OWIN OpenIdConnect Middleware IDX10311 nonce cannot be validated

I have an application using the OWIN middleware for OpenIdConnect. The startup.cs file uses the standard implementation of app.UseOpenIdConnectAuthentication. The cookie is set to the browser, but it errors with:
IDX10311: RequireNonce is 'true' (default) but validationContext.Nonce is null. A nonce cannot be validated. If you don't need to check the nonce, set OpenIdConnectProtocolValidator.RequireNonce to 'false'.
I've found that when running fiddler as I do for most debug projects this behavior happens. The error is returned, but if I go back to the site everything is working and my user is authenticated. Has anyone seen this behavior when running fiddler?
With fiddler:
SecurityTokenValidated notification in OpenIdConnect is executed twice.
After the second pass through the IDX10311 error is thrown
Browser contains the valid cookie, going back to the page I can view the valid User.Identity data.
Running without fiddler:
SecurityTokenValidated executes once in OpenIdConnect
No error thrown, proceeds to load up controller action for post authentication redirect Uri
Cookie also valid and User.Identity data correct.
Ideas? I can get around it without running fiddler, but when debugging it would be nice to also run fiddler to inspect traffic.
Maybe is this the cause?
Hello there, I think I found the root cause of this issue.
I'm summing up my discoveries:
The problem is in the OpenIdConnect.nonce.OpenIdConnect cookie
This cookie is set from the app (let's call this "ID Client") as soon as the OpenID Middleware init an authentication session
The cookie should be sent back from the browser to the "ID Client" as soon as the authentication has been completed. My assumption is that this cookie is needed to have a double check from the ID client point of view (i.e. did I really started an OpenID Connect authorization flow?)
A lot of confusion in me was caused by the "Nonce" term, used both in this cookie and in the OpenID Connect flow from the ID server.
The exception, in my case, was caused by the missing cookie (not the nonce of the ID Server), simply because it wasn't sent by the browser back to the "ID client"
So the main root, in my case, was this: OpenIdConnect.nonce.OpenIdConnect cookie was not sent back to the ID Client by the browser. In some cases (i.e. Chrome, Firefox and Edge) cookie was sent correctly, while in others (IE11, Safari) it wasn't.
After a lot of research, I discovered that the problem was on the Cookie restriction policy, defined on the browser. In my case, the "ID client" is embedded in an <iframe>. This cause the "ID Client" to be seen as a "third-party client", because the user didn't navigate to that URL directly in the main window. Because this is a third-party, for some browsers, it's cookies have to be blocked.
Indeed the same effect may be obtained on Chrome, by setting "Block third-party cookies".
So, I have to conclude that:
a) If iframe is a must (as in my case, because "ID Clients" are apps that must run inside the graphic content of the our main platform app), I think the only solution is to intercept the error, and handle it with a page, asking the user to enable third party cookies.
b) If iframe is not a must, it should suffice opening the "ID Client" in a new window.
Hope this helps somebody, because I got crazy!
Marco
I had the same problem but switching back the Microsoft.Owin.Security.OpenIdConnect to version 3.0.1 solved the issue
For anyone else who gets here in 2021, you'll likely get this issue if:
You're redirecting http -> https
Or you've changed your app's host domain.
Both of these aren't an issue with the middleware or your app, but it's about the combination of two issues:
The fact that your app is still hosted on the old old domain or protocol. You want to prevent browsers hitting that by implementing a redirect on the web server.
The redirect URI (sometimes know as reply URL) in Azure or whichever OpenIdConnect authorization server you're authenticating with. You want to get this updated to the new protocol or domain.
Our example: We had https://old.example.com/app/ that was now also hosted at https://new.example.com/app/. We wanted users' previous bookmarks to still work.
Our solution:
We updated the redirect URI (reply url) to point to the new domain for the app (https://new.example.com/app/signin-endpoint). Ideally, make sure there is only one URI listed for your app and that it's https.
We added the new domain binding to the site in IIS (we're old school, but do the same for your hosting of choice 😊)
We added an IIS redirect to the new domain (new.example.com) so that users' bookmarks still work. Again if you're not on IIS, implement a permanent redirect in the web server of your choice.
Until we had the final step above, we were seeing the error in the OP's post. It's the same process if you're forcing http -> https.
Here is the IIS re-write for those who are also "old school":
<rewrite>
<rules>
<rule name="Redirect old.example.com to new.example.com" enabled="true" patternSyntax="Wildcard" stopProcessing="true">
<match url="*" />
<conditions>
<add input="{HTTP_HOST}" pattern="old.example.com" />
</conditions>
<action type="Redirect" url="https://new.example.com{REQUEST_URI}" />
</rule>
</rules>
</rewrite>
It goes in the <system.webServer> section of your web.config file. Enjoy!
For me changing reply url in Azure active directory works.
This happens when you enable SSL because it changes only the sign on URL to the HTTPS URL while the reply URL remains the same HTTP URL.
When you try to access your app using the https URL, it sets a cookie with a unique number(nonce) in your browser and hits Azure AD for authentication. After authentication, the browser has to give access to that cookie. But since the sign on URL and reply URL are different the browser does not recognize your app and does not give access to that cookie and hence the application throws this error.
I know its an old post but I had this problem and nothing was working for me, after lose my mind behind a solution to make my enterprise application works I end up fixing it by setting the multi-tenanted option to yes in azure (in Azure select: app registration>settings>properties, set multi-tenanted to yes and click save).
hope it helps someone, couldn't see nobody mentioning it.
I noticed this error when running IIS Express in the background when I had switched to hosting in full IIS. When I disabled the IIS Express, my error went away.
A cookies rewrite rule in the web.config to ensure samesite cookies gave this cryptic exception. Disabling that rule solved it.
A temporary solution which worked for me for an app secured via Azure Active Directory was to signout (by going to the sites/Account/SignOut page) and then I was able to return to the home page and sign in ok. Hope this helps someone.
I know it's been a while on this one. My specific issue was with the IDX10311 error in relation to authenticating with IdentityServer while Fiddler (traffic inspector proxy) was running. I added a custom owin middleware to catch and absorb the IDX13011 in the case where the hostname contained "localhost". Ignoring this exception allowed us to use the site with fiddler as a workaround. I think it causes breaks in the authentication process though where we have to press enter in the browser address bar on the callbacks to get it going again, but this only affects development.
Here's the invoke method we used in the middleware to absorb the error. I should note though that we have seen this error in production occasionally as well. No explanation for a cause, but I have a feeling it is related to users on IE browsers.
public override async Task Invoke(IOwinContext context) {
try {
await Next.Invoke(context);
} catch (Exception ex) {
_errorHandling = new ErrorHandling();
if (ex.Message.Contains("IDX10803")) {
//do something here to alert your IT staff to a possible IdSvr outage
context.Response.Redirect("/Error/IdSvrDown?message=" + ex.Message);
} else if(ex.Message.Contains("IDX10311") && context.Request.Host.Value.Contains("localhost")) {
//absorb exception and allow middleware to continue
} else {
context.Response.Redirect("/Error/OwinMiddlewareError?exMsg=" + ex.Message + "&owinContextName=" + lastMiddlewareTypeName);
}
}
}
For me it was a different problem. My site was working with both of the urls below
https://www.example.com
and
https://example.com
But my redirect url was https://www.example.com.
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = ConfigurationManager.AppSettings["ClientId"].ToString(),
Authority = ConfigurationManager.AppSettings["Authority"].ToString(),
RedirectUri = ConfigurationManager.AppSettings["RedirectUri"].ToString();//https://www.example.com
}
Users who use https://example.com the mentioned exception occurs.
The cookie generated for www.example.com and example.com are different. So after the login when it redirects, the cookie doesn't contain the correct nonce to validate and the exception occurs.
The solution for the problem is to set the redirect URL dynamically
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = ConfigurationManager.AppSettings["ClientId"].ToString(),
Authority = ConfigurationManager.AppSettings["Authority"].ToString(),
RedirectUri = ConfigurationManager.AppSettings["RedirectUri"].ToString(),//https://www.example.com
,
// sample how to access token on form (when adding the token response type)
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = async n =>
{
var uri = n.Request.Uri; //From request URL determine the RedirctUri and set below
n.ProtocolMessage.RedirectUri =""//Set the url here
}
}
}
The same issue can happen with https://www.example.com and http://www.example.com
Users where getting this issue when Edge was set to IE compatibility mode, removed it from IE compatibility and it solved the issue. Setting / list of sites is controlled under edge://compat.
I end up allowing the Owin to skip to the next Middleware on AuthentificationFaild callback function. I'm checking if the error message contains nonce error Id and call SkipToNextMiddleware function from the context. With that, I'm restarting the sign-in process so if the user cookies were not set there will be a second call that will set the cookie.
The code is written in vb.net
Dim oidcAuthOpt= New OpenIdConnectAuthenticationOptions()
oidcAuthOpt.Notifications = New OpenIdConnectAuthenticationNotifications With {
.AuthenticationFailed = Function(n)
If (n.Exception.Message.StartsWith("OICE_20004") Or n.Exception.Message.Contains("IDX10311")) Then
n.SkipToNextMiddleware()
Return Task.FromResult(0)
End If
Return Task.FromResult(0)
End Function
}

Why is Windows Authentication working from local to server, but not server to server?

I have two sites, A and B. A consumes an API that B exposes, and B requires Windows authentication. Both sites live in Domain D.
The API is consumed via HttpClient, and when site A is run locally, under my domain account (which is in Domain P), access is granted. In this case, HttpClient is instantiated like so:
using(var client = new HttpClient(new HttpClientHandler { UseDefaultCredentials: true }))
When A is deployed to a testing server, the above results in a 401 Unauthorized response. The application pool on the testing server is running under a service account in domain D.
When explicitly using that service account like this:
var credential = new NetworkCredential("service-account", "password", "D");
var cache = new CredentialCache
{
{
new Uri(apiServerUri), "NTLM", credential
}
};
var handler = new HttpClientHandler
{
Credentials = cache
};
using(var client = new HttpClient(handler))
...
And again running site A locally, access is still granted. Access is also granted when accessing the API directly via browser, and specifying the service account credentials. Logs indicate that it is definitely the service account being used to access the API.
Deploying the above back to the testing server still results in 401 Unauthorized.
Deploying site A to a local instance of IIS, also successfully consumes the API of B.
Running site B locally, and then accessing it via site A locally, results in a 401 Unauthorized.
Accessing the API through a browser on the testing server where A is deployed, and specifying the service account credentials, also gives a 401 Unauthorized.
I'm not sure where to go from here - am I missing something in the code to get this working? Or is it likely to be an IIS or AD issue?
While I'm yet to determine exactly why this work around works, or if there is a better way of doing it (because this feels clunky), the following has allowed A to connect to B, when both are sitting on the same server.
Site B has had an additional host binding setup in IIS, to listen on localhost:12345. Site A has been configured to connect to that endpoint, rather than the domain name for Site B. Authentication is now working correctly.
I would be interested if anyone can explain why this is the case - I dislike 'magic' fixes.
edit
It would seem that this kb article is a likely cause for this behavior. Specifically:
When you use the fully qualified domain name (FQDN) or a custom host
header to browse a local Web site that is hosted on a computer that is
running Microsoft Internet Information Services (IIS) 5.1 or a later
version, you may receive an error message that resembles the
following: HTTP 401.1 - Unauthorized: Logon Failed This issue occurs
when the Web site uses Integrated Authentication and has a name that
is mapped to the local loopback address
and
Therefore, authentication fails if the FQDN or the custom host header that you use does not match the local computer name.
Registry modifications aren't really an option on these servers, so looks like the work around is what we will be using.

Categories

Resources