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
Related
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.
I have a .net core 3.1 MVC IdentityServer4 application (A tailored quickstart app provided by the guys at Idsrv) which I use for authentication against a SPA which is built using the oidc-client library and I've recently been able to replicate an issue regarding a 400 error that appears every now and then when trying to login to Identity Server.
Open up two separate tabs on Chrome/Firefox/Edge and navigate to the
path of the client app on both tabs (e.g. https://localhost:5001).
Both tabs correctly redirect to the URL of my identity server
instance as the user has not yet logged in (https://localhost:5002).
Login on the 1st tab with username and password, or even an external
OIDC client - this works fine.
Attempt to login on the 2nd tab with username and password
400 Error occurs and after checking the logs I get the following CORS issue (which I don't really think is related):
[15:51:29 Debug] IdentityServer4.Hosting.CorsPolicyProvider
CORS request made for path: /Account/Login from origin: null but was ignored because path was not for an allowed IdentityServer CORS endpoint
If I click back in the browser the login page shows again, and then I can login as expected.
So to summarise, once a user has successfully logged in to the 1st tab, the 2nd tab will always throw a 400 error the first time a user tries to login to IdentityServer there. I'm really struggling to work out why this could be - and the CORS error message is a little strange too as when I click back and attempt login the 2nd time it works fine.
new Client
{
ClientId = "WebApp",
ClientName = "Web App",
AllowedGrantTypes = GrantTypes.Code,
RequireClientSecret = false,
RequirePkce = true,
AccessTokenType = AccessTokenType.Reference, // This ensures tokens are revoked on logout
RedirectUris = {
$"{webClientHost}/Oidc/SigninOidc",
$"{webClientHost}/Oidc/SilentRefresh"
},
PostLogoutRedirectUris = { webClientHost + "/" },
AllowedCorsOrigins = { webClientOrigin },
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerSettings.ApiScopeName
},
AllowAccessTokensViaBrowser = true,
AccessTokenLifetime = (int) TimeSpan.FromMinutes(Convert.ToDouble(isAppSettings.WebAccessTokenLifetime)).TotalSeconds,
RequireConsent = false
}
Has anyone every experienced anything like this? Could it be something to do with my setup at all or is this a known flaw with the Identity Server 4 Quickstart app? I've been trying to debug, but the 400 error I experience means the debugging doesn't even hit so I'm struggling. Thanks
The issue is not related/caused by identity server rather how ASP.Net Core uses antiforgery token. This issue is similar in many cases to an issue reported on github https://github.com/IdentityServer/IdentityServer4/issues/2676.
In this particular instance once the user successfully logs in the application, Identity server issues a session cookie and thus the request token and antiforgery token go out of sync.
Since the preloaded login page relies on the old antiforgery token, once the request starts processing the systems throws following exception -
"Antiforgery token validation failed. "The antiforgery cookie token and request token do not match."
Here is the stack frame -
Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException: The antiforgery cookie token and request token do not match.
at Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgery.ValidateTokens(HttpContext httpContext, AntiforgeryTokenSet antiforgeryTokenSet)
at Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgery.ValidateRequestAsync(HttpContext httpContext)
at Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ValidateAntiforgeryTokenAuthorizationFilter.OnAuthorizationAsync(AuthorizationFilterContext context)
As antiforgery token validation is executed before actions, hence the authorization fails and request processing is aborted, the execution control never reaches the login code.
For those that come here in the future (I am using Duende Identity Server v6), I managed to work around this by adding this middleware which sends them back to the login page with the original request to get a new anti forgery token. If they had logged in correctly the login page will flash then automatically return them to the application otherwise they can just login as normal:
using Microsoft.AspNetCore.Antiforgery;
using System.Net;
namespace IdentityServer
{
public class FixAntiForgeryIssueMiddleware
{
private readonly RequestDelegate next;
private readonly ILogger logger;
private readonly string matchPath;
public FixAntiForgeryIssueMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, string matchPath)
{
this.next = next;
this.matchPath = matchPath;
logger = loggerFactory.CreateLogger<FixAntiForgeryIssueMiddleware>(); ;
}
public async Task Invoke(HttpContext context)
{
await next.Invoke(context);
if (context.Response.StatusCode == (int)HttpStatusCode.BadRequest)
{
if (context.Request.Path.StartsWithSegments(new PathString(matchPath)))
{
if (context.Request.Form.TryGetValue("Input.ReturnUrl", out var returnUrls))
{
var returnUrl = returnUrls.FirstOrDefault();
if (string.IsNullOrEmpty(returnUrl))
{
context.Response.Redirect(matchPath);
}
else
{
context.Response.Redirect(returnUrl);
}
}
}
}
}
}
}
Then in the Progam.cs/HostingExtensions.cs use it like this:
app.UseMiddleware<FixAntiForgeryIssueMiddleware>("/account/login");
I have implemented Single-Sign on in an existing C# Asp.Net MVC 4.5 application and therefore used Owin middleware and OpenIdConnectAuthentication.
The authentication and authorization works fine, but now I have following problem:
I sign in to my application by using AzureAD as identity provider
I sign in to Office365 in another browser tab
I sign out of my application - get redirected to the identity provider and also automatically sign out there
Office365 automatically signs out within the other tab
I do not have configured Single Sign out (so I didn't specified a Logout Url within the App Registration and neither in the configuration code), but I am still signed out of Office365. This is annoying for the customer, as he always uses Outlook365 within the browser.
How can I prevent Office365 from signing out the user automatically.
Here is a simplified code of my configuration of OpenIdConnect:
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = (context) =>
{
// ... my sign in logic ...
context.OwinContext.Response.Redirect(homeUrl);
return Task.FromResult(0);
},
},
AuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType
});
And here is how I sign out of the application:
context.GetOwinContext().Authentication.SignOut(new AuthenticationProperties { RedirectUri = redirectUrl }, OpenIdConnectAuthenticationDefaults.AuthenticationType);
I'm struggling to implement a custom auth flow with OAuth and JWT.
Basically it should go as follows:
User clicks in login
User is redirect to 3rd party OAuth login page
User logins into the page
I get the access_token and request for User Info
I get the user info and create my own JWT Token to be sent back and forth
I have been following this great tutorial on how to build an OAuth authentication, the only part that differs is that Jerrie is using Cookies.
What I Have done so far:
Configured the AuthenticationService
services.AddAuthentication(options =>
{
options.DefaultChallengeScheme = "3rdPartyOAuth";
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie() // Added only because of the DefaultSignInScheme
.AddJwtBearer(options =>
{
options.TokenValidationParameters = // Ommited for brevity
})
.AddOAuth("3rdPartyOAuth", options =>
{
options.ClientId = securityConfig.ClientId;
options.ClientSecret = securityConfig.ClientSecret;
options.CallbackPath = new PathString("/auth/oauthCallback");
options.AuthorizationEndpoint = securityConfig.AuthorizationEndpoint;
options.TokenEndpoint = securityConfig.TokenEndpoint;
options.UserInformationEndpoint = securityConfig.UserInfoEndpoint;
// Only this for testing for now
options.ClaimActions.MapJsonKey("sub", "sub");
options.Events = new OAuthEvents
{
OnCreatingTicket = async context =>
{
// Request for user information
var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
var response = await context.Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, context.HttpContext.RequestAborted);
response.EnsureSuccessStatusCode();
var user = JObject.Parse(await response.Content.ReadAsStringAsync());
context.RunClaimActions(user);
}
};
});
Auth Controller
[AllowAnonymous]
[HttpGet("login")]
public IActionResult LoginIam(string returnUrl = "/auth/loginCallback")
{
return Challenge(new AuthenticationProperties() {RedirectUri = returnUrl});
}
[AllowAnonymous]
[DisableRequestSizeLimit]
[HttpGet("loginCallback")]
public IActionResult IamCallback()
{
// Here is where I expect to get the user info, create my JWT and send it back to the client
return Ok();
}
Disclaimer: This OAuth flow is being incorporated now. I have a flow for creating and using my own JWT working and everything. I will not post here because my problem is before that.
What I want
In Jerrie's post you can see that he sets DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;. With that, when the /auth/loginCallback is reached I have the user claims in the HttpContext.
The problem is my DefaultAuthenticateScheme is set to JwtBearersDefault and when the loginCallback is called I can't see the user claims nowhere in the Request.
How can I have access to the information gained on the OnCreatingTicketEvent in my callback in this scenario?
Bonus question: I don't know much about OAuth (sure that is clear now). You may have noted that my options.CallbackPath differs from the RedirectUri passed in the Challenge at the login endpoint. I expected the option.CallbackPath to be called by the 3rd Part OAuth provider but this is not what happens (apparently). I did have to set the CallbackPath to the same value I have set in the OAuth provider configuration (like Jerries tutorial with GitHub) for it to work. Is that right? The Callback is used for nothing but a match configuration? I can even comment the endpoint CallbackPath points to and it keep working the same way...
Thanks!
Auth
As Jerrie linked in his post, there is a great explanation about auth middlewares:
https://digitalmccullough.com/posts/aspnetcore-auth-system-demystified.html
You can see a flowchart in the section Authentication and Authorization Flow
The second step is Authentication middleware calls Default Handler's Authenticate.
As your default auth handler is Jwt, the context is not pupulated with the user data after the oauth flow,
since it uses the CookieAuthenticationDefaults.AuthenticationScheme
Try:
[AllowAnonymous]
[DisableRequestSizeLimit]
[HttpGet("loginCallback")]
public IActionResult IamCallback()
{
//
// Read external identity from the temporary cookie
//
var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
if (result?.Succeeded != true)
{
throw new Exception("Nein");
}
var oauthUser = result.Principal;
...
return Ok();
}
Great schemes summary: ASP.NET Core 2 AuthenticationSchemes
You can persist your user with
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.authenticationhttpcontextextensions.signinasync?view=aspnetcore-2.2
Bonus
I did have to set the CallbackPath to the same value I have set in the OAuth provider configuration (like Jerries tutorial with GitHub) for it to work. Is that right?"
Yes.
For security reasons, the registered callback uri (on authorization server) and the provided callback uri (sent by the client) MUST match.
So you cannot change it randomly, or if you change it, you have to change it on the auth server too.
If this restriction was not present, f.e. an email with a mailformed link (with modified callback url) could obtain grant.
This is called Open Redirect, the rfc refers to it too: https://www.rfc-editor.org/rfc/rfc6749#section-10.15
OWASP has a great description: https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md
I can even comment the endpoint CallbackPath points to and it keep working the same way..."
That is because your client is trusted (you provide your secret, and you are not a fully-frontend Single Page App). So it is optional for you to send the callback uri.
But IF you send it, it MUST match with the one registered on the server. If you don't send it, the auth server will redirect to the url, that is registered on its side.
https://www.rfc-editor.org/rfc/rfc6749#section-4.1.1
redirect_uri
OPTIONAL. As described in Section 3.1.2.
https://www.rfc-editor.org/rfc/rfc6749#section-3.1.2
The authorization server redirects the user-agent to the
client's redirection endpoint previously established with the
authorization server during the client registration process or when
making the authorization request.
https://www.rfc-editor.org/rfc/rfc6749#section-3.1.2.2
The authorization server MUST require the following clients to register their redirection endpoint:
Public clients.
Confidential clients utilizing the implicit grant type.
Your client is confidential and uses authorization code grant type (https://www.rfc-editor.org/rfc/rfc6749#section-1.3.1)
https://www.rfc-editor.org/rfc/rfc6749#section-3.1.2.3
If multiple redirection URIs have been registered, if only part of
the redirection URI has been registered, or if no redirection URI has
been registered, the client MUST include a redirection URI with the
authorization request using the "redirect_uri" request parameter.
You have registered your redirect uri, that's why the auth server does not raise an error.
change [AllowAnonymous]
to [Authorize]
on the 'loginCallback' endpoint (AuthController.IamCallback method)
I have an Owin app that uses Ws-Federation authentication with a SSO application (not ADFS). Whenever my Owin app receives a request, it authenticates the user by first checking if it has the right cookie, and then it builds the claims and authentication ticket from that. If it doesn't have the right cookie, it redirects to the STS, which passes back a SAML token that can be used to complete the authentication.
All of this works except one part. After the token is received and validated, for some reason it redirects back to the STS, thus creating an infinite loop. I am quite sure it is because one or more of my configuration values is wrong since it's not very clear what each property is for and if it's required. I've copied my configuration code below:
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType( CookieAuthenticationDefaults.AuthenticationType);
CookieAuthenticationOptions cookieOptions = new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
CookieName = "MyCookie",
CookiePath = "/CookiePath",
AuthenticationMode = AuthenticationMode.Active
};
// Basically the same as saying app.UseCookieAuthentication(app, cookieOptions)
app.Use(typeof(MyCustomCookieAuthenticationMiddleware), app, cookieOptions);
// Define properties for WsFederationAuthenticationOptions
var config = new Microsoft.IdentityModel.Protocols.WsFederationConfiguration
{
Issuer = "https://sts-domain.com/STS/",
TokenEndpoint = this.owinServerUrl // I don't know what this should be. I just made it the same as my owin start url
};
Saml2SecurityTokenHandler handler = new Saml2SecurityTokenHandler
{
Configuration = new SecurityTokenHandlerConfiguration
{
IssuerTokenResolver = new MyCustomSecurityTokenResolver
{
Thumbprint = somePublicKeyStr,
StoreLocation = System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine,
StoreName = "My"
}
}
};
var handlers = new SecurityTokenHandlerCollection(new List<SecurityTokenHandler>() { handler });
var wsFedOptions = new WsFederationAuthenticationOptions
{
AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType,
AuthenticationMode = AuthenticationMode.Passive,
SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType, // I'm not sure what this is exactly, but I think I've seen this used in examples
Configuration = config,
Wtrealm = this.owinServerUrl, // I'm guessing what this should be
Wreply = someString, // I have no idea what this should be -- it hasn't seemed to have any effect so far
SecurityTokenHandlers = handlers,
TokenValidationParameters = new TokenValidationParameters
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType, // I'm not sure whether this should be cookie or ws federation, but I don't think it's relevant to my problem
ValidIssuer = "https://sts-domain.com/STS/", // same as config.Issuer above
}
};
app.UseWsFederationAuthentication(wsFedOptions);
AuthenticateAllRequests(app, WsFederationAuthenticationDefaults.AuthenticationType);
app.Use<MyCustomMiddleware>();
}
private static void AuthenticateAllRequests(IAppBuilder app, params string[] authenticationTypes)
{
app.Use((context, continuation) =>
{
if (context.Authentication.User?.Identity?.IsAuthenticated ?? false)
{
return continuation();
}
else
{
context.Authentication.Challenge(authenticationTypes);
return Task.CompletedTask;
}
});
}
As I indicate in my code with my comments, there are some properties I am unsure of. Unfortunately, the documentation for WsFederationAuthenticationOptions does not help me out much. For example, I know that Wtrealm and Wreply are important (perhaps Wreply less so), but all the documentation says is "Gets or sets the 'wtrealm'" and "Gets or sets the 'wreply'." I found this thread that has an explanation:
wtrealm is a URI (not necessarily a URL) that identifies the RP. The STS uses this to decide whether to issue a token and what claims to give it.
wreply is the URL that the RP would like to be redirected to with the resulting token. The STS is not bound to comply with this request... sometimes the STS has a predefined address it will redirect to based on the established trust. At the very least, the STS should refuse to redirect to a different domain than the one it associates with the realm. Otherwise, the request could be a vector to send the user to a malicious site.
This makes sense, except for the fact that while I've been testing my owin app, Wreply seems to have no effect on where the STS is redirected to pass back the token; the URL I put for Wtrealm is what determines that.
All I'd like to do is for the STS to pass back the token, authenticate the user, and carry on to the route that the user specified which started all this. I'm not sure if this is relevant, but I also thought that the cookie is supposed to be set when the STS passes back the token. If this were the case, the infinite redirect wouldn't occur because when it comes back to be authenticated, the cookie authentication would find the cookie and the app would proceed as normal.
Update 1
I have changed a few of the values and have gotten different error messages, so I thought I'd share them here in case they help shed light on what could be going on. As you can tell from my post, I don't want to share real info about the app, so bear with me. Let's say the overarching web application (what contains my owin app, along with a bunch of other stuff) has the url http://localhost/app. My owin app has the server url (what I call this.owinServerUrl in the code above) http://localhost/app/owin.
When I make WsFederationConfiguration.TokenEndpoint = "http://localhost/app", I get the infinite redirect. When I make it "http://localhost/app/owin", I don't get an infinite redirect, but I do get another error (which error I get depends on other values, which I'll explain now).
I was mistaken -- Wreply does seem to have an effect. When I don't set Wreply, I get a 414 error: request URL too long. When I do set it (and as any string, whether it's a URL that I think could make sense or just gibberish), I get a 400 bad request: request too long.
Isn't it because of your AuthenticateAllRequests function? I think you think it only runs after the user is authenticated and context.Authentication.User?.Identity?.IsAuthenticated is set. However, I think it runs after the user is authenticated and before context.Authentication.User?.Identity?.IsAuthenticated is set. Since is never set, does it just invoke authentication again by calling "context.Authentication.Challenge(authenticationTypes)" ?