Identityserver4 - Hosting in IIS - core

How to host ASPNET CORE APP with IdentityServer4 in IIS. The app is running fine from localhost but not as a web application setup in IIS.
For Example,
http://localhost:5000/connect/token is working but http://example.com/myauthapp/connect/token is not reachable - returning 500 - internal server error when tried from a console app using identity model or via postman. I am able to login to the app using web browser but not thru a console app or postman.
Further Troubleshoot and I find the below.
An unhandled exception has occurred: IDX10638: Cannot created the SignatureProvider, 'key.HasPrivateKey' is false, cannot create signatures. Key: Microsoft.IdentityModel.Tokens.RsaSecurityKey.
System.InvalidOperationException: IDX10638: Cannot created the SignatureProvider, 'key.HasPrivateKey' is false, cannot create signatures. Key: Microsoft.IdentityModel.Tokens.RsaSecurityKey.
at Microsoft.IdentityModel.Tokens.AsymmetricSignatureProvider..ctor(SecurityKey key, String algorithm, Boolean willCreateSignatures)
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.CreateEncodedSignature(String input, SigningCredentials signingCredentials)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.WriteToken(SecurityToken token)
at IdentityServer4.Services.DefaultTokenCreationService.CreateJwtAsync(JwtSecurityToken jwt)
at IdentityServer4.Services.DefaultTokenCreationService.<CreateTokenAsync>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
How can I solve this issue?

As Arun noted in his comment, the certificate has to be installed on the server.
1 . To test this on localhost first, make sure you are using "AddSigningCredential" not "AddTemporarySigningCredential".
services.AddIdentityServer()
.AddSigningCredential(new X509Certificate2(Path.Combine(_environment.ContentRootPath, "certs", "IdentityServer4Auth.pfx")));
//.AddTemporarySigningCredential()
;
Create the certificate in your project (create certs folder), running this in visual studio command:
"C:\Program Files (x86)\Windows Kits\8.1\bin\x64\makecert" -n
"CN=IdentityServer4Auth" -a sha256 -sv IdentityServer4Auth.pvk -r
IdentityServer4Auth.cer -b 01/01/2017 -e 01/01/2025
"C:\Program Files (x86)\Windows Kits\8.1\bin\x64\pvk2pfx" -pvk IdentityServer4Auth.pvk
-spc IdentityServer4Auth.cer -pfx IdentityServer4Auth.pfx
Test on localhost
If successful, deploy to iis server , install the certificate on the server by double clicking on it, and test.
Make sure the application pool "load user profile" is set to true :
Go to IIS Manager
Go to the application pool instance
Click advanced settings
Under Process model, set Load User Profile to true
Restart IIS
If this fails with a 500, like with me (and there is no logs to help you out), try this. To fix this recreate the certificate on the server the same way as in step 2 in the certs folder . double click the cert to install.
You might have to install the developer kit if you dont have visual studio installed:
https://developer.microsoft.com/en-us/windows/downloads/windows-8-1-sdk

A little background first, on why it is working on your Local Development computer and not running under IIS on a QA or Production Environment. If you are using Temporary Signing Credential when adding the service Identity Server 4 like so,
services.AddIdentityServer().AddTemporarySigningCredential();
Then you have to make sure the "User" that the "Process" is running as has a Private Key available for ID4 to create a Temporary Certificate. This is why the error message is
SignatureProvider, 'key.HasPrivateKey' is false, cannot create
signatures. Key: Microsoft.IdentityModel.Tokens.RsaSecurityKey.
In the case of Windows, this private key is generated by Windows automatically and can be found at the folder %APPDATA%\Microsoft\Crypto\RSA of the User or C:\Users\Username\AppData\Roaming\Microsoft\Crypto\RSA. The reason why this Private Key is missing is perhaps because the User that your Process is running as has never logged onto that Computer.
The likely SOLUTION in that case is to log in once as the User that will be running the Process on that Server. It is quite common for the Private Key directory to be missing if your Application Pool within IIS runs as a "non-service" user with very High Privileges and that user has never interactively logged onto the Server itself. This also explains why "localhost" works on your development computer, while running on a Production or QA Server may not.
More information on how and where Windows creates the Private Key for a User can be found here in this link Microsoft Key Storage and Retrieval. Also, it is recommended as mentioned by David Smit to explicitly specify a Private Key file, instead of using Temporary Signing Credentials. That is the cleaner solution if you are allowed to make code changes.
services.AddIdentityServer()
.AddSigningCredential(new X509Certificate2("C:\Certs\IdentityServer4PrivateKeyFile.pfx")));

