OWIN Authentication with Azure AD B2C OP: SIgnOut Doesn't Remove Cookie when Called from External iFrame - owin

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());
}
}

Related

Not able to solve Infinite Loop Issue after Azure AD Authentication using OpenIDConnect - Authorization Code grant

We are implementing Azure AD Authentication in ASP.NET MVC 5 using Open ID Connect. When the application was running on premise we had windows Authentication, so there is no login page or Login button.
We have put [Authorize] attribute to all the controllers so that the user is authenticated before accessing the page. Below is the code in start up Auth.
app.UseKentorOwinCookieSaver();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies",
CookieManager = new Microsoft.Owin.Host.SystemWeb.SystemWebChunkingCookieManager()
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
RedirectUri = RedirectUri,
ResponseType = OpenIdConnectResponseType.Code,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthenticationFailed = (context) => {
context.HandleResponse();
return Task.FromResult(0);
}
}
});
But we are facing the Infinite loop once the user is authenticated. And I tried all the solutions on internet, but my issue is not solved.
https://stackoverflow.com/a/37666371/55775
https://github.com/Sustainsys/owin-cookie-saver
https://github.com/aspnet/AspNetKatana/wiki/System.Web-response-cookie-integration-issues
ASP.NET_SessionId + OWIN Cookies do not send to browser
asp.net mvc azure AAD authentication infinite loop

Kentor/Owin/Azure AD Authentication

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");

IdentityServer4 - is it possible to use local login form with external provider and no round trip?

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 .

Is it right way to use Owin.Security.ActiveDirectory library with AAD B2C

We have two Application registered in the same AAD B2C tenant via "New" and "Old" portal.
Authentication with "Old" Application credentials works correct.
With "New" Application credentials - error appears:
IDX10500: Signature validation failed. Unable to resolve SecurityKeyIdentifier: 'SecurityKeyIdentifier
(
IsReadOnly = False,
Count = 1,
Clause[0] = System.IdentityModel.Tokens.NamedKeySecurityKeyIdentifierClause
)
'
Is it right way to use Microsoft.Owin.Security.ActiveDirector library (to protect ASP.Net Web API) with Application registered in AAD B2C tenant.
P.S. My question is based on this post.
You should only create applications via the Azure AD B2C blade in the new Azure portal (portal.azure.com).
Do NOT create applications for Azure AD B2C using the classic Azure Portal (manage.windowsazure.com).
If you want to secure a WebApp, you should use Owin's OpenIdConnectAuthentication. This document has more details on how to do this: Sign-Up & Sign-In in a ASP.NET Web App
If you want to secure a WebAPI, you should use Owin's OAuthBearerAuthentication. This document has more details on how to do this: Build a .NET web API
Samples configuration of a WebApp:
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Generate the metadata address using the tenant and policy information
MetadataAddress = String.Format(AadInstance, Tenant, DefaultPolicy),
// These are standard OpenID Connect parameters, with values pulled from web.config
ClientId = ClientId,
RedirectUri = RedirectUri,
PostLogoutRedirectUri = RedirectUri,
// Specify the callbacks for each type of notifications
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = OnRedirectToIdentityProvider,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed,
},
// Specify the claims to validate
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name"
},
// Specify the scope by appending all of the scopes requested into one string (separated by a blank space)
Scope = $"{OpenIdConnectScopes.OpenId} {YourScope1} {YourScope2}"
}
);
}
Samples configuration of a Web API:
public void ConfigureAuth(IAppBuilder app)
{
TokenValidationParameters tvps = new TokenValidationParameters
{
// Accept only those tokens where the audience of the token is equal to the client ID of this app
ValidAudience = ClientId,
AuthenticationType = Startup.DefaultPolicy
};
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
// This SecurityTokenProvider fetches the Azure AD B2C metadata & signing keys from the OpenIDConnect metadata endpoint
AccessTokenFormat = new JwtFormat(tvps, new OpenIdConnectCachingSecurityTokenProvider(String.Format(AadInstance, Tenant, DefaultPolicy)))
});
}

Azure B2C with Web API

The examples I've seen for using Azure B2C with Web API show app.UseOAuthBearerAuthentication (as shown below), however my ASP .NET 5 Web API project uses IApplicationBuilder (not IAppBuilder) and UseOAuthBearerAuthentication does not exist. I've tried app.UseOpenIdConnectAuthentication, however I believe this uses cookies and I couldn't get it working using a Xamarin app as a client. I've tried app.UseWindowsAzureActiveDirectoryBearerAuthentication but I believe this is for standard Azure AD (not B2C) is that true? Any ideas how to get Azure B2C working with the very latest ASP .NET Web API?
Thanks!!!
public void ConfigureAuth(IAppBuilder app)
{
TokenValidationParameters tvps = new TokenValidationParameters
{
// This is where you specify that your API only accepts tokens from its own clients
ValidAudience = clientId,
};
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
// This SecurityTokenProvider fetches the Azure AD B2C metadata & signing keys from the OpenIDConnect metadata endpoint
AccessTokenFormat = new JwtFormat(tvps, new OpenIdConnectCachingSecurityTokenProvider(String.Format(aadInstance, tenant, "v2.0", discoverySuffix, commonPolicy)))
});
}
This works for me. I hope it helps someone else who is looking to use Azure B2C with the latest .NET Web API framework:
public void ConfigureAuth(IApplicationBuilder app, IOptions<PolicySettings> policySettings)
{
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AuthenticationScheme = JwtBearerDefaults.AuthenticationScheme,
AutomaticAuthenticate = true,
AutomaticChallenge = true,
MetadataAddress = "https://login.microsoftonline.com/[my-tenant].onmicrosoft.com/v2.0/.well-known/openid-configuration?p=B2C_1_my-signup-signin-policy",
Audience = "[My-Azure-App-Guid]",
Events = new JwtBearerEvents
{
OnTokenValidated= ctx =>
{
var nameClaim = ctx.AuthenticationTicket.Principal.FindFirst("name");
if (nameClaim != null)
{
var claimsIdentity = (System.Security.Claims.ClaimsIdentity)ctx.AuthenticationTicket.Principal.Identity;
claimsIdentity.AddClaim(new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Name, nameClaim.Value));
}
return Task.FromResult(0);
},
OnAuthenticationFailed = ctx =>
{
ctx.SkipToNextMiddleware();
return Task.FromResult(0);
}
}
});
}
If the JWT token is available in your client, then you can use JwtBearerAuthentication in the Web API. The JWT issued by Azure B2C ( social logins) can be used as a token to get authenticated in the Web API.
Refer to https://github.com/sendhilkumarg/AzureB2CWebAppAndAPIAuthentication
In this sample the client is a web app

Resources