ASP.NET Core 6 and react Windows authentication - reactjs

I have an ASP.NET Core 6 Web API for Windows authentication which is like this:
[HttpGet("GetIdentity")]
public IActionResult GetIdentity()
{
var userName = HttpContext.User.Identity.Name;
return Ok(new { UserName = userName });
}
and everything works fine. Then I called this API from reactjs like this:
import React, { useEffect, useState } from "react";
function UserNameApi() {
const [users, setUser] = useState("");
const fetchData = () => {
return fetch("http://localhost:8000/User/GetIdentity",{withCredentials:true})
.then((res) => {res.json()
.then((result) => setUser(result.userName));
});
};
useEffect(() => {
fetchData();
}, []);
return (
<div>
<p>UserName:{users}</p>
</div>
);
}
export default UserNameApi;
I published both server and client side on IIS but as I open the page, I get an http 401 unauthorized error. I can call the API in the browser, and also used postman and by setting NTLM it works too. I have to add that I have set CORS in API and IIS too.
I will appreciate it if anyone can help me.
Thanks in advance

UPDATE
After adding below code and facing CORS issue.
Change AddCors code like below can fix it.
// Add services to the container.
builder.Services.AddCors(options =>
options.AddPolicy("CorsPolicy", builder =>
{
builder.AllowAnyMethod()
.SetIsOriginAllowed(_ => true)
.AllowAnyHeader()
.AllowCredentials();
}));
...
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCors("CorsPolicy");
app.UseRouting();
app.UseAuthorization();
...
In addition to setting "withCredentials" to "true", you should also set "credentials" to "include". This should allow the browser to send the Windows credentials to the server.
Code should like below:
const fetchData = () => {
return fetch("http://localhost:8000/User/GetIdentity",{
withCredentials: true,
credentials: "include"
})
.then((res) => {
res.json()
.then((result) => setUser(result.userName));
});
};

Related

AxiosError with Get Method give me 404 Error Page

When I request data from Mongoose, it shows me an error and tells me that the page does not exist, knowing that I tested the back-end in Postman and it succeeded. I also tested the react by fetching data from an external link and it succeeded. I do not know what the problem is?!
I try with this code
This is my back-end code
const app = require("./app");
const mongoose = require("mongoose");
app.set("port", process.env.PORT || 3000);
const mongoURI = "mongodb://localhost:27017/get-now";
mongoose.connect(mongoURI, {
    useNewUrlParser: true,
    useUnifiedTopology: true
})
.then(() => console.log(`database connected`))
.catch(err => console.log(err));
const port = process.env.PORT || 3000;
app.listen(port, () => {console.log(`the server is ${port}`)});
routes.get("/alldata" , async (req, res) => {
try {
const foodDatas = await Data.find({})
res.send(foodDatas);
console.log(foodDatas)
} catch (error){
res.status(500).json({
Error: error
})
}
});
This is my front-end (Reactjs) code
const fetchData = async () => {
    axios.get('http://localhost:3000/alldata')
  .then((res) => {
    console.log(res.data)
}).catch (err => {
console.log(err)
})
    }
  useEffect(() => {
    fetchData()
  }, [])
All these don't work it give me Error 404 page with data: undefined
did you configure the cors in the app file?
if don't, please install cors.
and add:
const cors = require('cors')
also:
app.use(
cors({
origin: "*",
})
);
Note:
Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources

CORS Even After Enabling

I am developing a web application with react. To call the API's I am using HTTP triggers in Azure function apps. All the API's are working without an issue. I am in the integrating process. I came across this CORS issue. I have tried to overcome this but I am still getting the same error. and I dont have an express.js
Program.cs
var builder = WebApplication.CreateBuilder(args);
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(
policy =>
{
policy.WithOrigins("http://localhost:3000");
});
});
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddDbContext<EmployeeDBContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("EmployeeDB")));
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.Run();
ReactApp
const makeAPICall = async () => {
try {
const response = await fetch('http://localhost:7182/api/GetEmployees', {mode:'cors'});
const data = await response.json();
console.log({ data })
}
catch (e) {
console.log(e)
}
}
useEffect(() => {
makeAPICall();
}, [])
return (
<div className="App">
<h1>React Cors Guide</h1>
</div>
);
}
Updated
I found out that the problem is Missing Header.
Middlewares like this have to be places in the pipeline in correct order and sometimes other middlewares have to be enabled
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-7.0#middleware-order
According to your issue, it seems that app.UseRouting() is missing (may be something else, article above definitely knows it better)