Related

How to Delegate Credentials through double hop to SQL Server?

What I am trying to do:
We have a Task Scheduler that kicks off an EXE, which in the course of its runtime, will connect to SQL Server.
So that would be:
taskServer.myDomain triggers the Task Scheduler action
taskServer.myDomain exe runs locally
taskServer.myDomain initiates a connection to sqlServer.myDomain
The scheduled task is associated with a service account (svc_user) that is set to run with highest privilege, run whether the user is logged in or not, and store credentials for access to non-local resources.
The actual behavior
What we are seeing is the Task Scheduler is indeed running as svc_user. It triggers the EXE as expected, and the EXE is also running as svc_user. When the EXE initiates a connection to SQL Server, it errors on authentication.
Looking at the Event Viewer we can see the failure trying to initialize the connection to SQL
Exception Info: System.Data.SqlClient.SqlException
at System.Data.SqlClient.SqlInternalConnectionTds..ctor(System.Data.ProviderBase.DbConnectionPoolIdentity, System.Data.SqlClient.SqlConnectionString, System.Data.SqlClient.SqlCredential, System.Object, System.String, System.Security.SecureString, Boolean, System.Data.SqlClient.SqlConnectionString, System.Data.SqlClient.SessionData, System.Data.ProviderBase.DbConnectionPool, System.String, Boolean, System.Data.SqlClient.SqlAuthenticationProviderManager)
And then looking at the SQL Server logs we can see the root of the issue
Logon,Unknown,Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON'. Reason: Could not find a login matching the name provided.
The connection initialized by the EXE to SQL Server is trying to authenticate as ANONYMOUS LOGON.
What I have tried
Background
This issue popped up when our IT team started deploying a GPO lockdown in our environments. So in order to get to this point, we first had to add some GPO exceptions to allow the svc_user to:
log on locally
log on as batch job
Progress?
This is where we started being able to capture the ANONYMOUS LOGON error in SQL Server. From there we tried a handful of other GPO exceptions including
Allow Credential Save
Enable computer and user accounts to be trusted for delegation
The actual issue?
So it would appear that this is a double hop delegation issue. Which eventually led me here and then via the answer, here and here.
So I tried adding GPO policies to allow delegating fresh credentials using the WSMAN/* protocol + wildcard.
Two issues with this:
the Fresh credentials refer to prompted credentials while the EXE is running as a service during off-hours and inheriting the credentials from the TaskScheduler
the WSMAN protocol appears to be used for remote PowerShell sessions (via the original question in the serverfault post) and not SQL Service connections.
So, I added the protocol MSSQLSvc/* to the enabled delegation and tried all permutations of Fresh, Saved and Default delegation. (This was all done in Local Computer Policy -> Computer Configuration -> Administrative Templates -> system -> Credentials Delegation)
Where it gets weird
We have another server, otherServer.myDomain, which we setup with the same TaskSchedule. It is setup with the same GPO memberships, but seems to be able to successfully connect to SQL Server. AFAIK, the servers are identical as far as setup and configuration.
The Present
I have done a bit more digging into anywhere I could think that might offer clues as to how I can feed the credentials through or where they might be falling through. Including watching the traffic between the taskServer and the sqlServer as well as otherServer and sqlServer.
I was able to see NTLM challenges coming from the sqlServer to the taskServer/otherServer.
In the case of taskServer, the NTLM response only has a workstationString=taskServer
On otherServer, the NTLM response has workstationString=otherServer, domainString=myDomain, and userString=svc_user.
Question
What is the disconnect between hop 1 (task scheduler to EXE) and hop 2 (EXE to SQL on sqlServer)? And why does this behavior not match between taskServer and otherServer?
So I finally have an update/solution for this post.
The crux of the issue was a missing SPN. The short answer:
Add an SPN for sqlServer associated with the service account SQL services are running as (not the svc_user)
example: SetSPN -S MSSQLSvc/sqlServer.myDomain myDomain\svc_sql_user
Add another SPN like above but w/ the sql service port
example: SetSPN -S MSSQLSvc/sqlServer.myDomain:1433 myDomain\svc_sql_user
Set the SQL service user account to allow delegation like so

<server\instance> Jobname The job failed. The Job was invoked by User <Domain\Use name>. The last step to run was step 1 (<Stepname>)

This is our first SSIS package created on xx.xx.xx.77 for pull data from xx.xx.xx.56 server and load on share path (server ip xx.xx.xx.130) as an Excel file. That path is accessible from both servers.
SQL Server Agent is configured with NT Service\SQLAgent$instance in xx.xx.xx.77 server.
So we got Error1.
Error1:
Msg: The application-specific permission settings do not grant Local
Activation permission for the COM Server application with CLSID
{FDC3723D-1588-4BA3-92D4-42C430735D7D} and APPID
{83B33982-693D-4824-B42E-7196AE61BB05} to the user NT
SERVICE\SQLAgent$instance SID
(S-1-5-80-3789775531-3388834719-430539279-2094781381-3838682281) from
address LocalHost (Using LRPC) running in the application container
Unavailable SID (Unavailable). This security permission can be
modified using the Component Services administrative tool.
Log Type: Windows NT Log Source: System Source:
Microsoft-Windows-DistributedCOM Categroy: (0) Event: 10016 User:
NT SERVICE\SQLAgent$instance Computer:
After Google search we go with Proxy user. Now we facing issue (Error2) in scheduling that package on xx.xx.xx.77 server using proxy user.
Error2:
The job failed. The Job was invoked by
User. The last step to run was step 1 ()
FYI: Although same Proxy user has been created on xx.xx.xx.55 server, same error persist while run job on 77 server.
Reference Links:
Give access to virtual account. But we cant do this due to our privileges is not enough.
Proxy Creation
We refer more SO Q&A and Google results for SSIS package creation. No one helps. Because our scenario includes two servers. So we need helping hand.
Edit 1:
We did this after Error1.
"Launch & Activation Permission" -> click Edit -> Add in the account NT Service\SQLAgent$instance-> Name Not Found error. Ref: below image. Same error for, add "Service Account" in Object Type on below dialogue box.
Then we change location from Domain.com to Server Name in Location as follows:
Now, NT Service\SQLAgent$instance name is found. Then we check "Local Launch & Local Activation". Ref:
After restart the Service, we got same error. Thats' we go with Proxy User.
Reference link for Deploy the SSIS package.
EDIT 2
We move the SSIS project to another xx.xx.xx.231 server, where SQL Service Agent is configured with Domain user like backupuser#domain.com. Now the job is run successfully on scheduled time.
FYI: Another reason of the error is the destination file is may be opened by another user. Here, file is in shard location. When run the job on xx.xx.xx.231 server, we got same error again. After some cross checking, we found that Excel file is opened by our Team met.
Thanks to All,
Pugal
You might need to provide permission using the below instruction.
Click Start -> Run -> Type -> dcomcnfg, expand Component Services -> Computers -> My Computer -> DCOM Config.
Click View -> Detail -> Now you will get Application Name and Application ID in right side.
Scroll down and find the application ID " " -> Right Click -> Properties and select the Security tab.
Click Customize under "Launch & Activation Permission" -> click Edit -> Add in the account NT AUTHORITY\SYSTEM and set local launch and local activation.
Restart the application Service linked to this Application ID or restart the server and monitor

Error 500 when deploying site from localhost to IIS

I finally want to deploy my asp.net WebApi to IIS 10.
The site works flawless on localhost, but as soon as I try to do anything Database related, I get an Internal Server Error (500).
I checked the connectionstring and it seems to be correct.
I added a user for Domain\MachineName$ on SQL Server.
my connectionstring:
<add name="myDB" connectionString="data source=correctserver;initial catalog=correctdatabase;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
one of the failing requests:
var request = new XMLHttpRequest()
request.open('GET', hostedserverurl + '/v1/correctapicall/' + correctrequestparameter, true)
Error happens here:
request.send()
Expected result: the same as from localhost, JSON if using direct API call.
Actual result:
Errormessage: ("Fehler" is german for "Error")
{"Message":"Fehler"}
I spent quite some time on this and now I'm on the finish line. But it seems it's more a finish wall...
What do I need to configure? I did not setup anything special.
EDIT:
After enabling more extensive error messages with the help of the webconfig, I was able to get to this inner exception:
"ExceptionMessage": "Die SELECT-Berechtigung wurde für das __MigrationHistory-Objekt, correctdatabase-Datenbank, dbo-Schema, verweigert.",
"ExceptionType": "System.Data.SqlClient.SqlException",
This leads me to believe this is a user priviledge related thing... But I already created a user for the IIS.
I get this every time I copy the production database, rename it to be my dev database and forget to add my dev user to the new dev db - the very first thing that EF tries to do (when you carry out any database action) is establish the existence/setup the migrationhistory table - if the user it's using doesn't have the privileges or doesn't exist, an error like this occurs
Ensure that the login/user pair the application pool is running under, exists in the database. If your IIS App Pool is called Wiggle, then you'll need to create a login/user pair for [IIS APPPOOL\Wiggle] inside the DB and ensure that that user has rights to create tables, perform all CRUD operations etc
You can see which app pool your site is using in IIS Manager; select the site, and click basic settings in the top right
ps; if this still isn't working out, you should post up some screenshots of the config pages where you have set up your site, your site's app pool (and the local identity it uses), and your SQL server user. Don't obscure them like I have here, because we need to see them to check what they're set to:
Website settings:
App pool settings:
SQLS settings:

Asp.NET MVC 4 deployment issues related to LocalDb and SQLExpress

I am trying to deploy a Asp.NET MVC 4 app to a third-party host, and encountered some problems while following this tutorial.
The tutorial created 2 New SQL Server Database, and set the Remote connection string of DefaultConnection to one SQLExpress database and that of SchoolContext to another. However, I want to have only one database, so I only created one New SQL Server Database, and used the same connection string for DefaultConnection AND MyAppContext. Would this cause problem?
Right now my app won't publish with the following error. not sure if it stems from this. MyAppUsr comes from a Grant script, Grant.sql, that I developed from this tutorial.
Error 3 Web deployment task failed. (An error occurred during execution of the database script. The error occurred between the following lines of the script: "8" and "11". The verbose log might have more information about the error. The command started with the following:
"CREATE USER [MyAppUsr] "
The login already has an account under a different user name. http://go.microsoft.com/fwlink/?LinkId=178587 Learn more at: http://go.microsoft.com/fwlink/?LinkId=221672#ERROR_SQL_EXECUTION_FAILURE.) 0 0 MvcCP
MyAppContext uses LocalDb instead of SQLExpress. The deployment in the tutorial uses SQLExpress. Can I just create a new SQLExpress database and have MyAppContext (LocalDb) "points to" the SQLExpress database?
You must have run the script at least once prior, correct? Because what it is saying is that the user [MyAppUsr] already exists. You will need to drop the user first, then rerun it. ie:
DROP USER [MyAppUsr]
You can embed that into your script, better yet, put a conditional check if exists...

Entity framework returns null when hosted

I have a silverlight application where when I run the application I read the database and populate the results in the combobox, but when I publish it on the server(IIS) the it returns me a null, I can't even debug the error because it return the result when I host it on my system but null when hosted..
Please guide...
Taking a stab in the dark here. Most likely this is a security issue. When debugging locally, your web server runs under your credentials. If using integrated authentication to your database, then the web service host acts like it is you when making requests to the database. However, when you deploy, the server tries to connect to the database using the credential associated with the application pool that is running your site. Check your database connection string to see if it is pointing to the right database and try using a named user/password with an account that does have access to the database.
If this doesn't fix the problem, try accessing one of your services directly (using Fiddler?) to see what the service is returning as an error message that your Silverlight client is ignoring.

Resources