IIS Reverse Proxy with OpenIdConnect going to wrong Identity provider - azure-active-directory

OpenIdConnect (Azure) Settings
// COOKIES: Tells it to use cookies for authentication.
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
CookieManager = new SystemWebCookieManager()
});
// OPEN-ID: Authenticate via Azure AD using OpenIdConnect.
//https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-webapp-webapi-openidconnect/
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions()
{
ClientId = ClientID,
Authority = Authority,
PostLogoutRedirectUri = PostLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthenticationFailed = PrincipalService.OnAzureAuthenticationFailure,
// REFERENCE: https://russellyoung.net/2015/09/05/mvc-role-based-authorization-with-azure-active-directory-aad/
AuthorizationCodeReceived = (AuthorizationCodeReceivedNotification notification) =>
{
var username = notification.AuthenticationTicket.Identity.Name.Split('#').LastOrDefault();
Logger.Log(Level.Auth, "Azure login success! Username: '" + username + "'.");
return Task.FromResult(0);
}
}
});
ARR Url Rewrite Settings
<rewrite>
<outboundRules>
<preConditions>
<preCondition name="ResponseIsHtml1">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
</preCondition>
</preConditions>
</outboundRules>
<rules>
<rule name="Secondary Server" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{CACHE_URL}" pattern="^(https?)://" />
</conditions>
<action type="Rewrite" url="https://qa2mobile.auntmillies.com/{R:1}" />
<serverVariables>
</serverVariables>
</rule>
</rules>
</rewrite>
Problem
Azure AD authentication works just fine normally, but once I setup a reverse-proxy it starts taking me to my own domain as if it's the identity provider.
e.g. https://my.domain.com/<guid>/oauth2/authorize?client_id=<guid>&etc
From this point, if I manually change my.domain.com to login.microsoftonline.com it'll log me in and proceed to work normally.
If I remove the reverse proxy and set the server up normally, it goes directly to login.microsoftonline.com instead of my domain.
What is causing this? I mean, obviously the reverse proxy, but how do I fix it without removing the reverse proxy?

What is causing this? I mean, obviously the reverse proxy, but how do I fix it without removing the reverse proxy?
As far as I know, if you use url rewirte rule in the web.config, it will rewrite all the request to the https://my.domain.com/ domain which is include the login.microsoftonline.com.
I suggest you could write a new rule which is used to match the /oauth2 request url to directly rediret to the login.microsoftonline.com instead of the https://my.domain.com/. Then it will work well.
More details, you could refer to below url rewrite rule.
<rewrite>
<rules>
<rule name="rule2" stopProcessing="true">
<match url="<talentid>/oauth2/(.*)" />
<action type="Redirect" url="https://login.microsoftonline.com/{R:0}" />
</rule>
<rule name="ReverseProxyInboundRule1" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="https://<domain>.com/{R:1}" />
</rule>
</rules>
<outboundRules>
<preConditions>
<preCondition name="ResponseIsHtml1">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
</preCondition>
</preConditions>
</outboundRules>
</rewrite>

Related

Redirect to root page on 403 response