Proxying requests in a create-react-app application

I have been looking all over to try and figure out why this doesn't work. I have two applications, a client and a server. I'd like to proxy the client requests to the server. I have a route called /api/repositories. I can't have the proxy in the package.json, because it needs to work in production, also.
It is a Create React App project. Here are the important files.
setupProxy.js
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = (app) => {
app.use(
createProxyMiddleware("/api", {
target: "http://my.server.com",
changeOrigin: true,
onProxyReq: (proxyReq) => {
console.log("logged");
if (proxyReq.getHeader("origin")) {
proxyReq.setHeader("origin", "http://my.server.com");
}
},
})
);
};
And I use it in a functional React component called Searchbar, as such:
Searchbar.js
import axios from "axios"
async function getRepos() {
const response = await axios({
method: "GET",
url: "/api/repositories",
});
return response.data;
}
function Searchbar() {
const [repos, setRepos] = useState([]);
// Get the repos on load
useEffect(async () => {
setRepos(await getRepos());
}, []);
return (
<div>
{repos.map((repo) => <p>{repo}<p>)}
</div>
);
}
However, when I run npm run start and run the development server, all of my API requests are going to http://localhost:3000/api/repositories, which obviously returns a 404 error. What am I doing wrong?

Adjusted proxy settings in Create-React-App to get around CORS blocking error and now get an undefined response

I'm having issues after adding a work-around for CORS blocking a request within Create-React-App.
As recommended I have updated the proxy settings to the same domain I am trying to call in my package.json (example with dummy data)
"proxy": "https://jsonplaceholder.typicode.com"
and make the call in my App.js file as follows..
import React, { useState, useEffect } from "react";
function App() {
const [products, setProducts] = useState([]);
useEffect(() => {
fetch("/posts/")
.then((res) => {
console.log(res.data);
})
.then((res) => setProducts(res.data));
});
return <div className="App">Returned here</div>;
}
export default App;
I don't get the CORS error anymore, which is good, however I do get the following error message
Unhandled Rejection (TypeError): Cannot read property 'data' of undefined
Tested the API in postman and its all returning correctly so not sure where the issue is.
Any help would be appreciated
Code sandbox doesn't seem to be using create-react-app out of the box.
From looking at the code:
fetch("/posts/")
.then((res) => {
console.log(res.data);
})
.then((res) => setProducts(res.data));
Should be adjusted to
useEffect(() => {
fetch("/posts")
.then((res) => {
return res.json();
})
.then((data) => {
console.log(data);
setProducts(data);
});
}, []);

'AADSTS500011' error message returned from API call using adalFetch

