I have developed an ASP.Net Core 6 web application which uses Cookie Authentication. Everything works as it should do i.e. I secure the Controllers using the [Authorize] and it challenges the authentication on a request, forces them over to the Login Page, they login, cookie is generated and all works fine. I have a "remember me" and I set this to last for 5 days and again this all works fine apart from the following situation:
On mobile devices (Android & iOS) in Chrome:
The user is logged in a accessing secure pages fine, they close the chrome app. They then re-open the chrome app, chrome opens and it then reloads my webpage as the tab has the site in there from when it was previously closed.
It would at this stage seem there is NO authentication cookie sent over on this reload of the browser. The user is taken straight to the Login Page.
BUT, they are logged in as if they click any link on the Login Page e.g. the Logo/Home page icon -> it checks the User.Identity.IsAuthenticated and redirects them to their dashboard which is behind a secure controller method.
Has anyone come across this before? It works perfectly on a desktop, just not on mobile devices and only seems to be with chrome when the chrome browser application is reloaded after being closed. Safari seems fine.
This is an issue for me as the users will be asked to create a shortcut link on their mobile homescreen to go to the website. If they close their browser and later click the shortcut link - it will reopen chrome and it will re-ask them to login but it shouldn't as they are already logged in.
Hope this makes sense.
Here are some of the configs:
Program.cs
builder.Services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.Lax;
});
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromDays(5);
options.LoginPath = "/Account/Login";
options.AccessDeniedPath = "/Account/RestrictedAccess";
options.LogoutPath = "/Account/Logout";
options.SlidingExpiration = false;
options.Cookie.SameSite = SameSiteMode.Lax;
options.Cookie.MaxAge = TimeSpan.FromDays(5);
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});
builder.Services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = "xxxxxxx";
options.SchemaName = "dbo";
options.TableName = "WebSessions";
options.DefaultSlidingExpiration = TimeSpan.FromDays(7);
});
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromDays(7);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = false;
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});
app.UseSession();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
The entire site works on https (ssl) and all content is served via https.
Related
I've got a Blazor webassembly app. It's part of an ASP.NET core website which has Cookie Authentication. You can login by visiting /login which is implemented as a .net core razor page.
services.AddRazorPages()
//.AddMvcOptions(options => options.Filters.Add(new AuthorizeFilter()))
.AddRazorPagesOptions(options =>
{
options.Conventions.AllowAnonymousToPage("/Login");
options.Conventions.AuthorizeFolder("/");
options.Conventions.AuthorizePage("/Logout");
options.Conventions.AllowAnonymousToPage("/Login");
});
services.AddAuthentication(options =>
{
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(options =>
{
options.LoginPath = "/Login";
// true by default
options.SlidingExpiration = true;
// 14 days by default
options.ExpireTimeSpan = TimeSpan.FromDays(20);
});
When the user isn't logged in, it's still possible to visit any route in the Blazor WebAssembly app. All the .net core Views are (correctly) inaccessible.
Where do I configure the WebAssembly part so that it's inaccessible to a non-authenticated user?
NOTE, I don't want to implement OIDC authentication in the WebAssembly part of the app (described in the Microsoft documentation) - or to add any of the login/logout logic to the WebAssembly part, just to make all the routes exposed by the WebAssembly app inaccessible for a non-authenticated user.
My .Net Core application is published to an elastic beanstalk load balanced environment and I'm using the Cognito hosted UI for authentication but after entering correct login details I get a 502 error.
snippet from startup.cs
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.ResponseType = Configuration["Authentication:Cognito:ResponseType"];
options.MetadataAddress = Configuration["Authentication:Cognito:MetadataAddress"];
options.ClientId = Configuration["Authentication:Cognito:ClientId"];
options.SaveTokens = true;
options.ClientSecret = Configuration["Authentication:Cognito:Secret"];
options.Scope.Add(Configuration["Authentication:Cognito:Scope"]);
options.Events = new OpenIdConnectEvents()
{
OnRedirectToIdentityProviderForSignOut = OnRedirectToIdentityProviderForSignOut,
OnRedirectToIdentityProvider = (async context =>
{
context.ProtocolMessage.RedirectUri = context.ProtocolMessage.RedirectUri.Replace("http:", "https:");
await Task.FromResult(0);
})
};
});
When I inspect the network activity in the browser I'm seeing this...
...which suggests that cognito is redirecting to /signin-oidc but there's no authorisation so it redirects back to cognito which then redirects back, and this repeats until it eventually throws the 502 error.
When I'm testing locally I'm able to login okay which makes me think it's maybe some kind of loadbalancing issue??
I kept getting a Bad Gateway 502 until I discovered that my next.config.js had async rewrites enabled for the source/destination URLs in my clumsy attempt to fix a CORS issue. Removing that fixed my problem to get next-auth working with cognito. Then I had to fix the underlying CORS problem with server side policy settings.
I am currently experimenting with external login providers like Google Authentication in my ASP.NET application.
As you can see on my Program.cs i'am running .NET6.
After the Google-Login was successfull, the ClaimsPrincipal has exactly one ClaimsIdentity (IsAuthenticated == true). Now I want to add my own 'Custom-Claim' to this identity. The addition works without any problems, but the next request is missing the custom claim (all other claims by Google are there).
Here is the part of my Program.cs where I add the authentication:
builder.Services.AddAuthentication(x =>
{
x.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
})
.AddCookie()
.AddGoogle(GoogleDefaults.AuthenticationScheme, o =>
{
o.ClientId = builder.Configuration["Authentication:Google:ClientId"];
o.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"];
o.ClaimActions.MapJsonKey("urn:google:picture","picture","url");
});
Here is the configuration of the middleware pipeline in Program.cs:
app.UseCookiePolicy();
app.UseAuthentication();
app.UseAuthorization();
app.Use((httpContext, func) =>
{
if (httpContext.Request.Path.StartsWithSegments("/api"))
httpContext.Request.Headers[HeaderNames.XRequestedWith] = "XMLHttpRequest";
return func();
});
app.UseRouting();
app.MapRazorPages();
app.MapControllers();
Endpoint for Google Login:
[HttpGet]
[Route("GoogleSignIn")]
public async Task GoogleSignIn()
{
await HttpContext.ChallengeAsync(GoogleDefaults.AuthenticationScheme,
new AuthenticationProperties {RedirectUri =Url.Action("GoogleResponse")});
}
In the "GoogleResponse" method i add the mentioned custom claim:
//this method gets called after the google login has finished
public async Task<IActionResult> GoogleResponse()
{
var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
var identity = result.Principal.Identities.FirstOrDefault();
var currentSidClaim = identity.FindFirst(x => x.Type == ClaimTypes.Sid);
if (currentSidClaim != null)
identity.RemoveClaim(currentSidClaim);
identity.AddClaim(new Claim(ClaimTypes.Sid, "Hi i am some claim, but i'll miss on the next request :("));
return Redirect("/");
}
ClaimsIdentity after adding the claim:
[https://i.stack.imgur.com/INdGy.png]
ClaimsIdentity on the next request (in the same controller btw):
[https://i.stack.imgur.com/oft3e.png]
Whether I access the identity via "User.Identity" or "HttpContext.User..." makes no difference.
Really hope somebody can clear things up for me.
I hope I don't have to implement a large / elaborate ASP.NET Identity solution to solve the problem. I would be happy with a lightweight authentication.
UPDATE:
After some further tests it looks like it is not necessarily related to the specific Google login. Even if I create a second identity at login, it is gone at the next request.
I guess the authentication cookie is not updated.
Thanks in advance!
Try to add claims after callback from the external website. As described here
I'm trying out some features of ASP.NET 5 and I'm struggling a bit with authentication. I've managed to use most of this sample app to connect to my Azure AD to log in, but I can't figure out how to restrict parts of my web app to authenticated users only. The article that accompanies the sample app I used states that
You can trigger the middleware to send an OpenID Connect sign-in
request by decorating a class or method with the [Authorize]
attribute, or by issuing a challenge
Since I'd like to avoid repeating the same challenge code everywhere, I opted for the attribute approach, but it doesn't work at all. All it seems to do is block access to unauthorized users, without redirecting to the login page the way the challenge does.
Since I intended the app I am building to be more private than public, I've also tried creating a global policy and opening up some select features using the AllowAnonymous attribute. This works, but again the unauthorized pages are simply shown as blank, instead of a challenge being issued.
This is the policy code I'm using currently, taken from here:
var policy = new AuthorizationPolicyBuilder()
//This is what makes it function like the basic [Authorize] attribute
.RequireAuthenticatedUser()
.Build();
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new AuthorizeFilter(policy));
});
Am I missing some setup to the authorization attribute or the policy that issues the challenge?
For posterity and most likely my future self as well:
I was missing the AutomaticAuthentication property in the OpenIdConnectOptions. The sample app was set up like this:
// Configure the OWIN Pipeline to use Cookie Authentication
app.UseCookieAuthentication(options =>
{
// By default, all middleware are passive/not automatic. Making cookie middleware automatic so that it acts on all the messages.
options.AutomaticAuthentication = true;
});
// Configure the OWIN Pipeline to use OpenId Connect Authentication
app.UseOpenIdConnectAuthentication(options =>
{
options.ClientId = Configuration.Get("AzureAd:ClientId");
options.Authority = String.Format(Configuration.Get("AzureAd:AadInstance"), Configuration.Get("AzureAd:Tenant"));
options.PostLogoutRedirectUri = Configuration.Get("AzureAd:PostLogoutRedirectUri");
options.Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
};
});
To get everything to work I had to make small adaptations to make it look like this:
app.UseCookieAuthentication(options => { options.AutomaticAuthentication = true; });
// Configure the OWIN Pipeline to use OpenId Connect Authentication
app.UseOpenIdConnectAuthentication(options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.ClientId = Configuration.Get("AzureAd:ClientId");
options.Authority = String.Format(Configuration.Get("AzureAd:AadInstance"), Configuration.Get("AzureAd:Tenant"));
options.PostLogoutRedirectUri = Configuration.Get("AzureAd:PostLogoutRedirectUri");
options.Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
};
options.AutomaticAuthentication = true;
});
I have created an application using c# SDK.
The canvas page http://mywebsite/ is working fine and I made to accomplish what the client wants, but when i try to access the application via https://apps.facebook.com/ApplicationName which creates an iframe to http://mywebsite/, the login gets stuck.
This is what appears when I hit connect:
That is the permissions window, the permissions appear, I hit ok, and nothing happens.
Code:
<div id="fb-root"></div>
<script type="text/javascript">
window.fbAsyncInit = function () {
// alert("aqui estoy");
FB.init({
appId: '397613493644598', // App ID
status: true, // check login status
cookie: true, // enable cookies to allow the server to access the session
xfbml: true // parse XFBML
});
FB.Event.subscribe('auth.authResponseChange', function (response) {
if (response.status === 'connected') {
// the user is logged in and has authenticated your
// app, and response.authResponse supplies
// the user's ID, a valid access token, a signed
// request, and the time the access token
// and signed request each expire
var uid = response.authResponse.userID;
var accessToken = response.authResponse.accessToken;
//alert("conectado");
// Do a post to the server to finish the logon
// This is a form post since we don't want to use AJAX
var form = document.createElement("form");
form.setAttribute("method", 'post');
form.setAttribute("action", '/Home/Login');
var field = document.createElement("input");
field.setAttribute("type", "hidden");
field.setAttribute("name", 'accessToken');
field.setAttribute("value", accessToken);
form.appendChild(field);
document.body.appendChild(form);
form.submit();
} else if (response.status === 'not_authorized') {
alert("Not Authorized");
// the user is logged in to Facebook,
// but has not authenticated your app
} else {
alert("Not logged In");
// the user isn't logged in to Facebook.
}
});
};
// Load the SDK Asynchronously
(function (d) {
var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];
if (d.getElementById(id)) { return; }
js = d.createElement('script'); js.id = id; js.async = true;
js.src = "//connect.facebook.net/en_US/all.js";
ref.parentNode.insertBefore(js, ref);
} (document));
</script>
<fb:login-button perms="user_birthday,email"> Connect</fb:login-button>
I may know what the problem is, but I need confirmation.
Does the problem is that facebook apps address is https and my app canva url is http?,
Found the answer, if you are testing stage, and this happens to you, you can do the following action.
Go to this link https://www.facebook.com/settings?tab=security
And disable secure browsing (this is just while you publish your app). Then your app should work fine.
Once you are ready to buy a certificate, and publish your application, Enable again this feature.
So the answer to my question:
Does the problem is that facebook apps address is https and my app canva url is http?,
is YES