Mixing cookie external login using Azure AD and individual account in MVC5 - asp.net-mvc

I am getting problems with application cookie and external cookie that are integrated login with Azure AD to my web app using MVC5. Currently, my local account work correctly but external account (Google and Azure AD) cannot map external cookie to local cookie. My code get userId return incorrect user Id.
IIdentity ident = HttpContext.Current.GetOwinContext().Request.User.Identity;
ident.GetUserId()
Below is my startup.cs
public partial class Startup
{
// The Client ID is used by the application to uniquely identify itself to Azure AD.
string clientId = System.Configuration.ConfigurationManager.AppSettings["ClientId"];
// RedirectUri is the URL where the user will be redirected to after they sign in.
string redirectUri = System.Configuration.ConfigurationManager.AppSettings["RedirectUri"];
string postLogoutRedirectUri = System.Configuration.ConfigurationManager.AppSettings["PostLogoutRedirectUri"];
// Tenant is the tenant ID (e.g. contoso.onmicrosoft.com, or 'common' for multi-tenant)
static string tenant = System.Configuration.ConfigurationManager.AppSettings["Tenant"];
// Authority is the URL for authority, composed by Microsoft identity platform endpoint and the tenant name (e.g. https://login.microsoftonline.com/contoso.onmicrosoft.com/v2.0)
string authority = String.Format(System.Globalization.CultureInfo.InvariantCulture, System.Configuration.ConfigurationManager.AppSettings["Authority"], tenant);
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(AppIdentityDbContext.Create);
app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create);
app.CreatePerOwinContext<AppSignInManager>(AppSignInManager.Create);
app.CreatePerOwinContext<AppRoleManager>(AppRoleManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
AuthenticationMode = AuthenticationMode.Active,
LoginPath = new PathString("/"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<AppUserManager, AppUser>(
validateInterval: TimeSpan.FromHours(1),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
},
ExpireTimeSpan = TimeSpan.FromHours(1),
//Samesite secure
CookieSameSite = SameSiteMode.Lax,
CookieHttpOnly = true,
CookieSecure = CookieSecureOption.Always,
CookieManager = new SameSiteCookieManager(new SystemWebCookieManager())
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
//Open Id Connect
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ExternalCookie,
CookieManager = new SameSiteCookieManager(new SystemWebCookieManager())
});
app.UseOpenIdConnectAuthentication(CreateOpenIdOptions());
// GOOGLE
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = ConfigurationManager.AppSettings["GoogleClientID"].ToString(),
ClientSecret = ConfigurationManager.AppSettings["GoogleClientSecret"].ToString()
});
}
private OpenIdConnectAuthenticationOptions CreateOpenIdOptions()
{
var options = new OpenIdConnectAuthenticationOptions
{
Authority = authority,
ClientId = clientId,
RedirectUri = redirectUri,
AuthenticationMode = AuthenticationMode.Passive,
// PostLogoutRedirectUri is the page that users will be redirected to after sign-out. In this case, it is using the home page
PostLogoutRedirectUri = postLogoutRedirectUri,
Scope = OpenIdConnectScope.OpenIdProfile, // a basic set of permissions for user sign in & profile access
// ResponseType is set to request the id_token - which contains basic information about the signed-in user
ResponseType = OpenIdConnectResponseType.IdToken,
TokenValidationParameters = new TokenValidationParameters
{
// In a real application you would use ValidateIssuer = true for additional checks and security.
ValidateIssuer = false,
},
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthenticationFailed = OnAuthenticationFailed,
},
// Handling SameSite cookie according to https://learn.microsoft.com/en-us/aspnet/samesite/owin-samesite
CookieManager = new SameSiteCookieManager(
new SystemWebCookieManager()),
};
return options;
}
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
// Handle any unexpected errors during sign in
context.OwinContext.Response.Redirect("/Error?message=" + context.Exception.Message);
context.HandleResponse(); // Suppress the exception
return Task.FromResult(0);
}
}
Below is sign out method which is called before sign in
var authenticationTypes = new string[] {
DefaultAuthenticationTypes.ApplicationCookie,
DefaultAuthenticationTypes.ExternalCookie,
};
AuthManager.SignOut(authenticationTypes);
I also already tried apply many fixed posts related to this but it does not work. How can we resolve external cookie map to local cookie?

Finally, I found workaround solutions below:
First if you don want to use Open id connect use the link below
Use Azure AD only for Authentication and not Authorization
I use Kentor.OwinCookieSaver to resolve my problem
https://github.com/Sustainsys/owin-cookie-saver

Related

Azure AD OWIN and OpenID Connect authentication problem IDX21323

