How to make the MS Identity SPA example work for organization and personal accounts - asp.net-core

I am following this link to make my own project.
My project has
Reactjs
Asp.Net Core Web Api
to access user's emails from Hotmail.The Reactjs app and WebApi live on different servers.
The example is almost what I need, but it only accepts accounts within same organization, not accepts Personal accounts.
I thought I only need to change the Tenant ID from a specific ID to "common" in related configurations and it will work.
I also registered the Supported account types on Azure for both Web Api and Reactjs to use the
AnyOrg + Personal Account
here is the config for Angular
{
"auth": {
"clientId": "28xxx12",
"authority": "https://login.microsoftonline.com/common",
"validateAuthority": true,
"redirectUri": "http://localhost:4200",
"postLogoutRedirectUri": "https://localhost:44321/signout-oidc",
"navigateToLoginRequestUrl": true
},
"cache": {
"cacheLocation": "localStorage"
},
"scopes": {
"loginRequest": [ "openid", "profile", "Mail.Read", "offline_access", "user.read"]
},
"resources": {
"todoListApi": {
"resourceUri": "https://localhost:44351/api/todolist/",
"resourceScope": "https://papayee008.onmicrosoft.com/papayee008/access_as_user"
}
}
}
here is the config for Web Api:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "papayee008.onmicrosoft.com",
"TenantId": "common",
"ClientId": "28xxx12"
},
"https_port": 44351,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
But when I was trying to log in with my personal account, it throws me this error:

The simplest solution would be the use the same app/client id for both the SPA and the API. That is, instead of making separate app registrations for each component (as is suggested in the readme), simply make one app registration where you combine the steps for Register the service app (TodoListAPI) and Register the client app (TodoListSPA) in the readme of the sample repository.
EDIT: judging by the last screenshot, it might also be the case that your changes on "supported account types" on the AAD portal haven't taken effect yet. There's usually a few seconds delay, and if you tried to login with a personal account during that time, the issue in the screenshot would be expected.

Related

Rate limit policy settings is not applied using AspNetCoreRateLimit

