Identity Server 4 and docker - c#

I'm trying to configure IdentityServer4 with docker but I cannot make it work. To get started, I took the Client Credential example of the identity server documentation: Protecting an API using Client Credentials
IdentityServer
Hosted on port 5000
WebApi
Hosted on port 5001
In the Configure method of the Startup.cs file of my WebApi I did the following (the problem is probably here):
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://web:5000",
RequireHttpsMetadata = false,
ApiName = "api1"
});
Client
And the client
// Everything is fine here...
var disco = await DiscoveryClient.GetAsync("http://localhost:5000");
var tokenClient = new TokenClient(disco.TokenEndpoint, "client", "secret");
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api");
// This does not work
var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);
var response = await client.GetAsync("http://localhost:5001/identity");
The problem is probably in my WebApi:
1) If I set the authority to localhost:5000, I get an internal server error: "Unable to obtain configuration from: 'http://localhost:5000/.well-known/openid-configuration'" which makes sense since localhost:5000 is unknown in this container
2) If I set the authority to http://web:5000 I get an authorization error: "Issuer validation failed. Issuer: 'http://localhost:5000'. Did not match: validationParameters.ValidIssuer: 'http://web:5000' or validationParameters.ValidIssuers" which also makes sense but I don't know if it's possible to change the authority name? I also tried to set the IssuerUri in the IdentityServer project but it didn't help

Network
Let's suppose you have two physical machines: C1 and C2. Each machine is a docker host.
C1 runs Auth container.
C2 runs WebApi container.
As you expose port 5000 in Auth dockerfile, the address C1:5000 should be accessible from C2 and from WebApi container itself. You could prefer IPs to DNS, it doesn't matter. Moreover you should be able to make a successfull GET request to http://C1:5000/.well-known/openid-configuration to be sure.
There are a lot of network issues you could face to achieve that. For example:
What would prevent code running in a Docker container from connecting to a database on a separate server?
Issuer validation
Issuer validation failed
Your client's authority URL differs from Auth hostname. By default, authority URL should be equal to issuer property value (this property is in Identity Server autodiscovery document response).
issuer property value depends on your client's web request:
GET http://127.0.0.1:6000/.well-known/openid-configuration -> "issuer": "http://127.0.0.1:6000"
GET http://localhost:6000/.well-known/openid-configuration -> "issuer": "localhost:6000"
Try to set IssuerUri to a constant for a dev environment:
services.AddIdentityServer(x =>
{
x.IssuerUri = "foo";
})
to achieve a constant issuer value. This allowes to call Identity Server by any valid URL (using IP, machine name or DNS):
GET http://anything/.well-known/openid-configuration -> "issuer": "foo"
DiscoveryClient also validates issuer value. It's a simple equality comparison:
public bool ValidateIssuerName(string issuer, string authority)
{
return string.Equals(issuer, authority, StringComparison.Ordinal);
}
You could disable it by:
DiscoveryClient.Policy.ValidateIssuerName = false;
FYI, IssuerUri setting is not recommended for a production environment:
IssuerUri Set the issuer name that will appear in the discovery
document and the issued JWT tokens. It is recommended to not set this
property, which infers the issuer name from the host name that is used
by the clients.

Related

Website Azure connection on mulitple subdomains