My application is just looping redirection between login.microsoft.com and https://localhost:44353/
I have tried multiple workarounds provided like using Kentor and SystemWebChunkingCookieManager
Here is my startup.cs
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies",
CookieManager = new SystemWebChunkingCookieManager()
});
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 is the page that users will be redirected to after sign-out. In this case, it is using the home page
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
// ResponseType is set to request the id_token - which contains basic information about the signed-in user
ResponseType = OpenIdConnectResponseType.IdToken,
// ValidateIssuer set to false to allow personal and work accounts from any organization to sign in to your application
// To only allow users from a single organizations, set ValidateIssuer to true and 'tenant' setting in web.config to the tenant name
// To allow users from only a list of specific organizations, set ValidateIssuer to true and use ValidIssuers parameter
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false // This is a simplification
},
// OpenIdConnectAuthenticationNotifications configures OWIN to send notification of failed authentications to OnAuthenticationFailed method
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed
}
}
);
}
/// <summary>
/// Handle failed authentication requests by redirecting the user to the home page with an error in the query string
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
context.HandleResponse();
context.Response.Redirect("/?errormessage=" + context.Exception.Message);
return Task.FromResult(0);
}
}

Mixing ASP.net Identity and Azure AD authentication

We use ASP.Net Identity for our DB backed login currently and need to add support for Azure AD SSO.
I appreciate once logged in I will need to link the SSO user to a user in our system to assign the relevant Claims and Roles but am struggling to get the 2 authentication methods working side by side and app.UseCookieAuthentication seems to be at the root of my problems.
Currently we have:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Login/Index"),
ReturnUrlParameter = "url",
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, IdentityUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
},
ExpireTimeSpan = TimeSpan.FromMinutes(double.Parse(ConfigurationManager.AppSettings["AuthenticationTimeout"])),
SlidingExpiration = true
});
With this in I suspect the cookie returned by the SSO isn't getting processed correctly as the Request.IsAuthenticated is always false.
If I change it to:
app.UseCookieAuthentication(new CookieAuthenticationOptions());
Then the SSO works and returns me an authenticated request but obviously breaks the Identity login.
For info my OpenId setup is as follows, for now just trying to get it to work with our work AD but eventually will need to expand to multi tenant:
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
}
}
);
Any help or pointers appreciated.
try using the
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
before
app.UseOpenIdConnectAuthentication

Azure B2C authentication redirects locally, but not when hosted on Azure

I'm new to Azure AD B2C. I'm trying to set up azure B2C authentication for an MVC application.
The login works fine locally, but when it's not working on server.
The application is hosted on Azure AD.
I don't know if I missed something!! Can someone please help?
private OpenIdConnectAuthenticationOptions CreateOptionsFromPolicy(string policy)
{
return new OpenIdConnectAuthenticationOptions
{
// set the authentication type to the id of the policy
MetadataAddress = metaDataAddress,
AuthenticationType = policy,
// These are standard OpenID Connect parameters, with values pulled from web.config
ClientId = clientId,
//ClientSecret = clientSecret,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = AuthenticationFailed,
},
Scope = "openid",
ResponseType = "id_token",
// used for displaying the user's name in the navigation bar.
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role",
SaveSigninToken = true
}
};
}
When it is deployed on to the server, after sign in, it is not returning to the application. Instead the page seems to blink and in between I can see something displayed as "As part of the authentication process the page is displayed several times, please click the button to continue"..
Startup.cs file seems alright to me.Couple things to cross check:
redirectUri matches with the website
Here is my signup and sign in action method:
public void SignIn()
{
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties() { RedirectUri = "/" }, Startup.SignInPolicyId);
}
}
public void SignUp()
{
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties() { RedirectUri = "/" }, Startup.SignUpPolicyId);
}
}
I have followed below github repo and it worked for me. Try this and see if it works
https://github.com/tjoudeh/Azure-Active-Directory-B2C/tree/master/AADB2C.WebClientMvc/Controllers
Reference:
http://bitoftech.net/2016/08/31/integrate-azure-ad-b2c-asp-net-mvc-web-app/
Please provide the detailed error with entire code base will try to reproduce at my end.

Get custom claims in client application after login cookie issued using Identity Server 3

