I have a web forms app which I am trying to authenticate against Azure AD using SAML 2/Kentor/Owin. I think I have things configured OK, but when my login page issues the following command I am not being redirected to a login page.
HttpContext.Current.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/Login.aspx" });
Here is my startup.cs
private void ConfigureSAML2Authentication(IAppBuilder app) {
var authServicesOptions = new KentorAuthServicesAuthenticationOptions(false)
{
SPOptions = new SPOptions
{
EntityId = new EntityId("https://login.microsoftonline.com/<tenant guid>/saml2")
}
},
AuthenticationType = "KentorAuthServices",
Caption = "ADFS - SAML2p",
};
authServicesOptions.IdentityProviders.Add(new IdentityProvider(
new EntityId("https://sts.windows.net/<tenant guid>/"),
authServicesOptions.SPOptions)
{
MetadataLocation = "https://login.microsoftonline.com/<tenant guid>/federationmetadata/2007-06/federationmetadata.xml",
LoadMetadata = true,
});
app.UseKentorAuthServicesAuthentication(authServicesOptions);
}
As far as I can tell looking at the Network Tools in chrome, no auth request is being sent at all. Is anyone able to tell me why?
The AuthServices middleware is configured as Passive by default, so it will not automatically respond to an authentication challenge unless you specify the provider.
When you issue the challenge you should specify the same AuthenticationType that you used when the middleware was set up. By default this is "KentorAuthServices" but can be changed.
If you change your challenge to include the type, it should trigger the redirect:
HttpContext.Current.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/Login.aspx" }, "KentorAuthServices");
Related
I have an authentication module in Umbraco using OWIN/OIDC, authenticating against our Azure AD B2C resource. As part of this module, there is a LogOut controller method, which is working correctly.
We are trying to develop single sign out for applications within our Azure tenant. We are still working on having Azure AD B2C call the logout method for each application. In order to test initiation of sign out from other applications, I have set up an iframe in one of our custom applications (also authenticating via Azure AD B2C) that calls the LogOut method in our Umbraco implementation when users sign out from that application. I can see that the LogOut method is being called when the external method opens the iframe, and all of the objects look the same as when the method is called from within Umbraco. However, the user is not logged off of the application. The authentication cookie, which is .AspNet.ApplicationCookie, has SameSite as None, Secure as true and HttpOnly as false, but it is not removed as it is when Umbraco calls the method.
Any tips on how to get the LogOut method to work from the external application would be appreciated.
Here is my configuration:
private void ConfigureAzureB2CAuthentication(object sender, OwinMiddlewareConfiguredEventArgs args) {
//get appbuilder
AppBuilder app = (AppBuilder)args.AppBuilder;
app.SetDefaultSignInAsAuthenticationType(DefaultAuthenticationTypes.ApplicationCookie);
app.UseCookieAuthentication(Current.Factory.GetInstance<FrontEndCookieAuthenticationOptions>(), PipelineStage.Authenticate);
//Set configuration on appbuilder
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions {
MetadataAddress = string.Format(
ConfigurationManager.AppSettings["ida:AzureInstance"],
ConfigurationManager.AppSettings["ida:Tenant"],
ConfigurationManager.AppSettings["ida:SignUpSignInPolicyId"]),
ClientId = ConfigurationManager.AppSettings["ida:ClientId"],
RedirectUri = ConfigurationManager.AppSettings["ida:RedirectUri"],
PostLogoutRedirectUri = ConfigurationManager.AppSettings["ida:RedirectUri"],
Notifications = new OpenIdConnectAuthenticationNotifications {
RedirectToIdentityProvider = OnRedirectToIdentityProvider,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed
},
TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters {
NameClaimType = ConfigurationManager.AppSettings["ida:ClaimsLabelEmail"],
ValidateIssuer = false
},
Scope = ConfigurationManager.AppSettings["ida:ScopesOpenIDConnect"],
});
//reafirm backoffice and preview authentication
app.UseUmbracoBackOfficeCookieAuthentication(_umbracoContextAccessor, _runtimeState, _userService, _globalSettings, _securitySection, PipelineStage.Authenticate)
.UseUmbracoBackOfficeExternalCookieAuthentication(_umbracoContextAccessor, _runtimeState, _globalSettings, PipelineStage.Authenticate)
.UseUmbracoPreviewAuthentication(_umbracoContextAccessor, _runtimeState, _globalSettings, _securitySection, PipelineStage.PostAuthenticate);
}
and this is the LogOut method:
public void LogOut(string redirectUrl = "/") {
if (Request.IsAuthenticated) {
RemoveLoggedInMemberAccessToken();
IEnumerable<AuthenticationDescription> authTypes = HttpContext.GetOwinContext().Authentication.GetAuthenticationTypes();
AuthenticationProperties authenticationProperties = new AuthenticationProperties { RedirectUri = redirectUrl };
HttpContext.GetOwinContext().Authentication.SignOut(authenticationProperties, authTypes.Select(t => t.AuthenticationType).ToArray());
}
}
I follow below article to get work with asp.net identity in IdenityServer4:
http://docs.identityserver.io/en/release/quickstarts/6_aspnet_identity.html
Now i want to add Azure AD as external Idp :
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryPersistedGrants()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddAspNetIdentity<ApplicationUser>();
services.AddAuthentication()
.AddOpenIdConnect("AAD", "Azure Active Directory", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.SignOutScheme = IdentityServerConstants.SignoutScheme;
options.Authority = "https://login.microsoftonline.com/xxxxxx.onmicrosoft.com";
options.ClientId = "xxxxxxx";
options.Scope.Add("openid");
options.Scope.Add("profile");
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false
};
options.GetClaimsFromUserInfoEndpoint = true;
});
But when i debug my application ==> redirect to identity server==>click AAD login . I notice when i reach the Azure AD username/password page ,my client app stops , so after enter the credentials and consent , i will stay on the identity server page . Can anyone provides some suggestions ?
I see you commented out your Microsoft Authority URL, like it is specific to your company. I have Azure AD external login configured for my company's auth and I just have to call https://login.microsoftonline.com/common.
Also, if it is redirecting fine to Microsoft's site and not returning back to your Auth server, you probably do not have something set up properly in the ExternalLogin function in your controller.
It should look something like this:
var props = new AuthenticationProperties()
{
RedirectUri = Url.Action("ExternalLoginCallback"),
Items =
{
{ "returnUrl", returnUrl },
{ "scheme", provider },
}
};
return Challenge(props, provider);
Where provider would be your 'AAD'.
The interesting part is when i turn to use chrome to start my client app in visual studio , the client app is not stopped anymore . Quite strange and not to find a explanation .
I'm trying to use a local login form to authenticate a user credentials against its external provider (Azure Active Directory).
I understand that, per client, you can enable local login. That helps, as when set to true, I'll get the local login form but but I'm still unclear as to how to fire off the middle ware for that external provider. Is there a way to send client credentials to the external provider to receive an ID token? My current code redirects to the Microsoft login; and then back to my identity server, and then the client application. I want the user to login in through identity server but not have them know it's really authenticating against Azure.
Here's my start up:
var schemeName = "Azure-AD";
var dataProtectionProvibder = app.ApplicationServices.GetRequiredService<IDataProtectionProvider>();
var distributedCache = app.ApplicationServices.GetRequiredService<IDistributedCache>();
var dataProtector = dataProtectionProvider.CreateProtector(
typeof(OpenIdConnectMiddleware).FullName,
typeof(string).FullName, schemeName,
"v1");
var dataFormat = new CachedPropertiesDataFormat(distributedCache, dataProtector);
///
/// Azure AD Configuration
///
var clientId = Configuration["AzureActiveDirectory:ClientId"];
var tenantId = Configuration["AzureActiveDirectory:TenantId"];
Redirect = Configuration["AzureActiveDirectory:TenantId"];
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = schemeName,
DisplayName = "Azure-AD",
SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
ClientId = clientId,
Authority = $"https://login.microsoftonline.com/{tenantId}",
ResponseType = OpenIdConnectResponseType.IdToken,
StateDataFormat = dataFormat,
});
app.UseIdentity();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
This is the login.
[HttpGet]
public async Task<IActionResult> ExternalLogin(string provider, string returnUrl)
{
var context = this.HttpContext.Authentication;
List<AuthenticationDescription> schemes = context.GetAuthenticationSchemes().ToList();
returnUrl = Url.Action("ExternalLoginCallback", new { returnUrl = returnUrl });
// start challenge and roundtrip the return URL
var props = new AuthenticationProperties
{
RedirectUri = returnUrl,
Items = { { "scheme", provider } }
};
//await HttpContext.Authentication.ChallengeAsync(provider, props);
return new ChallengeResult(provider, props);
}
In my opinion ,we shouldn't directly pass the username/password directly from other Idp to azure AD for authentication as a security implementation .And even Azure AD supports the Resource Owner Password Credentials Grant ,it's only available in native client. I suggest you keep the normal way and don't mix them .
We are currently overriding WSFederationAuthenticationModule.RedirectToIdentityProvider in our product to change the returnUrl to which the users agent is redirected to after authentication.
Now we're in the proces of adopting OWIN (Katana) middleware instead of HttpModules. In the RedirectToIdentityProvider notification in WsFederationAuthenticationOptions, I see the WCtx parameter now contains a WsFedOwinState parameter which is encrypted using DPAPI.
How do I implement the RedirectToIdentityProvider action to change the return URL? Do I need to decrypt the WsFedOwinState parameter to add the returnUrl query parameter or is there some other way?
inside RedirectToIdentityProvider, you will have access to the WsFederationMessage.
Set the Wreply property to the value you need.
As a note: MachineKey is used by default, not DPAPI for protecting wctx.
In my case, I changed the return URL in SecurityTokenValidated and had the redirection from ADFS always go to the same URL
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(WsFederationAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType });
app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions
{
Wtrealm = realm,
MetadataAddress = adfsMetadata,
Notifications = new WsFederationAuthenticationNotifications
{
SecurityTokenValidated = nx =>
{
nx.AuthenticationTicket.Properties.RedirectUri = "/RedirectionGoesHere.aspx";
return Task.FromResult(0);
}
}
});
// This makes any middleware defined above this line run before the Authorization rule is applied in web.config
app.UseStageMarker(PipelineStage.Authenticate);
}
Our Mvc/WebAPI solution currently has four trusted identity providers which we have registered in ADFS3. Each of these identity providers can be used by our users by direct links, effectively working around any home-realm-cookies that ADFS may have created (eg: www.ourportal.com/accounts/facebook or www.ourportal.com/accounts/twitter). Currently we are migrating from WIF to OWIN but will keep using WS-Federation protocol for the time being by implementing wsfederation and cookie authentication middleware. When using WIF, we did the following in order to go directly to a known identity provider:
var signInRequest = new SignInRequestMessage(stsUrl, realm) { HomeRealm = homeRealm };
return new RedirectResult(signInRequest.WriteQueryString());
This seems to have two concerning behaviors, it does not pass the WsFedOwinState parameter, and on the return back to the Relying Party, the Home.cshtml is built (with a windows principal) before the the Owin authentication middleware is fired. The Home.cshtml being fired before the Owin middleware is the most concering as this view relies on Claims that would is provided in the transformation done by the authentication pipeline, which is fired afterwards and thus our view does not work. It works in the correct order when going to the portal in the normal way (eg www.ourportal.com)
I understand that in order to provide the Whr parameter, you do the following when configuring the ws-federation middleware:
RedirectToIdentityProvider = (context) =>
{
context.ProtocolMessage.Whr = "SomeUrnOfAnIdentityProvider";
return Task.FromResult(0);
}
but this sets a single identity provider for the whole solution and does not allow our users to go directly to one of a list of identity providers.
The non-working method which builds the sign-in-request is currently:
private RedirectResult FederatedSignInWithHomeRealm(string homeRealm)
{
var stsUrl = new Uri(ConfigurationManager.AppSettings["ida:Issuer"]);
string realm = ConfigurationManager.AppSettings["ida:Audience"];
var signInRequest = new SignInRequestMessage(stsUrl, realm)
{
HomeRealm = homeRealm
};
HttpContext.Request.GetOwinContext().Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType);
return new RedirectResult(signInRequest.WriteQueryString());
}
The ws-federation and cookie middleware are configured as the first middleware in OWIN startup and the default authentication is set to
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
I think I found a solution. The new method for skipping the home realm screen would be like this :
private void FederatedSignInWithHomeRealm(string homeRealm)
{
HttpContext.Request
.GetOwinContext()
.Authentication
.SignOut(CookieAuthenticationDefaults.AuthenticationType);
var authenticationProperties = new AuthenticationProperties { RedirectUri = "/" };
authenticationProperties.Dictionary.Add("DirectlyToIdentityProvider", homeRealm);
HttpContext.GetOwinContext().Authentication.Challenge(authenticationProperties);
}
And the OWIN WS-Federation middleware would be configured like this :
app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions
{
Notifications = new WsFederationAuthenticationNotifications()
{
RedirectToIdentityProvider = notification =>
{
string homeRealmId = null;
var authenticationResponseChallenge = notification.OwinContext
.Authentication
.AuthenticationResponseChallenge;
var setIdentityProvider = authenticationResponseChallenge != null
&& authenticationResponseChallenge.Properties
.Dictionary
.TryGetValue("DirectlyToIdentityProvider", out homeRealmId);
if (setIdentityProvider)
{
notification.ProtocolMessage.Whr = homeRealmId;
}
return Task.FromResult(0);
}
},
MetadataAddress = wsFedMetadata,
Wtrealm = realm,
SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = realm
}
});