I am using AspNetCoreRateLimit version 4.0.1 and I have done all the setup in .net core 6 web api. I can see rate limit is working when I send a call via postman.
However, when I add IpRateLimitPolicies with specific IP address, the settings won't be applied.
I use postman and this time in the proxy I added the ip address to 127.0.0.1. I can see the ip hitting the api is set correctly when I use Request.HttpContext.Connection.RemoteIpAddress;
I registered them as follow in program.cs:
_serviceCollection.AddOptions();
_serviceCollection.AddMemoryCache();
_serviceCollection.Configure<IpRateLimitOptions>(builder.Configuration.GetSection("IpRateLimiting"));
_serviceCollection.Configure<IpRateLimitPolicies>(builder.Configuration.GetSection("IpRateLimitPolicies"));
_serviceCollection.AddInMemoryRateLimiting();
_serviceCollection.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
_serviceCollection.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
_serviceCollection.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
_serviceCollection.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
Also added:
app.UseIpRateLimiting();
My appsettings also looks like:
{
"IpRateLimiting": {
"EnableEndpointRateLimiting": false,
"StackBlockedRequests": false,
"RealIPHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"IpWhitelist": [ ],
"EndpointWhitelist": [],
"ClientWhitelist": [],
"HttpStatusCode": 429,
"GeneralRules": [
{
"Endpoint": "*",
"Period": "10s",
"Limit": 1
}
]
},
"IpRateLimitPolicies": {
"IpRules": [
{
"Ip": "127.0.0.1",
"Rules": [
{
"Endpoint": "*",
"Period": "20s",
"Limit": 2
}
]
}
]
}
But apparently the settings under IpRateLimitPolicies won't be applied.
I wonder if I have missed anything here?
Thank you
After testing, I think the AspNetCoreRateLimit package is not compatible in .net5 and .net6. Maybe the .net core3.1 version will be more stable.
You can submit issues on github.
Apparently I have missed some configuration in program.cs or startup.cs:
https://github.com/stefanprodan/AspNetCoreRateLimit/issues/305
Since we are using startup I have added the following in Configure method
var ipPolicyStore = app.ApplicationServices.GetRequiredService();
ipPolicyStore.SeedAsync().GetAwaiter().GetResult();

ASP.NET Core (3.1) re-using IIS certificate in self-hosted (Kestrel) application

I have an ASP.NET Core (3.1) application which is self-hosted and running as a service. I would like to expose an HTTPS endpoint for it. On the same machine there is an IIS instaled with already configured https together with certificate:
The certificate seems to stored in local computer certificate store:
I can also list it via the powershell:
> get-childitem cert:\LocalMachine\My\ | format-table NotAfter, Subject
NotAfter Subject
-------- -------
27.10.2023 07:38:45 <irrelevant>
08.03.2022 09:52:44 CN=a7642e58-2cdf-4e9b-a277-60fad84d7c64, DC=3336d6b0-b132-47ee-a49b-3ab470a5336e
23.02.2022 21:51:53 CN=a7642e58-2cdf-4e9b-a277-60fad84d7c64, DC=3336d6b0-b132-47ee-a49b-3ab470a5336e
27.10.2031 06:48:06 CN=a7642e58-2cdf-4e9b-a277-60fad84d7c64
26.10.2024 10:41:03 E=****.com, CN=****, OU=IT, O=****, L=****, S=***, C=**
I changed the appsettings.json to use the certificate from the store:
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"EndPoints": {
"Http": {
"Url": "http://*:5000"
},
"HttpsDefaultCert": {
"Url": "https://*:5001"
}
},
"Certificates": {
"Default": {
"Subject": "E=****.com, CN=****, OU=IT, O=****, L=****, S=***, C=**",
"Store": "My",
"Location": "LocalMachine",
"AllowInvalid": "true"
}
}
}
}
However this does not seem to work. I always get the following error:
System.InvalidOperationException: The requested certificate E=****.com, CN=****, OU=IT, O=****, L=****, S=***, C=** could not be found in LocalMachine/My with AllowInvalid setting: True
I do not know what could be the problem. The only thing that I think might be problematic is that the certificate subject actually contains newlines in the subject:
I do not know if this is the problem and I do not know how to enter it in the appsettings.json as multiline values can not be entered.
I've managed to track down the issue. Kestrel uses FindBySubjectName when searching for certificate.
FindBySubjectName does a sub-string search and will not match the full Subject of the certificate. If your certificate subject is something like 'CN=my-certificate' then searching for 'CN=my-certificate' will not find anything. Searching only for 'my-certificate' will work.
Additional note: In addition to specifying the correct search expression, make sure that the account under which you are running the application has sufficient permissions to read the certificate from certificate store. Certificates do have ACL so you do not have to run your app as an administrator.
I refer to the documentation for configuring the SSL certificates for Asp.NetCore app running on Kestrel.
I noticed some URL and ports settings also get stored in Properties/LaunchSettings.json file.
See Here: Configure endpoints for the ASP.NET Core Kestrel web server
Further, I noticed that you have put the Certificate under Defaults. I found other ways to configure the certificate. You could try to test them.
In the following appsettings.json example:
Set AllowInvalid to true to permit the use of invalid certificates (for example, self-signed certificates).
Any HTTPS endpoint that doesn't specify a certificate (HttpsDefaultCert in the example that follows) falls back to the cert defined under Certificates:Default or the development certificate.
{
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://localhost:5000"
},
"HttpsInlineCertFile": {
"Url": "https://localhost:5001",
"Certificate": {
"Path": "<path to .pfx file>",
"Password": "$CREDENTIAL_PLACEHOLDER$"
}
},
"HttpsInlineCertAndKeyFile": {
"Url": "https://localhost:5002",
"Certificate": {
"Path": "<path to .pem/.crt file>",
"KeyPath": "<path to .key file>",
"Password": "$CREDENTIAL_PLACEHOLDER$"
}
},
"HttpsInlineCertStore": {
"Url": "https://localhost:5003",
"Certificate": {
"Subject": "<subject; required>",
"Store": "<certificate store; required>",
"Location": "<location; defaults to CurrentUser>",
"AllowInvalid": "<true or false; defaults to false>"
}
},
"HttpsDefaultCert": {
"Url": "https://localhost:5004"
}
},
"Certificates": {
"Default": {
"Path": "<path to .pfx file>",
"Password": "$CREDENTIAL_PLACEHOLDER$"
}
}
}
}
Schema notes:
Endpoints names are case-insensitive. For example, HTTPS and Https are equivalent.
The Url parameter is required for each endpoint. The format for this parameter is the same as the top-level Urls configuration parameter except that it's limited to a single value.
These endpoints replace those defined in the top-level Urls configuration rather than adding to them. Endpoints defined in code via Listen are cumulative with the endpoints defined in the configuration section.
The Certificate section is optional. If the Certificate section isn't specified, the defaults defined in Certificates:Default are used. If no defaults are available, the development certificate is used. If there are no defaults and the development certificate isn't present, the server throws an exception and fails to start.
The Certificate section supports multiple certificate sources.
Any number of endpoints may be defined in Configuration as long as they don't cause port conflicts.
Reference: Replace the default certificate from configuration