I have a React application that is registered in Azure Active Directory. In the API Permissions section, I have added permissions to access the API I am trying to access.
I am using the react-adal package to handle login and storage of access tokens when the user enters the app. My understanding is that the access token for the API is created at this point and adalFetch handles the logistics during the call to the API.
The response from the API is an error object (I replaced the actual id's; yes they match exactly and are correct in AAD):
{
message: "AADSTS500011: The resource principal named https://<domain>.onmicrosoft.com/APP_ID/access_as_user was not found in the tenant named TENANT. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant."
msg: "invalid_resource"
}
I have searched high and low to find a solution to why this isn't working. There is documentation on the API, but none specifying a resource or anything beyond the various endpoints i.e. http://thing-api.azurewebsites.net/api/endpointGoesHere
The API page states:
To use the API, apps need to implement modern authentication (OIDC) using AzureAD (AAD) and then request a token from AAD for the API.
The app id in Azure is https://domain.onmicrosoft.com/APP_ID and requires the “access_as_user” scope.
adalConfig.js
import { AuthenticationContext, adalFetch, withAdalLogin } from 'react-adal';
export const adalConfig = {
clientId: CLIENT_ID,
tenant: TENANT,
endpoints: {
thingApi: 'https://<domain>.onmicrosoft.com/APP_ID/access_as_user',
graphApi: 'https://graph.microsoft.com',
},
cacheLocation: 'localStorage',
};
export const authContext = new AuthenticationContext(adalConfig);
export const adalApiFetch = (fetch, url, options) =>
adalFetch(authContext, adalConfig.endpoints.thingApi, fetch, url, options);
export const adalGraphFetch = (fetch, url, options) =>
adalFetch(authContext, adalConfig.endpoints.graphApi, fetch, url, options);
Function for the API call. Executed in componentDidMount.
TrainLanding.jsx
//Returns error
fetchData = () => {
adalApiFetch(fetch, 'http://thing-api.azurewebsites.net/api/EventGet', {})
.then((response) => {
response.json()
.then((responseJson) => {
this.setState({ apiResponse: JSON.stringify(responseJson, null, 2) }, () => {
console.log(this.state.apiResponse)
})
});
})
.catch((error) => {
console.error(error);
})
}
//works perfectly fine
fetchGraph = () => {
adalGraphFetch(fetch, 'https://graph.microsoft.com/v1.0/me', {})
.then((response) => {
response.json()
.then((responseJson) => {
this.setState({ apiResponse: JSON.stringify(responseJson, null, 2) }, () => {
console.log(this.state.apiResponse)
})
});
})
.catch((error) => {
console.error(error);
})
}
I set up a graph API call in the exact same way to test the method, and it works perfectly fine. So I know adal is set up correctly, I just don't understand the error and where I am going wrong. My googling has not yielded any useful results.
Ok, so if you're here, some things to note:
Don't use ADAL. Use MSAL. ADAL is v1 and does not work. Read here for examples: https://www.npmjs.com/package/react-aad-msal
You should wrap your entire app inside the component you get from above. I will show how I did it below.
You must have already registered your app in Azure Active Directory, configured redirect URLs, and included API permissions.
index.js
import { AzureAD, MsalAuthProviderFactory, LoginType } from 'react-aad-msal';
import { msalConfig, authParams } from './msalConfig';
class Index extends Component {
state = {
userInfo: null,
}
userJustLoggedIn = (accInfo) => {
this.setState({
userInfo: accInfo
})
}
render() {
return(
<AzureAD
provider={
new MsalAuthProviderFactory(msalConfig, authParams, LoginType.Redirect)
}
forceLogin={true}
accountInfoCallback={this.userJustLoggedIn}
>
<HashRouter>
<App userInfo={this.state.userInfo}/>
</HashRouter>
</AzureAD>
);
}
}
ReactDOM.render(
<Index/>, document.getElementById('root')
);
This might not be what your index looks like if you are using the most recent version of Create React App. I converted the Index into a component for a couple of reasons. First, the authentication loop for me was getting stuck 1 refresh short when redirecting. Second, so I could store the logged in user's info in state, update with setState (which forces another render), and then pass it as a prop to the rest of my app.
msalConfig.js
export const msalConfig = {
auth: {
authority: process.env.REACT_APP_AUTHORITY, //this should be "https://login.microsoftonline.com/<your-tenant-id>"
clientId: process.env.REACT_APP_CLIENT_ID, //just "<your-client-id>"
redirectUri: process.env.REACT_APP_REDIRECT //"<url of your app or localhost port you dev on>"
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true
}
};
export const authParams = {
//can be whatever api scopes you need here **as long as they are from the same API address**
scopes: [
'https://graph.microsoft.com/User.ReadBasic.All',
'https://graph.microsoft.com/email',
'https://graph.microsoft.com/profile',
'https://graph.microsoft.com/User.Read'
],
extraScopesToConsent: [
//any non Microsoft Graph API scopes go here for this example
'any extra strings of APIs to consent to'
]
}
Read above env files and variables here: https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#what-other-env-files-can-be-used
I have a .env.development and a .env.production with the proper redirect URLs for each.
After you have authenticated the user, you can access the API.
You need to acquire a token silently before each API call and use the token in the request. For me it looks like this:
const authProvider = new MsalAuthProviderFactory(msalConfig, authParams);
console.log(authProvider)
authProvider.getAuthProvider().UserAgentApplication.acquireTokenSilent(authParams)
.then((res) => {
axios({
headers: {
'Authorization': 'Bearer ' + res.accessToken
},
method: 'GET',
url: "api address"
})
.then((response) => {
//do stuff with response
console.log(response)
})
.catch((error) => {
console.log('axios fail: ' + error)
})
})
.catch((error) => {
console.log('token fail: ' + error)
})
I put this into a function and called during componentDidMount.
I will update if anything changes. I hope this helps someone.

Resources