.Net 6 react SPA and windows authentication does not get username - reactjs

I have a default react SPA page made with visual studio 2022 and im using windows authentication. My app will run only on an IIS hosted intranet app.
At my Program.cs, i have configured the authentication and authorization:
builder.Services.AddControllersWithViews();
builder.Services.AddAuthentication();
builder.Services.AddAuthorization();
builder.Services.AddHttpContextAccessor();
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = NegotiateDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = NegotiateDefaults.AuthenticationScheme;
}).AddNegotiate();
app.UseAuthentication();
app.UseAuthorization();
To test, i created a method app.Use to capture the username, and when i run it with the IISExpress profile, i can get the logged windows user successfully at the homepage
The breakpoint on the if clause is hit in every request on the application
app.Use(async (context, next) =>
{
    if (!context.User.Identity.IsAuthenticated)
    {
        await context.ChallengeAsync();
        await next();
    }
    else
    {
        
        await next();
    }
});
But when i call an endpoint to the example "weatherForecastcontroller" or i dont get the logged user on the httpContext, or the page sends me an Error 401.2 - Unauthorized. I also set up the credentials on the fetch call to "include"
    async populateWeatherData() {
      const response = await fetch('weatherforecast', { "credentials": "include" });
  const data = await response.json();
  this.setState({ forecasts: data, loading: false });
}
I also have set the launchsettings.json to accept only the windows authentication
"iisSettings": {
  "windowsAuthentication": true,
  "anonymousAuthentication": false,
  "iisExpress": {
    "applicationUrl": "http://localhost:53168",
    "sslPort": 44381
  }
Tried also to change the InProcess to OutOfProcess build and the same error occurs
Also, i created an mini web.config, to setup the security tag to allow windows authentication and tried to modiffy the .vs folder .vs<app>\config\applicationhost.config to change de windows and anonymous auth configs
If i use an MVC project on the same machine, it works like a charm, i think there's an proxy or another thing on SPAProxy that bugs things up
What am I missing to successfully get those credentials, any tips or workarounds?

Well, after many googling and testing, i came with a workaround:
The http-proxy-middleware mix up the request headers, and .Net Core does not let authorize, whathever i do;
Configure Authentication and enable cors in Program.cs, allowing it in all application, or just in the controllers that needs to be authenticated. The url in withorigins is the react development server. For a real application, use appSettings.<Development/Staging/Production>.json approach and include that variable as an array of strings of adresses. For debug in local IIS and in production environment, install the IIS Cors Module, downloading it by Microsoft
Program.cs
builder.Services.AddCors(options =>
{
options.AddPolicy("Padrao", builder =>
{
builder
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
.WithOrigins("https://localhost:44431");
});
});
builder.Services.AddAuthentication(configureOptions =>
{
configureOptions.DefaultAuthenticateScheme = NegotiateDefaults.AuthenticationScheme;
configureOptions.DefaultChallengeScheme = NegotiateDefaults.AuthenticationScheme;
}).AddNegotiate();
builder.Services.AddAuthorization();
var app = builder.Build();
Use the environment variable in React, to inject the desired backend host as the address. IMPORTANT: That variable needs to have whe REACT_APP_ prefix to the front end read it. Learned from jasonwatmore.com
.env.development
PORT=44431
HTTPS=true
REACT_APP_ASPNETCORE_URLS="https://localhost:44375/"
.env
REACT_APP_ASPNETCORE_URLS="https://localhost/testreact/"
Use the fetch with credentials and keep-alive headers
async populateWeatherData() {
const response = await fetch(process.env.REACT_APP_ASPNETCORE_URLS +'weatherforecast', {
method: "GET",
mode:'cors',
credentials: 'include',
headers: {
"Connection": 'keep-alive',
}
});
const data = await response.json();
this.setState({ forecasts: data, loading: false });}
Allow windows authentication and disable anonymous authentication. You will need to run the app as IISExpress profile, or create a IIS profile to debug, change it in launchsettings.json
"iisSettings": {
"windowsAuthentication": true,
"anonymousAuthentication": true,
"iis": {
"applicationUrl": "http://localhost/testereact",
"sslPort": 443
},
"iisExpress": {
"applicationUrl": "http://localhost:43938",
"sslPort": 44375
}
To allow Vs20220 to run in Local IIS, install de IIS Integration on the Visual Studio Installer. With this approach, you will need to call npm start in your /Clientapp by the Powershell command prompt (i didn't make it do call by himself)
I still do not tested this approach in production environment with a real application, but this solves the issue

Related

How to configure a react capacitor app running on mobile by usb to make http request to sendbird as localhost instead of its IP address?

I have a React webapp that I have converted it to native app using Capacitor. For live reloading, I had to edit capacitor.config.json as follows:
const config: CapacitorConfig = {
//
webDir: 'build',
bundledWebRuntime: false,
server: {
url: 'http://192.XXX.XXX:3000',
cleartext: true
},
};
This works just fine until the app makes a request to sendbird for initialization. I get the error message: 'SendBirdCall needs https connection, except for 'localhost' or '127.0.0.1'. I tried to setup an express https server but failed. Later I created two channels using ngrok - one for the server and one for the app. The sendbird request was successful but the graphql websocket was failing as ngrok does not support websocket links, also tried a tcp link but it failed as well.
I have another app that I created using the Sendbird React Native Quickstart project and I did not need to do any configuration for that. What could be done in order to make sendbird request using localhost from mobile connected via usb while also being able to have a ws connection?
Try to change the url:
const config: CapacitorConfig = {
...
server: {
url: 'http://localhost:3000',
cleartext: true
},
};
And after connecting a physical device execute the following:
cd $ANDROID_HOME/platform-tools
./adb reverse tcp:3000 tcp:3000
Where $ANDROID_HOME is the Android SDK directory.

add custom ca-bundle to next.js for server side calls

I have my site domain.com hosted on Vercel. The next.js application talks to a Laravel API deployed at a subdomain.domain.com on AWS for server-side rendering.
I bought a separate SSL certificate for the wildcard domains and added the CAA entries to the DNS for the CA authorities. I see the certificate verified and working fine in the browsers. However, the server-side rendering requests were failing with the following error (local development connecting to the API hosted at subdomain)
event - build page: /user/[profile_id]
wait - compiling...
event - compiled successfully
Error: unable to verify the first certificate
at TLSSocket.onConnectSecure (_tls_wrap.js:1321:34)
at TLSSocket.emit (events.js:210:5)
at TLSSocket._finishInit (_tls_wrap.js:794:8)
at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:608:12) {
code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE',
config: {
url: 'https://subdomain.domain.com/api/account/john-doe',
method: 'get',
headers: {
Accept: 'application/json',
Authorization: '',
token: '',
'User-Agent': 'axios/0.21.1'
},
.
.
I came across this package ssl-root-cas, and the issue is fixed (local development) and the pages load fine.
added this snippet to next.config.js
'use strict';
var rootCas = require('ssl-root-cas').create();
rootCas.addFile(__dirname + '/domain.ca-bundle');
// default for all https requests
// (whether using https directly, request, or another module)
require('https').globalAgent.options.ca = rootCas;
However, this doesn't seem to be working when I deploy to my staging site on Vercel.
My guess is Vercel doesn't have the domain.ca-bundle file? The file is added to the git version control, so should exist in the codebase when the build is generated.

OAuth responses require a User Pool defined in config

I am unable to connect to userpool which I defined right above: Auth is an amplify Object
Auth.configure({
region: process.env.REACT_APP_AWS_REGION,
userPoolId: process.env.REACT_APP_PROD_UNIFY_COGNITO_USER_POOL_ID,
userPoolWebClientId: process.env.REACT_APP_PROD_UNIFY_COGNITO_CLIENT_ID,
// Cognito Hosted UI configuration
oauth: {
domain: process.env.REACT_APP_PROD_UNIFY_COGNITO_DOMAIN,
scope: ['email', 'openid', 'aws.cognito.signin.user.admin', 'profile'],
redirectSignIn: 'http://localhost:3000',
redirectSignOut: 'http://localhost:3000',
responseType: 'code',
},
});
I get this error in my browser :Unhandled Rejection (Error): OAuth responses require a User Pool defined in config
AuthClass.
What else is there to connecting programmatically to a user pool. I didn't do amplify push,add auth or anything with the amplify cli b/c I don't think it's necessary?
This is most often down to the fact that you have changed your .env file, but not stopped and restarted your local web server.
For instance if you use yarn serve, make sure you stop the process, then update your .env, then yarn serve again.

Opening up Rails API to users within a closed network

I am attempting to run a React/Rails Website locally for 2-3 users within a closed network. Currently, the users can reach the React portion (I have that set to port 80). The problem is, the API calls I have set to my Rails backend are all on localhost:3001 and the users have zero access to that database when I try to submit HTTP requests.
I know it's a CORS issue but I thought the code below would allow any domain to make the necessary request.
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*'
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end
Server side work is not my forte, so I may be missing something glaring. Any idea how to open up that backend so the networked users can make API Calls on their end?
React is on this ip: http://192.168.2.70:80/
API calls on this port: 3001
Example API call from front end (works on host computer; not on other users):
getData = () => {
axios.get('http://localhost:3001/api/v1/pickup_deliveries')
.then((response) => {
this.setState({
apiData: response.data})
})
.catch((error)=>{console.log(error);});
}
The Problem
Unless I missed something, it looks like you're making a simple mistake that you're going to smack yourself for.
Typically, a CORS issue in a JavaScript application would yield a CORS/pre-flight request error error in your browser's JavaScript console.
The Misconception
The JavaScript that makes up a React application is downloaded by the client (i.e. your users). After that, the fetch call is executed on the client's computer. The fetch request doesn't originate from the server, but rather from the remote end.
It works on your computer because both React and the Rail API are hosted on your computer, so localhost:3001 resolves correctly. However, on your users computer, the React app attempts to find a service running on port 3001 on their computer.
Initial Solution
You need to tell React to reach out to the IP server that's hosting the API, like this:
getData = () => {
axios.get('http://192.168.2.70:3001/api/v1/pickup_deliveries')
.then((response) => {
this.setState({
apiData: response.data})
})
.catch((error)=>{console.log(error);});
}
Long Term Solution (for deployment)
In the future, look into dotenv, which will allow you to set React environment variables. You could have a .env.local file, and a .env.production file.
In the local file, you could put:
REACT_APP_BACKEND_SERVER=http://localhost:3001
and in the production on you could put:
REACT_APP_URL=http:/<server_ip>:3001
Then, in your program, do something like:
getData = () => {
axios.get(process.env.REACT_APP_SERVER_URL + "/api/v1/pickup_deliveries")
.then((response) => {
this.setState({
apiData: response.data})
})
.catch((error)=>{console.log(error);});
}
This will automatically resolve to localhost when serving React using npm run start and then resolve to <server_ip> when you are serving the static files generated by npm run build.
Feel free to comment on this answer. I would be happy to answer any other questions you have. Welcome to React!

Simple password-protection for React app on Heroku

I have a simple React app, created with create-react-app, that I'd like to deploy to Heroku (or somewhere easy) and password-protect. The protection can be really simple—just a single password is fine.
I started looking into HTTP basic auth but didn't find an easy answer. The closest I found was in this post, but (a) I don't love the idea of having to eject my app, and (b) I couldn't get it working. I was hoping I could find a Heroku plugin, but no luck there either.
It wouldn't be too hard to write a component that wraps my app and requests a password before showing it. The problem is that it executes client-side. I want to store the correct password server-side (or a hash thereof), and have the app send password attempts up to the server.
Since create-react-app operates on top of Node, I'm hoping there's an easy way to tell it to execute and store certain things on the server, but maybe I'm wrong. Any suggestions?
This create-react-app buildpack seems to support http basic auth:
https://github.com/substantial/create-react-app-buildpack
https://elements.heroku.com/buildpacks/substantial/heroku-buildpack-static
I am assuming your intentions are wanting to protect the config vars in heroku so other people cannot access you database with your credentials.
The way I password protected my deployment to heroku, is to make a keys_prod.js file containing the Heroku config vars of my mLab database in my backend using express and mongoDB:
keys_prod.js file:
module.exports = {
mongoURI: process.env.MONGO_URI,
secretOrKey: process.env.SECRET_OR_KEY
};
keys.js file:
if (process.env.NODE_ENV === 'production') {
module.exports = require('./keys_prod');
} else {
module.exports = require('./keys_dev');
}
in my server.js file I added:
// DB Config
const db = require('./config/keys').mongoURI;
// Server static assets if in production
if (process.env.NODE_ENV === 'production') {
// Set static folder
app.use(express.static('client/build'));
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
}
This allows you to request the config vars you filled in heroku without including it in your repo.
If you use Node in backend you can use Passport Basic Auth
app.get('*', passport.authenticate('basic', { session: false }), (req, res) => {
res.sendFile(path.join(`${__dirname}/../build/index.html`))
})
Every time you access the page in browser, a popup will appear, asking you username and password.

Resources