How to change values in appSettings.json file based on environment

I am building API in .net core project and have to store some key value pairs in appSettings.json file for jwt token. For example i need to store valid issuer and valid audience. For development i have my appSettings.json file as below:
`{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"jwt": {
"JwtIssuer": "https://localhost:44332/",
"JwtAudience": "https://localhost:44332/",
"JwtSecretKey": "my super secure key"
}
"AllowedHosts": "*"
}`
For development environment this is fine but when i will have to deploy to say azure, do i need to change these URLs manually here in appSettings.json file or is there any other efficient way to manage these URLs that automatically gets updated based on environment.
The default of ConfigurationBuilder is looking for appsettings.<EnvironmentName>.json file, so based on the environment that you are working with
when you are in IIS Express you are in Development and when you deploy your application your environment is Production. This is why you need appsettings.Production.json

How do I make APIManagement Service Logger deploy before the Application insights Resource?

I am trying to make the following ARM deploy a APIM service logger, however the service logger starts to deploy before the app insights resource and fails, the app insights resource is in a seperate template. I have added a dependson statement and thought that would do the job but that did'nt work either. Also the code below actually works if the app insights is already deployed.
does anyone have any pointers?
{
"type": "Microsoft.ApiManagement/service/loggers",
"name": "[concat(variables('apiManagementInstanceName'), '/', parameters('appInsightsName'))]",
"apiVersion": "2018-01-01",
"properties": {
"loggerType": "applicationInsights",
"description": "Logger resources to APIM",
"credentials": {
"instrumentationKey": "[reference(resourceId('Microsoft.Insights/components', parameters('appInsightsName')), '2015-05-01').InstrumentationKey]"
}
}
"dependsOn": [
"[resourceId('microsoft.insights/components', parameters('appInsightsName'))]"
]
}
also tried depending on both the APIM and app insights
"dependsOn": [
//"[resourceId('Microsoft.ApiManagement/service', variables('apiManagementInstanceName'))]"
"[resourceId('microsoft.insights/components', parameters('appInsightsName'))]"
],
You can use linked templates to reference another template file and define dependencies on it: https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/linked-templates#linked-template

OpenShift Service Account Permissions to Read Pod and Deployment Status

I would like to use the OpenShift REST api to make queries from a separate portal. We first created a service account using the following steps (where my-id is an admin in the project):
C:\openshift>oc login
Authentication required for https://openshift-test.foo.com:8443 (openshift)
Username: my-id
Password:
Login successful.
You have access to the following projects and can switch between them with 'oc project <projectname>':
* datalake-replication-consumers
datalake-replication-demo
Using project "datalake-replication-consumers".
C:\openshift>oc create serviceaccount gmi-registry
serviceaccount "gmi-registry" created
C:\openshift>oc policy add-role-to-user admin system:serviceaccounts:datalake-replication-consumers:gmi-registry
role "admin" added: "system:serviceaccounts:datalake-replication-consumers:gmi-registry"
C:\openshift>oc serviceaccounts get-token gmi-registry
<token here>
I then pasted that token as a bearer token into Postman to make a few api calls. Since I added my service account to the admin role within my project, I assumed this would work, but instead we're getting back a 403.
GET pods:
https://openshift-test.foo.com:8443/api/v1/namespaces/datalake-replication-consumers/pods
Response:
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "User \"system:serviceaccount:datalake-replication-consumers:gmi-registry\" cannot list pods in project \"datalake-replication-consumers\"",
"reason": "Forbidden",
"details": {
"kind": "pods"
},
"code": 403
}
GET specific deployment:
https://openshift-test.foo.com:8443/oapi/v1/namespaces/datalake-replication-consumers/deploymentconfigs/entity-65869977-9d56-49a5-afa2-4a547df82d5c
Response:
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "User \"system:serviceaccount:datalake-replication-consumers:gmi-registry\" cannot get deploymentconfigs in project \"datalake-replication-consumers\"",
"reason": "Forbidden",
"details": {
"name": "entity-65869977-9d56-49a5-afa2-4a547df82d5c",
"kind": "deploymentconfigs"
},
"code": 403
}
What are we missing for service account permissions here?
UDPATE: I should also add that I pulled my own bearer token out of the CLI and used that for both calls. That worked.
Not a very exciting answer, but our problem was solved when we installed a v3.7 instance. My initial tests were on v1.5, which I think corresponds to 3.5 or 3.6 in the enterprise offering?