I have an IIS Website that runs on localhost:3000
using NodeJS, Express, passport and passport-windowsauth.
When I try to access the URL, it keeps prompting for user credentials - even if I type in the correct credentials. It simply pops up again. If I choose "Cancel", I obviously get the "Unauthorized". Struggling with this error since 3 full days and think about just giving up. I tried nearly every possible solution I could find, with no success.
And what's strange: This error only occurs on Windows-VM (Virtualbox). On a normal Windows-OS it works like a charm, with the exact same code and same IIS settings. This is driving me crazy.
What I tried:
- granting Full Access for IUSR / IUSRS group, put NTLM first, enabled Kernel Mode Authentication and set Extended Protection as "Accept" (suggested here)
- change Application Pool Identity to
Local Service
,Local System
orManaged System
- added
localhost
to "Trusted Sites" in Internet Options (even though that felt strange), as suggested here - tried enabling Anonymus Authentication with IUSR
- did security loopback check (suggested here and here)
- Rebooting after every action I did
- did
iisreset
after every action I did - checking IIS Node in Handler Mappings
- adding localhost manually to "Hosts"-file
overrideModeDefault = Allow
for Windows Authentication (and Anonymus Authentication as well) inapplicationHost.config
Also, I have no custom error pages that could be related to the problem.
server.js
:
const express = require('express');
const server = express();
const passport = require('passport');
const WindowsStrategy = require('passport-windowsauth');
server.use(passport.initialize());
// https://stackoverflow.com/questions/55296233/nodejs-express-passport-iis
passport.use('MyAuthStrategy', new WindowsStrategy({
integrated: true
},
function(profile, done) {
if (profile) {
user = profile;
return done(null, user);
}
else {
return done(null, false);
}
}
));
//https://stackoverflow.com/questions/19948816/passport-js-error-failed-to-serialize-user-into-session
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
const port = process.env.PORT || 3000;
// use windows-authentication
server.get('/',
passport.authenticate('MyAuthStrategy'),
function (req, res) {
res.json(req.user);
});
server.listen(port, () => {
console.log(`Listening on ${port}`);
});
And the web.config
:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<!--
All appSettings are made available to your Node.js app via environment variables
You can access them in your app through the process.env object.
process.env.<key>
-->
<!-- Unconmment the below appSetting if you'd like to use a Virtual Directory -->
<!-- <add key="virtualDirPath" value="" /> -->
</appSettings>
<system.webServer>
<!-- Remove the modules element if running on IIS 8.5-->
<modules runAllManagedModulesForAllRequests="false" />
<httpErrors existingResponse="PassThrough"></httpErrors>
<iisnode node_env="%node_env%" nodeProcessCountPerApplication="1" maxConcurrentRequestsPerProcess="1024" maxNamedPipeConnectionRetry="100" namedPipeConnectionRetryDelay="250" maxNamedPipeConnectionPoolSize="512" maxNamedPipePooledConnectionAge="30000" asyncCompletionThreadCount="0" initialRequestBufferSize="4096" maxRequestBufferSize="65536" uncFileChangesPollingInterval="5000" gracefulShutdownTimeout="60000" loggingEnabled="true" logDirectory="iisnode" debuggingEnabled="true" debugHeaderEnabled="false" debuggerPortRange="5058-6058" debuggerPathSegment="debug" maxLogFileSizeInKB="128" maxTotalLogFileSizeInKB="1024" maxLogFiles="20" devErrorsEnabled="true" flushResponse="false" enableXFF="false" configOverrides="iisnode.yml" watchedFiles="web.config;*.js" nodeProcessCommandLine="C:\Program Files\nodejs\node.exe" />
<!--
Before the handlers element can work on IIS 8.5
follow steps listed here https://github.com/tjanczuk/iisnode/issues/52
-->
<handlers>
<add name="iisnode" path="server.js" verb="*" modules="iisnode" />
</handlers>
<rewrite>
<rules>
<!-- Don't interfere with requests for node-inspector debugging -->
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^server.js\/debug[\/]?" />
</rule>
<!-- First we consider whether the incoming URL matches a physical file in the /public folder -->
<rule name="StaticContent" patternSyntax="Wildcard">
<action type="Rewrite" url="public/{R:0}" logRewrittenUrl="true" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
</conditions>
<match url="*.*" />
</rule>
<!-- All other URLs are mapped to the Node.js application entry point -->
<rule name="DynamicContent">
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True" />
</conditions>
<action type="Rewrite" url="server.js" />
</rule>
<rule name="SocketIO" patternSyntax="ECMAScript">
<match url="socket.io.+" />
<action type="Rewrite" url="server.js" />
</rule>
</rules>
</rewrite>
<directoryBrowse enabled="true" />
<security>
<authentication>
<anonymousAuthentication enabled="false" userName="IUSR" />
<windowsAuthentication enabled="true" useKernelMode="true">
<extendedProtection tokenChecking="Allow" />
<providers>
<clear />
<add value="NTLM" />
<add value="Negotiate" />
</providers>
</windowsAuthentication>
</authentication>
</security>
</system.webServer>
<system.web>
<authentication mode="Windows" />
<authorization>
<allow users="*" />
</authorization>
<identity impersonate="false" />
</system.web>
</configuration>
Here the installed Features:
Any suggestions will be greatly appreciated. I really don't know what to do.