I've been messing with this for a few days now...
What I would like to do is Authenticate users with Azure AD, and when successful, automatically log them in using ASP.NET Identity for authorization. If they do not have an account I would like to create one automatically.
Essentially Azure AD is just confirming that they are a part of the organization, the ASP.NET Identity portion is it's own database where I can use the [Authorize] attribute to set up custom roles OUTSIDE of Azure AD.
This is my ConfigureAuth() method:
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(IntranetApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = SettingsHelper.ClientId,
Authority = SettingsHelper.Authority,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(SettingsHelper.ClientId, SettingsHelper.AppKey);
String signInUserId = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.Authority, new ADALTokenCache(signInUserId));
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, SettingsHelper.AADGraphResourceId);
return Task.FromResult(0);
},
RedirectToIdentityProvider = (context) =>
{
// This ensures that the address used for sign in and sign out is picked up dynamically from the request
// this allows you to deploy your app (to Azure Web Sites, for example)without having to change settings
// Remember that the base URL of the address used here must be provisioned in Azure AD beforehand.
string appBaseUrl = context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase;
context.ProtocolMessage.RedirectUri = appBaseUrl + "/";
context.ProtocolMessage.PostLogoutRedirectUri = appBaseUrl;
return Task.FromResult(0);
},
AuthenticationFailed = (context) =>
{
// Suppress the exception if you don't want to see the error
context.HandleResponse();
return Task.FromResult(0);
}
}
});
// 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,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
},
});
}
Right now the ASP.NET Identity is taking over when I do a HttpContext.Request.IsAuthenticated which is okay, I just need a way to check if the OpenID portion is authenticated or not so I can put in my custom logic to automatically sign the user in.
Got it!
My biggest problem was attempting to use the OWIN middleware to do everything for me. The OpenID middleware is not needed for simple authentication to Azure AD. I essentially created a OpenIdAuth method in the Account controller which acts as my in-between to authenticate the user with Azure before they have access to the site.
[AllowAnonymous]
public ActionResult OpenIdAuth(string code)
{
string clientId = "00000000-0000-0000-0000-000000000000"; // Client ID found in the Azure AD Application
string appKey = "111111111112222222222223333333333AAABBBCCC="; // Key generated in the Azure AD Appliction
if (code != null)
{
string commonAuthority = "https://login.windows.net/<TENANT_URL>"; // Eg. https://login.windows.net/MyDevSite.onmicrosoft.com
var authContext = new AuthenticationContext(commonAuthority);
ClientCredential credential = new ClientCredential(clientId, appKey);
AuthenticationResult authenticationResult = authContext.AcquireTokenByAuthorizationCode(code, new Uri(HttpContext.Request.Url.GetLeftPart(UriPartial.Path)), credential, "https://graph.windows.net");
var userManager = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
var signInManager = HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
var user = UserManager.FindByName(authenticationResult.UserInfo.UniqueId);
if (user != null)
{
signInManager.SignIn(user, false, false);
}
else
{
var newUser = new ApplicationUser { UserName = authenticationResult.UserInfo.UniqueId, Email = authenticationResult.UserInfo.DisplayableId };
var creationResult = UserManager.Create(newUser);
if (creationResult.Succeeded)
{
user = UserManager.FindByName(newUser.UserName);
signInManager.SignIn(user, false, false);
}
else
{
return new ViewResult { ViewName = "Error" };
}
}
return Redirect("/");
}
else
{
var url = new Uri($"https://login.microsoftonline.com/<TENANT_URL>/oauth2/authorize?client_id={clientId}&response_type=code&redirect_uri=https://localhost/Account/OpenIdAuth");
return Redirect(url.AbsoluteUri);
}
}
The awesome part is the code variable that will be passed by Microsoft when the user logs in successfully. (As documented Here) I used the same controller method and checked if it was null but technically two different controller methods can be used (Microsoft will redirect back to the url you specify for the redirect_uri parameter).
After I got the authorization code I can use the AuthorizationContext from the Microsoft.IdentityModel.Clients.ActiveDirectory nuget package to call: AcquireTokenByAuthorizationCode. The last parameter is the Resource URI. I'm using the Graph Resource but you can use any other resource that you've given your app access to in the Azure Management Portal.
Finally my ConfigureAuth Method is back to the plain ol' ASP.NET Identity version:
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(IntranetApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.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,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
},
});
}
Related
I have an MVC project that uses Azure AD as a connected service for single sign-on authentication of the user. That works fine, and any controller with the [Authorize] attribute works as expected.
I have two app roles defined in Azure for this application, and I've assigned myself to both of them. But when I add [Authorize(Roles="foo")] to a controller, the application redirects to Microsoft to ask for another sign in, and then continues to do that forever. I can't tell whether the roles aren't being passed back in the token, or whether MVC is failing to pick up the roles that are being passed back.
I've tried using KentorOwinCookieSaver but that didn't seem to address the problem.
Is there an additional step I need to take to get MVC to recognize the Azure appRoles? I'm not using Identity Manager or storing any user info in the database.
Here's my Startup.Auth:
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{ CookieSecure = CookieSecureOption.Always,
CookieManager = new Microsoft.Owin.Host.SystemWeb.SystemWebChunkingCookieManager()});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new NaiveSessionCache(signedInUserID));
return authContext.AcquireTokenByAuthorizationCodeAsync(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
},
AuthenticationFailed = (context) =>
{
context.HandleResponse();
context.Response.Redirect("/Home/Error?message=" + context.Exception.Message);
return Task.FromResult(0);
}
}
});
}
This turned out to be a ridiculous mistake on my end. After looking at the token again, at Win's prompting, and comparing it to the app registration manifest in the Azure portal, I see the problem. I was trying to use the "displayName" of the role, which is what is displayed on the administration screens, rather than the "value" which is, predictably, the value passed back with the token.
Oops.
In my login web page, I have a webform (Identity 2.0) you can use to create an account in a database or you can use Azure Active Directory to authenticate with a corporate email. (External auth)
I've put the [Authorize] attribute to decorate the Index() action in UserController. My List() action in the same controller is decorated like this : [Authorize (Roles = "Admin")]
When logged in with my webform login, if I go to /MyController/List/ I'm redirected to the Microsoft account Login page. When going to /MyController/Index I'm not redirected.
What is causing this behavior? I don't want to check in Azure when the user is logged in with webform. How can I prevent this from happening?
Here's my Startup.Auth.cs
public partial class Startup
{
private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
private static string appKey = ConfigurationManager.AppSettings["ida:ClientSecret"];
private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
private static string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
public static readonly string Authority = aadInstance + tenantId;
// This is the resource ID of the AAD Graph API. We'll need this to request a token to call the Graph API.
string graphResourceId = "https://graph.windows.net";
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and role manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.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,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
// Enables the application to remember the second login verification factor such as phone or email.
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
// This is similar to the RememberMe option when you log in.
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
// Pour Azure
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
AuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new ADALTokenCache(signedInUserID));
Task<AuthenticationResult> result = authContext.AcquireTokenByAuthorizationCodeAsync(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
return Task.FromResult(0);
}
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
//app.UseGoogleAuthentication(
// clientId: "",
// clientSecret: "");
}
}
EDIT
Here's the controller code
using System.Web.Mvc;
namespace MyApp.Controllers
{
public class AccueilController : Controller
{
[Authorize]
public ActionResult Index()
{
return View();
}
[Authorize(Roles = "Admin")]
public ActionResult List()
{
return View();
}
}
}
I'm logged in with Cookie Authenticatoin (email/password).
I hit the Index action, I see the content of that page inside my app.
If I hit the List() action, I'm redirected to OpenIdConnect login page.
The webforms user doesn't have the Admin role, but your AD user seems to have the Admin role. You can verify this by quickly querying the AspNetUserRoles table.
Decorating an action with [Authorize(Roles="Admin")] will make it accessible only to users with the role. The default behaviour for unauthorized users (irrespective of whether the user's authentication status) is to redirect them to the login page.
Your options are:
Subclass the AuthorizeAttribute and override the default behavior.
Redirect the user somewhere else (perhaps a forbidden page with the right status code set) from the login page if he's authenticated.
As far as I know, the default redirect behavior cannot be overriden without subclassing the AuthorizeAttribute like you can in ASP.NET Core.
We are already running an ASP.NET MVC web application which is using internal users via token authentication. This is implemented in the standard way the ASP.NET MVC template provides.
Now we have a requirement to extend this authentication model and allow external Azure AD users to sign into the web application for configured tenant. I have figured out everything on the Azure AD side. Thanks to Microsoft's Azure Samples example.
Now both individual account authentication and Azure AD are working well independently. But they're not working together. When I insert both middleware together its giving issue.
Here's my startup_auth.cs file:
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
string ClientId = ConfigurationManager.AppSettings["ida:ClientID"];
string Authority = "https://login.microsoftonline.com/common/";
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = ClientId,
Authority = Authority,
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = false,
},
Notifications = new OpenIdConnectAuthenticationNotifications()
{
RedirectToIdentityProvider = (context) =>
{
string appBaseUrl = context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase;
context.ProtocolMessage.RedirectUri = appBaseUrl;
context.ProtocolMessage.PostLogoutRedirectUri = appBaseUrl;
return Task.FromResult(0);
},
SecurityTokenValidated = (context) =>
{
// retriever caller data from the incoming principal
string issuer = context.AuthenticationTicket.Identity.FindFirst("iss").Value;
string UPN = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.Name).Value;
string tenantID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
if (
// the caller comes from an admin-consented, recorded issuer
(db.Tenants.FirstOrDefault(a => ((a.IssValue == issuer) && (a.AdminConsented))) == null)
// the caller is recorded in the db of users who went through the individual onboardoing
&& (db.Users.FirstOrDefault(b =>((b.UPN == UPN) && (b.TenantID == tenantID))) == null)
)
// the caller was neither from a trusted issuer or a registered user - throw to block the authentication flow
throw new SecurityTokenValidationException();
return Task.FromResult(0);
},
AuthenticationFailed = (context) =>
{
context.OwinContext.Response.Redirect("/Home/Error?message=" + context.Exception.Message);
context.HandleResponse(); // Suppress the exception
return Task.FromResult(0);
}
}
});
}
}
This configuration works well for local user accounts but doesn't work for AAD. To enable AAD authentication, I need to configure the UseCookieAuthentication part as shown below, which will break my local user account authentication.
app.UseCookieAuthentication(new CookieAuthenticationOptions { });
Basically I need to remove the local users middleware to get AAD work.
What I mean by AAD not working is, I am not able to go to any secured action which is protected by the [Authorize] attribute. It's calling the SecurityTokenValidated event, and I am able to get all AAD claims and able to validate against my custom tenant. But when I redirect to root of my app (which is a secured action) at the end, it throws me back to my custom login page. Seems it's not internally signing in the user and not creating the necessary authentication cookies.
I would appreciate any ideas on what I could be missing here.
Thanks
To support both individual accounts and other account from social data provider, only need to add them using OWIN component.
And to sign-out the users which login from Azure AD, we need to sign-out both the cookie issued from web app and Azure AD. First, I modified the ApplicationUser class to add the custom claim to detect whether the users login from Azure AD or individual accounts like below.
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
if((this.Logins as System.Collections.Generic.List<IdentityUserLogin>).Count>0)
userIdentity.AddClaim(new Claim("idp", (this.Logins as System.Collections.Generic.List<IdentityUserLogin>)[0].LoginProvider));
return userIdentity;
}
}
Then we can change the LogOff method to support sign-out from Azure AD to clear the cookies from Azure AD:
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
var idpClaim = ClaimsPrincipal.Current.Claims.FirstOrDefault(claim => { return claim.Type == "idp"; });
if (idpClaim!=null)
HttpContext.GetOwinContext().Authentication.SignOut(
OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType);
return RedirectToAction("Index", "Home");
}
It sounds like your OpenID Connect auth is not connecting to your Cookie auth. It looks like you need to specify a SignInAsAuthenticationType in your OpenIdConnectAuthenticationOptions that matches the AuthenticationType in your CookieAuthenticationOptions or your ExternalCookie auth type.
I'm trying to use an Azure AD to authenticate users. Everything seems to be working fine until the redirect to RegisterExternalLogin.aspx.cs. For some reason the Context.GetOwinContext().Authentication.GetExternalLoginInfo() method keeps returning null, causing the application to redirect. Since it redirects here it's failing to create a new Identity User which is causing me issues further down the line.
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
// app.UseCookieAuthentication(new CookieAuthenticationOptions());
var openIDOptions = new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
RedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/Error?message=" + context.Exception.Message);
return Task.FromResult(0);
}
}
};
openIDOptions.Scope = "openid profile email";
app.UseOpenIdConnectAuthentication(openIDOptions);
}
Here's the Page_Load() from RegisterExternalLogin.aspx.cs
protected void Page_Load()
{
// Process the result from an auth provider in the request
ProviderName = IdentityHelper.GetProviderNameFromRequest(Request);
if (String.IsNullOrEmpty(ProviderName))
{
RedirectOnFail();
return;
}
if (!IsPostBack)
{
var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
var signInManager = Context.GetOwinContext().Get<ApplicationSignInManager>();
var loginInfo = Context.GetOwinContext().Authentication.GetExternalLoginInfo();
if (loginInfo == null)
{
RedirectOnFail();
return;
}
var user = manager.Find(loginInfo.Login);
if (user != null)
{
signInManager.SignIn(user, isPersistent: false, rememberBrowser: false);
IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);
}
else if (User.Identity.IsAuthenticated)
{
// Apply Xsrf check when linking
var verifiedloginInfo = Context.GetOwinContext().Authentication.GetExternalLoginInfo(IdentityHelper.XsrfKey, User.Identity.GetUserId());
if (verifiedloginInfo == null)
{
RedirectOnFail();
return;
}
var result = manager.AddLogin(User.Identity.GetUserId(), verifiedloginInfo.Login);
if (result.Succeeded)
{
IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);
}
else
{
AddErrors(result);
return;
}
}
else
{
email.Text = loginInfo.Email;
}
}
}
I saw a couple of answers related to cookies but these don't seem to fix the issue for me. Any help would be much appreciated.
To implement Azure AD external login using OpenID Connect with ASP.NET identity .You need to enable the application to use a cookie to store information for the signed in user and use a cookie to temporarily store information about a user logging in with a third party login provider . Please refer to below configuration:
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.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,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
// Use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
//app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
// Enables the application to remember the second login verification factor such as phone or email.
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
// This is similar to the RememberMe option when you log in.
//app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
var openIDOptions = new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
RedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/Error?message=" + context.Exception.Message);
return Task.FromResult(0);
}
}
};
openIDOptions.Scope = "openid profile email";
app.UseOpenIdConnectAuthentication(openIDOptions);
I am using mvc 5 with identity 2.0. I want use custom claim values over the application but I get null values. What am I doing wrong?
Updated code
Login Code in account controller
if (!string.IsNullOrEmpty(model.UserName) && !string.IsNullOrEmpty(model.Password))
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var result = SignInManager.PasswordSignIn(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
//Generate verification token
Dictionary<string, string> acceccToken = null;
if (SignInStatus.Success == 0)
{
var userDeatails = FindUser(model.UserName, model.Password).Result;
if (userDeatails != null)
acceccToken = GetTokenDictionary(model.UserName, model.Password, userDeatails.Id);
}
if (model.RememberMe)
{
HttpCookie userid = new HttpCookie("rembemberTrue", "1");
userid.Expires.AddDays(1);
Response.Cookies.Add(userid);
}
else
{
HttpCookie userid = new HttpCookie("rembemberTrue", "0");
userid.Expires.AddDays(1);
Response.Cookies.Add(userid);
}
#region custom claims
var claims = new Claim[]
{
new Claim("urn:Custom:MasterUniqueId", Convert.ToString(Guid.NewGuid()))
};
ClaimsIdentity identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
IAuthenticationManager authenticationManager = System.Web.HttpContext.Current.GetOwinContext().Authentication;
authenticationManager.SignIn(identity);
Starup.Auth.cs
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.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,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
},
SlidingExpiration = true,
ExpireTimeSpan = TimeSpan.FromMinutes(60)
});
another controller
Here I am trying to fetch that claim values but it shows null
var identity = (ClaimsIdentity)User.Identity;
var res= identity.FindFirst("urn:Custom:MasterUniqueId");
res is null
You should add those claims on identity validation phase. Please check similar implementation here: Server side claims caching with Owin Authentication
In your account controller, to get a valid authenticationManager, you should use Request.GetOwinContext().Authentication. Im affraid System.Web.HttpContext.Current.GetOwinContext().Authentication gets you a fresh authenticationManager instead of the current Owin context one
First you need to convert identity to claims identity and then try to get claim using identity type
(HttpContext.Current?.User?.Identity as ClaimsIdentity)?.Claims?.FirstOrDefault(x => x.Type == "urn:Custom:MasterUniqueId")?.Value