I have a React App hosted on IIS. If I access an existing directory, such as <app_url>/static/, it returns a 403 response since there's no permission on that directory. I'd like to redirect the user to my root page (~/).
I have tried this:
<httpErrors>
<remove statusCode="403" subStatusCode="-1" />
<error statusCode="403" prefixLanguageFilePath="" path="/" responseMode="ExecuteURL" />
</httpErrors>
This kinda works, it does return the root page, however, it tries to download the React bundles from /static/ instead of '/' only.
What can I do here?
I used this rule and it worked:
<rewrite>
<rules>
<rule name="Redirect Static" patternSyntax="ExactMatch" stopProcessing="true">
<match url="static" />
<action type="Redirect" url="/" redirectType="Found" />
</rule>
</rules>
</rewrite>
(Thanks to #samwu)

React getting index.html response instead of API data from GET/POST methods after deploying to IIS v10

I'm following the instruction on how to build react app in the IIS. URL
However, for some reason, all my API calls are getting 404 errors.
Any hints or advice would be appreciated!
I'm using a proxy in order to bypass CORS policy on specific URLs.
Whenever I'm running using the terminal/code editor the API works perfectly, the response API errors only happen after I've deployed to the IIS.
Here are my codes:
Proxy i 've set:
const {createProxyMiddleware} = require("http-proxy-middleware");
module.exports = function(app) {
app.use(
createProxyMiddleware('/v3/quotes',{
target:'https://quote.dellsvc',
secure: false,
changeOrigin: true
})
);
web.config for react routes:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<cors enabled="true">
<add origin="*" >
<allowHeaders allowAllRequestedHeaders="true" />
</add>
</cors>
<rewrite>
<rules>
<rule name="React Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Terminal:- network request and console result:
Request URL: http://localhost:3000/v3/quotes?number=xxx&version=x
Request Method: GET
Status Code: 200 OK
Remote Address: 127.0.0.1:3000
Data response:
Able to retrieve the data correctly.
After deploying to IIS:- network request and console result:
Request URL: http://11.111.111.111:8000/v3/quotes?number=xxx&version=x
Request Method: GET
Status Code: 200 OK
Remote Address: 10.139.140.218:8000
Data response:
It shows the data of index.html instead of the API response data.
Note: The data is able to retrieve even though it has been deployed to IIS if without setting up a proxy (For example the API that is able to retrieve without the blockage from the CORS policy)

Why does my HTTP GET call to a subroute in react app gives a 404 NOT Found instead of HTML?

When I try to do a GET call to a subroute of my react application I get a 404.
When I do this to my homepage it returns correctly the HTML.
When just accessing the subroutes via the browser my webpage gets correctly rendered.
I'm Using React version 16.9.0.
I'm hosting the web application in Azure App service.
The reason for this question is to use an external program that can check for deadlinks in a website. At the moment I get 404's for every call to a subroute.
GET calls in Postman:
www.test.be => works
www.test.be/custom-route => gives a 404
This is my React Router Wrapper component:
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import Home from 'home/containers/home-container.js';
import NotFoundPage from './error-pages/not-found-page';
import Questions from 'questions/containers/question-container.js';
const Body = () => {
return (
<Switch>
<Route path="/" exact component={Home} />
<Route path="/questions" component={Questions} />
<Route component={NotFoundPage} />
</Switch>
);
};
export default Body;
This is my web.config file:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<httpRuntime requestPathInvalidCharacters="<,>,*,&,:,\\,?" />
</system.web>
<system.webServer>
<httpProtocol>
<customHeaders>
<remove name="X-Powered-By" />
</customHeaders>
</httpProtocol>
<rewrite>
<rules>
<rule name="HTTPSs">
<match url="(.*)"/>
<conditions>
<add input="{HTTPS}" pattern="Off"/>
<add input="{HTTP_HOST}" negate="true" pattern="^localhost" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}"/>
</rule>
<rule name="ReactRoutes" stopProcessing="true">
          <match url=".*" />
          <conditions>
            <add input="{HTTP_METHOD}" pattern="^GET$" />
            <add input="{HTTP_ACCEPT}" pattern="^text/html" />
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
          </conditions>
          <action type="Rewrite" url="/index.html" />
        </rule>
</rules>
</rewrite>
<security>
<requestFiltering allowDoubleEscaping="true"/>
</security>
</system.webServer>
</configuration>
You don't provide complete details but If I understand correctly, it can help you to solve your problem, if you have this problem on the server and when you open the homepage it works correctly and when open a subroute it goes to 404 page, add this to your .htaccess file
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
This is because how reactjs applications gets deployed on your production WebServer.
Reactjs applications are served deployed on with single index.html. Entire entire react project is served from index.html (This is how 'www.test.be' works). React-router programmatically updates the browser URL based on which page you navigate.
When your visit 'www.test.be/custom-url', the request is first caught and processed by the webserver. The webserver is expecting HTML file named 'custom-url.html' doesn't find it. So webserver throws a '404' i.e. 'Page not found' error. The request is not forwarded to your ReactApp - so your ReactApp never gets a chance to process the request.
The solution depends on the type of hosting service you are using. Basically, you want to redirect all URLs to React App (index.html) so that React Application will get a chance to process 404.
Update your question with the hosting service you are using (such as netlify, heroku, aws etc) and I can update this answer with a solution suitable for that hosting service.
For instance, if the webhost is netlify, you can create a file called _redirect in public/ directory with the following content
/* /index.html 200
That will redirect all traffic (*) to index.html
It happens because you don't have SSR (Server-Side Rendering). probably you're using CRA to create your ReactJs web application, so it works with CSR and the sub-routes couldn't be understood by server.
I don't mean bring SSR to your project, because you should be serious about using SSR for some special reasons. by the way, there is a very simple solutions for any kind of servers, you should redirect all requests to the index.html then react-router can understand the sub-route in the client:
Redirect all to index.html:
PM2 server:
pm2 start [path-to-build-folder/app.js] --spa
Serve:
serve -s [path-to-build-folder/app.js]
IIS:
<!--web.config url rewrite-->
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Redirect To Index" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/index.html" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
I don't know how you deployed your ReactJs web application, because some time DevOps specialist use running ReactJs app by using serve, pm2 or node under a special port for example: 8008 then by using IIS proxy try to connect the web application to local process, so by calling the port 80 user will be able to see the website. Also, you can directly serve the application by serving index.html in the IIS, and totally your solution is redirecting all requests to index.html.
Hope this answer helps you.
try this web.config :)
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="React Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>

How to configure IIS URL Rewrite module to work with "HTML 5 pushState" style and hash fragments on url?

Angular 2 has a PathLocationStragy to work with html5 routes and a HashLocationStrategy to work with hash fragments '#' on routes.
The issue here is when we use OIDC (OpenID Connect) for user authentication and PathLocationStragy (or html5mode for routes on AngularJS) because OpenID on some flows returns data after a '#' fragment after redirect back to the site after user logging with a url as follow:
http://mysite/callback#code=7b60c0570d7c4da0fc60678c0e46deadd5eeab0a659203fd44efb7cd51b2cdab&id_token=eyJhb ...
For html5 routes is very common the settings bellow on IIS Url Rewrite:
...
<system.webServer>
<rewrite>
<rules>
<remove name="AngularJS Routes" />
<rule name="AngularJS Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_URI}" pattern="/(api)/" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
</system.webServer>
...
But when the web sever receives the url with http://mysite/callback#code=7b60c0570d7c4d from Authorization Server after user logging the response for this request is: 403 - Forbidden: Access is denied.
I think that the problem is with the Url rewrite settings, not with AngularJS or Angular 2 app´s...
How to solve that? How to use AngularJS/Angular 2 with html5 route style and OpenID Connect?
Fragment values are not sent to the server, they are only handled client-side by the browser. The browser is only sending http://mysite/callback

ASP.net WebAPI ignoring web.config?

I am setting up a web.config in wwwroot to redirect all requests to index.html for angularjs to handle. This excludes existing files and folders and anything from /api.
The problem is, it's not working that way. I simply get blank pages for anything where a file does not exist.
Here is my web.config:
<configuration>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<handlers>
<add name="httpPlatformHandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified"/>
</handlers>
<httpPlatform processPath="%DNX_PATH%" arguments="%DNX_ARGS%" stdoutLogEnabled="false" startupTimeLimit="3600"/>
<rewrite>
<rules>
<rule name="IndexHomeRule" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/api/" negate="true" />
</conditions>
<action type="Rewrite" url="/index.html" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
I can't figure out any reason for it to still be trying to load the files instead of redirecting them to index.html
I am using .NET Core RC2 and I have static files and default files set to be used. I am using VS2015. I set the project up as a WebAPI from the beginning. Not empty.
You can use Microsoft.AspNetCore.SpaServices:
app.UseMvc(options =>
{
options.MapRoute("Api",
template: "api/{controller}/{action?}/{*path}",
defaults: new { controller = "Home" });
options.MapSpaFallbackRoute("spa-fallback", new { controller = "Home", action = "Index" });
});

Resources