We are hosting a website on our webserver. That website needs to connect to Azure/Adfs. Users need to login through Azure/Adfs to access some parts of the site.
But it only works half. I can connect on "customer.nl", but on "subdomain.customer.nl" I get a "NONCE error".
There is a "Startup" class, which inherits from "UmbracoDefaultOwinStartup" (an Umbraco override for the regular OwinStartup). The class has a "ConfigureAuth" method, which sets the configurationparameters. One of them is the RedirectUri, and it's set (via web.config) to "customer.nl".
The "startup" code:
[assembly: OwinStartup(typeof(Ip.Startup))]
namespace Customername {
public class Startup : UmbracoDefaultOwinStartup {
string redirectUri = System.Configuration.ConfigurationManager.AppSettings["RedirectUri"];
public new void Configuration(IAppBuilder app) {
ConfigureAuth(app);
app.MapSignalR();
base.Configuration(app);
}
public void ConfigureAuth(IAppBuilder app) {
app.SetDefaultSignInAsAuthenticationType(
CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions(){
CookieManager = new SystemWebCookieManager()
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions {
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.IdToken,
TokenValidationParameters = new TokenValidationParameters() {
ValidateIssuer = false
},
Notifications = new OpenIdConnectAuthenticationNotifications {
AuthenticationFailed = OnAuthenticationFailed
}
});
}
}
}
If I try to login on "subdomain.customer.nl", I redirected to login.microsoftonline.com but I see a "redirect_url=customer.nl" in the URL.
The function to redirect a unauthenticated user is:
public void SignIn(string ReturnUrl = "/") {
if (!Request.IsAuthenticated) {
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = ReturnUrl },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
}
But changing the RedirectUri in this function doesn't change the 'Redirect_Uri' in the login.microsoftonline.com url.
If I login on subdomain.customer.nl, I get returned to customer.nl with the following querystring (I've decoded the URL):
https://www.customer.nl/?errormessage=IDX21323:
RequireNonce is '[PII is hidden]'.
OpenIdConnectProtocolValidationContext.Nonce was null,
OpenIdConnectProtocol.ValidatedIdToken.Payload.Nonce was not null.
The nonce cannot be validated.
If you don't need to check the nonce, set OpenIdConnectProtocolValidator.RequireNonce to 'false'. Note if a 'nonce' is found it will be evaluated.
My guess is that the NONCE error pops up when the redirect_uri doesn't match the origin-url (subdomain.customer.nl != customer.nl).
Is this correct? And if so, how can I change the Redirect_Uri to the subdomain a user is visiting? Setting it on startup isn't the way to go, it seems.
• Firstly, I would suggest you to please ensure that public DNS records exist for the subdomains that you want to connect to through the base domain URL, i.e., ‘customer.nl’. The public DNS records for the subdomains can be ‘A’ host records, ‘TXT’ records but need to be configured correctly within your public DNS server and pointing to a public IP address if independent web applications are hosted on them.
• Secondly, since you seem to use Azure AD authentication in your website for redirecting to the subdomain, I would suggest you configure the redirect URI for the concerned subdomains in the Azure AD registered application for the base domain such that after successful Azure AD authentication, the web application gets correctly redirected to subdomain page as desired.
For more information on the above, kindly refer to the documentation link below: -
https://learn.microsoft.com/en-us/azure/app-service/configure-authentication-provider-aad
But changing the RedirectUri in this function doesn't change the 'Redirect_Uri' in the login.microsoftonline.com url
You can do the above by delegating the required API permissions and scope to the Azure function application in your registered Azure AD application. Kindly refer to the documentation link below for your reference: -
https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent
Also, the domains for the authentication request and the response need to be matched as it stores the ‘nonce’ and the ‘state’ for CSRF login attacks mitigation. Thus, I would like to suggest you consider the scenario below for different clients (as per your redirection mechanism) and take advantage of SSO: -
a) The user logs in into the first application (customer.nl). The callback URL belongs to this app.
b) After processing the callback (on the ‘parseHash’ callback function), redirect the user to the subdomain URL.
c) When the user lands on the subdomain URL app, the app will see that there’s no session for the user and ask Azure AD for authentication (either authorize () or checkSession()). If the user already has a session in Azure AD, there will be no prompt to the user and a new authentication response will be provided to the app.
If you are using universal login (as opposed to embedded login as above), when the user clicks on “Login” on the base domain URL (customer.nl) app, you send the user directly to the SPA, pointing to a login initiation endpoint (e.g.: - https://app.mydomain.com/login 1), and have the subdomain URL app start the actual login flow.
For more information regarding the above, I request you to please refer the below link: -
https://community.auth0.com/t/log-in-from-different-subdomain-produces-state-error/19116

Implementation of Openiddcit authorization in NestJS when the authentication server is written in .Net

I'm trying to implement authorization on NestJS OpenID connect. An authentication server (hereinafter the server) written in Net C# Web.API is allocated for the entire application. There are also several additional Net Web.APIs that interact correctly with my server, and the code is quite small
builder.Services
.AddOpenIddict()
.AddValidation(options =>
{
options.SetIssuer("<issuer>");
options.UseSystemNetHttp();
options.UseAspNetCore();
});
Since I'm new to NestJS, I can't get my NestJS to work properly with my server, the maximum I've reached is getting.well-known/OpenID-configuration
export const buildOpenIdClient = async () => {
const issuer = await Issuer.discover(`server/.well-known/openid-configuration`);
// Because TypeError: authorization_endpoint must be configured on the issuer (don't know how fix)
issuer['authorization_endpoint'] = 'server/auth_endpoint';
//I Accept Anonymous Clients
return new issuer.Client({
client_id: "todo",
client_secret: "todo",
});
};
Also I have a error: TypeError: authentication requires session support,
The internet says that I need to add this line
await app.use(session({ secret: 'you secret', saveUninitialized: true, resave: true }));
but I don't have a secret key in this place.
Is there some elegant solution or am I moving in the wrong direction at all?

Trying to login with Azure AFDS on multiple domains

I'm trying to connect to an Azure AD server with an Umbraco website.
To start off, I have no knowledge of Azure. There is a third party who administers the Azure part.
We use OWIN to connect to Azure via OpenID.
OnStartup:
public void ConfigureAuth(IAppBuilder app){
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions{
// Sets the ClientId, authority, RedirectUri as obtained from web.config
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.IdToken,
TokenValidationParameters = new TokenValidationParameters(){
ValidateIssuer = false
},
Notifications = new OpenIdConnectAuthenticationNotifications{
AuthenticationFailed = OnAuthenticationFailed
}
});
}
The SignIn function in the SurfaceController:
public void SignIn(string ReturnUrl = "/"){
if (!Request.IsAuthenticated) {
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = ReturnUrl },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
Here come the non-working part.
If I test this site at a local domain (only available from within our office), it works.
If I test this site on a publicly-available staging domain, it works.
If I test this site on a live domain, it works.
But as soon as I change a sub-domain, I get send to the working domain with a "RequireNonce" error.
So for example:
https://customer.localdomain.com -> login -> I return logged in at https://customer.localdomain.com.
https://test.localdomain.com -> login -> I return to https://customer.localdomain.com (notice the domain), with a "Nonce-error".
https://customer.stagingdomain.com -> login -> I return logged in at https://customer.stagingdomain.com.
https://test.stagingdomain.com -> login -> I return to https://customer.stagingdomain.com (notice the domain), with a "Nonce-error".
https://www.livedomain.com -> login -> I return logged in at https://www.livedomain.com.
https://test.livedomain.com -> login -> I return to https://www.livedomain.com (notice the domain), with a "Nonce-error".
The complete error is:
IDX21323:
RequireNonce is '[PII is hidden]'.
OpenIdConnectProtocolValidationContext.Nonce was null,
OpenIdConnectProtocol.ValidatedIdToken.Payload.Nonce was not null.
The nonce cannot be validated.
If you don't need to check the nonce, set OpenIdConnectProtocolValidator.
RequireNonce to 'false'. Note if a 'nonce' is found it will be evaluated.
What can we do to resolve this problem? Our customer has a couple of subdomains (seperate sites) that all need this login functionality.
We've tried adding subdomains to a reply-list in Azure (well, the third party added them for us), but that didn't solve the problem.
Is it possible to just turn RequireNonce off somewhere?
Thank you JamesHamil-MSFT Posting your suggestion as an answer to help other community members .
"The problem was that the time or automatic reference program service binding a custom domain name.
After the application network management is configured. The Host IP that modifies the custom domain name points to a public IP that is gateway."
Please try checking that your domain is configured correctly and points to the correct gateway."
Please refer the below links for further information:
. Configure App Service with Application Gateway using PowerShell | MS DOC .
. SO THREAD for similar issue.

Why doesn’t the "https://login.microsoftonline.com/common" AAD endpoint work, while the "https://login.microsoftonline.com/[tenant ID]" does?

I’m developing a UWP application that calls an API. The API is made of an Azure Function triggered by HTTP requests. I want the Azure Function to be secured through Azure Active Directory. To do so, I created two app registrations in AAD, one for the UWP and one for the API. Both support accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g., Skype, Xbox). The API app registration provides scope, and the UWP app registration uses that scope. The code I use on my UWP is:
var HttpClient _httpClient = new HttpClient();
const string clientId = "[UWP app registration’s client ID]";
const string authority = "https://login.microsoftonline.com/[Tenant ID of the UWP app registration]";
string[] scopes = { "api://[API app registration’s client ID]/[scope]" };
var app = PublicClientApplicationBuilder
.Create(clientId)
.WithAuthority(authority)
.WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
.Build();
AuthenticationResult result;
var accounts = await app.GetAccountsAsync();
try {
result = await app.AcquireTokenSilent(scopes, accounts.FirstOrDefault()).ExecuteAsync();
}
catch (MsalUiRequiredException) {
try {
result = await app.AcquireTokenInteractive(scopes).ExecuteAsync();
}
catch (Exception exception) {
Console.WriteLine(exception);
throw;
}
}
if (result == null) return;
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
var response = _httpClient.GetAsync("[API URL]").Result;
This code works, but if I replace the authority with https://login.microsoftonline.com/common (as specified here), being my app registrations multi-tenant, I get a 401 response when calling the API _httpClient.GetAsync("[API URL]").Result. The docs say the code must be updated somehow when using the /common endpoint, but I don’t understand how I should edit it. I also tried to follow these tips, but without success, while these seem not to be related to my case since I’m not building an IWA. If I run the working version of the code, result is populated with an object whose TenantId property gets the right value of the tenant that owns the app registrations while using the not-working version of the code, result is populated with an object whose TenantId property gets a value I don’t know where it’s coming from.
Can anyone help me, please?
Here's my understanding of AAD multitenancy flow :
The common authority can't be used to get a token. It's used as a common endpoint to get the templated server metadata :
v1 : https://login.microsoftonline.com/common/.well-known/openid-configuration
v2 : https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration
A token should be requested from the issuer where the client is defined.
But the common authority can be used in a multitenant API (eg your Azure Functions API) to verify that a client has a valid AAD token. From the documentation :
Because the /common endpoint doesn’t correspond to a tenant and isn’t an issuer, when you examine the issuer value in the metadata for /common it has a templated URL instead of an actual value : https://sts.windows.net/{tenantid}/
Therefore, a multi-tenant application can’t validate tokens just by matching the issuer value in the metadata with the issuer value in the token. A multi-tenant application needs logic to decide which issuer values are valid and which are not based on the tenant ID portion of the issuer value.

