User unauthorized after Azure AD login to different application simultaneously - asp.net

I have two MVC applications AppA and AppB, and implemented Azure AD authentication for login.
I am able to sign-in successfully to both applications.
But the issue is, after I login to AppA and then to AppB, after sometime when I return back to AppA I am facing the issue where user has been logged out, and it again redirects to login screen (in AppA).
After I login to AppA (second time) and go back to AppB (user in AppB is logged out).
Client IDs are different ; TenandID is same. Both apps are hosted in same server.
Startup file:
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
SlidingExpiration = true,
Provider = new CookieAuthenticationProvider
{
OnResponseSignIn = context =>
{
context.Properties.AllowRefresh = true;
context.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddDays(1);
},
OnValidateIdentity = MyCookieValidateIdentity
},
ExpireTimeSpan = TimeSpan.FromDays(2)
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = appId,
//CookieManager=new SameSiteCookieManager(new SystemWebCookieManager()),
Authority = "https://login.microsoftonline.com/xxxxxx/v2.0",
Scope = $"openid email profile offline_access {graphScopes}",
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = (context) =>
{
context.ProtocolMessage.DomainHint = "xyz.com";
return Task.FromResult(0);
},
// SecurityTokenValidated = OnSecurityTokenValidated,
AuthenticationFailed = OnAuthenticationFailedAsync,
AuthorizationCodeReceived = OnAuthorizationCodeReceivedAsync
}
}
);
}
actionContext.RequestContext.Principal.Identity.IsAuthenticated is returning False
I am assuming it has to do something with the cookie. Can someone please help resolve this ?
Edit:
Debugged further and found:
Initially if the cookies for AppA are set as:
.AspNet.Cookies = A_abc123 ; ASP.NET_SessionId = A_def456
And for AppB .AspNet.Cookies = B_mno123 ; ASP.NET_SessionId = B_pqr456
Then after I click any link in AppA, the cookie's values are updated with AppB's cookies, i.e. .AspNet.Cookies = B_mno123 ; ASP.NET_SessionId = B_pqr456
.AspNet.Cookies ASP.NET_SessionId
AppA A_abc123 A_def456
AppB B_mno123 B_pqr456
AppA B_mno123 B_pqr456

One thing that you need to do is to configure the Data Protection API so that both services uses the same cookie protection key. Out of the box each service creates its own unique key, and a cookie from one service is not valid in a different service.
I also did a blog post about the data protection API here.
See
How to: Use Data Protection
Get started with the Data Protection APIs in ASP.NET Core

public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
//AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,// DefaultAuthenticationTypes.ApplicationCookie,
CookieName = ".AspNet.AppA.Cookies",
SlidingExpiration = true,
CookieManager = new SystemWebCookieManager(),
Provider = new CookieAuthenticationProvider
{
OnResponseSignIn = context =>
{
context.Properties.AllowRefresh = true;
context.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddDays(1);
},
},
ExpireTimeSpan = TimeSpan.FromDays(2)
});
//... code removed for brevity //
}
The Default Cookie Name set by the application was: .AspNet.Cookies
And when I modified the default cookie name, the issue got resolved. Each application was generating its own cookiename and hence the other application was not signing out the user.

Related

Redirect users to login page after session timeout using OpenIdConnectAuthentication

I'm using Azure B2C in an ASP.NET application with OpenIdConnectAuthentication. My sign-in policy has an absolute session length of 90 minutes.
After the session expires, I'd like the user to be automatically redirected to the login page, so they know they've been logged out. This doesn't seem to happen automatically. I was hoping this could be done automatically, perhaps using OWIN.
For now, as a workaround, I set a Refresh header in all HTTP responses that uses the remaining session life (calculated based on the iat claim and current time). But I was hoping this could just be handled automatically from the server. Is there any way to make the redirect occur automatically from the server-side once the session expires?
Below is my Startup class in Startup.Auth.cs:
public void ConfigureAuth(IAppBuilder app)
{
// ... other code ... //
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignInPolicyId));
}
private OpenIdConnectAuthenticationOptions CreateOptionsFromPolicy(string policy) {
var options = new OpenIdConnectAuthenticationOptions {
// For each policy, give OWIN the policy-specific metadata address, and
// set the authentication type to the id of the policy
MetadataAddress = String.Format(aadInstance, tenant, policy),
AuthenticationType = policy,
UseTokenLifetime = true,
// These are standard OpenID Connect parameters, with values pulled from
// Web.config
ClientId = clientId,
RedirectUri = redirectUri,
PostLogoutRedirectUri = logoutUri,
Notifications =
new OpenIdConnectAuthenticationNotifications {
AuthenticationFailed = AuthenticationFailed,
RedirectToIdentityProvider =
(context) => {
var value = context.OwinContext.Request.Path.Value;
if (value != "/default.aspx") {
context.OwinContext.Response.Redirect("/");
context.HandleResponse();
}
return Task.FromResult(0);
}
},
Scope = "openid",
ResponseType = "id_token",
TokenValidationParameters =
new TokenValidationParameters {
NameClaimType = "name",
},
};
return options;
}

Getting issue when I tried with integrating Microsoft Graph SDK with asp.net 4.8 web application

I am facing an issue with Microsoft GRAPH sdk.
IDX21323: RequireNonce is 'True'. 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.
public void ConfigureAuth(IAppBuilder app)
{ app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
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
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = appId,
Authority = "https://login.microsoftonline.com/common/v2.0",
Scope = $"openid email profile offline_access {graphScopes}",
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
TokenValidationParameters = new TokenValidationParameters
{
// For demo purposes only, see below
ValidateIssuer = false
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailedAsync,
AuthorizationCodeReceived = OnAuthorizationCodeReceivedAsync
}
}
);
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
}
private static Task OnAuthenticationFailedAsync(AuthenticationFailedNotification<OpenIdConnectMessage,
OpenIdConnectAuthenticationOptions> notification)
{
notification.HandleResponse();
string redirect = $"/ErrPage/Errormsg?message={notification.Exception.Message}";
if (notification.ProtocolMessage != null && !string.IsNullOrEmpty(notification.ProtocolMessage.ErrorDescription))
{
redirect += $"&debug={notification.ProtocolMessage.ErrorDescription}";
}
notification.Response.Redirect(redirect);
return Task.FromResult(0);
}