We are in the process of learning Identity Server with the eventual aim of migrating our existing authentication service to it. For company, logistical and compatibility reasons, we are sticking with IS 3. We're not quite ready to move over to Core.
There are two parts to my question:
1) I have modified the sample app, available here, that uses a custom login page so that the browser prompts the user for their X509Certificate2 (as a partial login). The user enters a password and the certificate is used to call another endpoint which returns user-specific data. At that point, we wish to create custom user claims based on the returned data and then issue the cookie.
This all works fine up until the client receives the cookie. I cannot seem to extract the custom claims added to AuthenticatedLogin's Claims object on the client application. The client is configured to access all scopes.
It seems like I'm missing something very basic. Am I doing something wrong here? Bear in mind, these are just meaningless claims for test purposes.
2) Would this be an acceptable approach to issue claims? We would then likely use the returned cookie in order to call a separate authorisation service, as our roles are quite complex.
I have implemented the custom user service, with PreAuthenticateAsync redirecting to the custom login page:
public override Task PreAuthenticateAsync(PreAuthenticationContext context)
{
var id = ctx.Request.Query.Get("signin");
context.AuthenticateResult = new AuthenticateResult("~/custom/login?id=" + id, (IEnumerable<Claim>)null);
return Task.FromResult(0);
}
The controller method which creates the claims and calls IssueLoginCookie :
[RequireHttps]
[Route("core/custom/login")]
[HttpPost]
public ActionResult Index(string id, string password)
{
var userData = GetUser(password);
var owinEnvironment = Request.GetOwinContext().Environment;
var authenticatedLogin = new AuthenticatedLogin
{
IdentityProvider = Constants.BuiltInIdentityProvider,
Name = userData.UserName,
Subject = userData.EmailAddress,
Claims = GetClaims(userData),
PersistentLogin = false
};
owinEnvironment.IssueLoginCookie(authenticatedLogin);
var msg = owinEnvironment.GetSignInMessage(id);
var returnUrl = msg.ReturnUrl;
owinEnvironment.RemovePartialLoginCookie();
return Redirect(returnUrl);
}
// add our CUSTOM claims
private List<Claim> GetClaims(CustomUser authenticatedUser)
{
List<Claim> claims = new List<Claim>();
claims.Add(new Claim("claim1", authenticatedUser.CustomClaim1));
claims.Add(new Claim("claim2", authenticatedUser.CustomClaim2));
claims.Add(new Claim("claim3", authenticatedUser.CustomClaim3));
claims.Add(new Claim("Claim4", authenticatedUser.CustomClaim4));
return claims;
}
The client controller method with Authorize decorator:
[Authorize]
public ActionResult About()
{
// "CustomClaim1", "CustomClaim2" etc are not there :(
return View((User as ClaimsPrincipal).Claims);
}
The registered in-memory scope:
var scope1 = new Scope
{
Enabled = true,
Name = "user",
Type = ScopeType.Identity,
Claims = new List<ScopeClaim>
{
new ScopeClaim("CustomClaim1", true),
new ScopeClaim("CustomClaim2", true),
new ScopeClaim("CustomClaim3", true),
new ScopeClaim("CustomClaim4", true),
},
IncludeAllClaimsForUser = true
};
And finally the client's Configuration:
public void Configuration(IAppBuilder app)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
AntiForgeryConfig.UniqueClaimTypeIdentifier = Constants.ClaimTypes.Subject;
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap = new Dictionary<string, string>();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = "https://localhost/idprov/core",
ClientId = "mvc",
RedirectUri = "https://localhost/dummyclient/About",
ResponseType = "id_token",
ClientSecret = "secret",
Scope = "openid partyuser",
SignInAsAuthenticationType = "Cookies",
});
}
Hi Try adding scope in your client like
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = "https://localhost/idprov/core",
ClientId = "mvc",
RedirectUri = "https://localhost/dummyclient/About",
ResponseType = "id_token",
ClientSecret = "secret",
Scope = "openid partyuser CustomClaim1 CustomClaim2",
SignInAsAuthenticationType = "Cookies",
});

Owin OpenId Callback url

I'm working in implementing OpenId in a .NET MVC application.
I have used: https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-v1-aspnet-webapp
as a starting guide. It is working, but i have 1 question regarding RedirectUri and CallbackPath.
If i only use RedirectUri, the callback page in my application gets a 302 redirection.
If i use CallbackPath the callback page is actually hit.
It is not really clear from the example what is going on? This is from MS:
"An optional constrained path on which to process the authentication callback. If not provided and RedirectUri is available, this value will be generated from RedirectUri."
I'm using [Authorize] attribute on my controllers.
Code Startup.cs:
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
var openIdOptions = new OpenIdConnectAuthenticationOptions
{
// Sets the ClientId, authority, RedirectUri as obtained from web.config
ClientId = ApplicationIdentifier,
Authority = FederationGateway,
RedirectUri = RedirectUrl,
ClientSecret = AppToken,
AuthenticationMode = AuthenticationMode.Active,
//CallbackPath = new PathString("/callback/"),
// PostLogoutRedirectUri is the page that users will be redirected to after sign-out. In this case, it is using the home page
PostLogoutRedirectUri = "~/home/loggedout/",
//Scope is the requested scope: OpenIdConnectScopes.OpenIdProfileis equivalent to the string 'openid profile': in the consent screen, this will result in 'Sign you in and read your profile'
Scope = OpenIdConnectScope.OpenIdProfile,
// ResponseType is set to request the id_token - which contains basic information about the signed-in user
////ResponseType = OpenIdConnectResponseType.IdToken,
ResponseType = OpenIdConnectResponseType.IdTokenToken,
// ValidateIssuer set to false to allow work accounts from any organization to sign in to your application
// To only allow users from a single organizations, set ValidateIssuer to true and 'tenant' setting in web.config to the tenant name or Id (example: contoso.onmicrosoft.com)
// To allow users from only a list of specific organizations, set ValidateIssuer to true and use ValidIssuers parameter
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false
},
// OpenIdConnectAuthenticationNotifications configures OWIN to send notification of failed authentications to OnAuthenticationFailed method
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
SecurityTokenValidated = OnSecurityTokenValidated
}
};
app.UseOpenIdConnectAuthentication(openIdOptions);
AntiForgeryConfig.UniqueClaimTypeIdentifier = IdentityNameIdentifier;
}

Resources