OpenID connect ADFS 2019 - authority invalid

Following the documentation example on microsoft for the adfs setup.
ADFS microsoft setup
I have the following application .Net 4.6.
startup.cs
app.UseMyAppApiAuthentication(config);
in the class
//setup OpenIdConnect Authentication
var options = config.DependencyResolver.GetService<OpenIdConnectAuthenticationAndNotificationOptions>();
app.UseOpenIdConnectAuthentication(options);
In the options class i have
ClientId = configProvider.GetOpenIdConnectClientId();
Authority = configProvider.GetOpenIdConnectAuthority();
PostLogoutRedirectUri = configProvider.GetOpenIdConnectPostLogoutRedirectUri();
RedirectUri = configProvider.GetOpenIdConnectRedirectUri();
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthorizationCodeReceived = authenticationNotificationProcessor.OnAuthorizationCodeReceived,
AuthenticationFailed = authenticationNotificationProcessor.OnAuthenticationFailed
};
where authority is
public string GetOpenIdConnectAuthority()
{
var instance = ConfigurationManager.AppSettings["moto:AADInstance"];
var tenant = ConfigurationManager.AppSettings["moto:Tenant"];
return String.Format(CultureInfo.InvariantCulture, instance, tenant);
}
In the webconfig, I have the clientid and client secret set, I have the AADinstance set and tenant is blank.
If I put in the ADFS URI. I get the following error:
Now if I edit the AADinstance and add /.well-known/Openid-configuration i get a different error...
I have also changed the config and removed authority and replace with "MetadataAddress" still no change.
What do I need todo to resolve this issue?
Note: if I change the redirect URI to something different when running the app, I manage to get to the adfs login screen with the error that there is a mismatch with the redirect.
I recommend you to open a support case since it needs deep troubleshooting in order to isolate the issue.

Categories

Resources