User claims Azure active Directory in asp.net mvc

I've just put my website on azure and I have some trouble with user claims.
I want to create special access from user who are from a special group in my azure active directory and when I was coding my website on localhost I make it this way :
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseKentorOwinCookieSaver();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieSecure = CookieSecureOption.Always,
ExpireTimeSpan = System.TimeSpan.FromMinutes(15),
SlidingExpiration = true,
CookieHttpOnly = true
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = context =>
{
if(context.AuthenticationTicket.Identity.Claims.Any(x => x.Type == "groups" && x.Value == "587642-sff4-f4c0-8085-ssdfe45d87ed"))
{
context.AuthenticationTicket.Identity.AddClaim(new Claim("roles","Admin"));
}
else
{
context.AuthenticationTicket.Identity.AddClaim(new Claim("roles", "User"));
}
return Task.FromResult(0);
}
}
});
}
If the user is in the group, he get the claim "Admin" and if he is not, he get "User".
On localhost, It was working perfectly but now I putted it on Azure, it doesn't go in the "Admin" claim anymore, only giving the User claim...
Can someone explain me where the problem come from?
Thanks in advance !
EDIT : I've made some test and it appears that the application is not finding groups or value(587642-sff4-f4c0-8085-ssdfe45d87ed) in context.AuthenticationTicket.Identity.Claims
Try to log out your claims and in particular what groups are present in the claims. Otherwise: Do you have access to more than one AAD tenant? Maybe you're on the wrong tenant when you go in the cloud.

How to avoid session expired cookie for Owin Federation authentication?

We are using Cookie and WsFederation middleware to authenticate against ADFS server. Everything is working fine except the generate cookie is always session expired.
When user close and reopen browser, they need to relogin.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType,
CookieDomain = "...some domain..."
});
app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions
{
MetadataAddress = ConfigurationManager.AppSettings[AdfsMetadataAddress],
Wtrealm = ConfigurationManager.AppSettings[AppWtRealm],
UseTokenLifetime = false
});
What are the things that we need to do in order to make cookie persisted in browser side?
Thanks very much in advance~
Add a CookieAuthenticationProvider where you can set the expiration
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType,
CookieDomain = "...some domain...",
Provider = new CookieAuthenticationProvider
{
OnResponseSignIn = context =>
{
context.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddDays(30);
context.Properties.IsPersistent = true;
}
}
});

How do I ignore the Identity Framework magic and just use the OWIN auth middleware to get the claims I seek?

The OWIN middleware stuff to integrate third-party logins to your ASP.NET app is very cool, but I can't seem to figure out how to tear it out from the new ID framework that replaces the crappy Membership API. I'm not interested in persisting the resulting claims and user info in that EF-based data persistence, I just want the claims info so I can apply it to my own user accounts in existing projects. I don't want to adopt the new ID framework just to take advantage of this stuff.
I've been browsing the code on CodePlex, but there's a whole lot of static magic. Can you offer any suggestions?
Use the following code to setup OWIN security middlewares:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Application",
AuthenticationMode = AuthenticationMode.Passive,
LoginPath = new PathString("/Login"),
LogoutPath = new PathString("/Logout"),
});
app.SetDefaultSignInAsAuthenticationType("External");
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "External",
AuthenticationMode = AuthenticationMode.Passive,
CookieName = CookieAuthenticationDefaults.CookiePrefix + "External",
ExpireTimeSpan = TimeSpan.FromMinutes(5),
});
app.UseGoogleAuthentication();
The code above sets up application cookie, external cookie and Google external login middlewares. External login middleware will convert external user login data as identity and set it to external cookie middleware. In your app, you need to get external cookie identity and convert it to external login data, then you can check it with your db user.
Here are some sample code.
Sign in with application cookie:
var authentication = System.Web.HttpContext.Current.GetOwinContext().Authentication;
var identity = new ClaimsIdentity("Application");
identity.AddClaim(new Claim(ClaimTypes.Name, "<user name>"));
authentication.AuthenticationResponseGrant = new AuthenticationResponseGrant(identity, new AuthenticationProperties() {
IsPersistent = false
});
Get application cookie identity:
var identity = System.Web.HttpContext.Current.User.Identity as ClaimsIdentity;
Get external cookie identity (Google):
var authentication = System.Web.HttpContext.Current.GetOwinContext().Authentication;
var result = await authentication.AuthenticateAsync("External");
var externalIdentity = result.Identity;
Extract external login data from identity:
public static ExternalLoginData FromIdentity(ClaimsIdentity identity)
{
if (identity == null)
{
return null;
}
Claim providerKeyClaim = identity.FindFirst(ClaimTypes.NameIdentifier);
if (providerKeyClaim == null || String.IsNullOrEmpty(providerKeyClaim.Issuer)
|| String.IsNullOrEmpty(providerKeyClaim.Value))
{
return null;
}
if (providerKeyClaim.Issuer == ClaimsIdentity.DefaultIssuer)
{
return null;
}
return new ExternalLoginData
{
LoginProvider = providerKeyClaim.Issuer,
ProviderKey = providerKeyClaim.Value,
UserName = identity.FindFirstValue(ClaimTypes.Name)
};
}

Resources