I have a mvc4 view form with some fields, let's say:
User name
Surname
Age
Code
below an example:
#{
var actionURL = "https://" + Request.Url.Host + Request.Url.PathAndQuery;
using (Html.BeginForm("AddUser", "Configure", FormMethod.Post), new { #action = actionURL })
{
...
#Html.TextBoxFor(model => model.UserViewModel.Name, null, new { id = "UserName" })
#Html.PasswordFor(model => model.UserViewModel.UserCode, new { id = "UserCode" })
...
<input id="submitUser" type="submit" value="Send" />
...
}
}
ConfigureController.cs:
[RequireHttps]
public ActionResult AddUser(UserViewModel model)
{
// Encrypt user code and store it in database
// Store in database the rest of user information as-is, without encrypting
}
Code field should be encrypted once user info is submited through internet but other fields are not necessary. Then once I recieve user info in the controller, I encrypt the code field and store it in the database. Other user information is stored in database as-is.
I want to encrypt the user code in server side not in client side.
My problem is that once submit button is clicked an error is thrown, a page is shown saying:
Ensure web address https://... is correct
Search the page with you search engine
Update/Refresh the page in a few minutes
Ensure TLS/SSL protocols are enabled by going to Tools->Internet Options->Advanced options->Configuration->Security
I have ensured that TLS and SSL protocols are enabled: SSL 3.0 and TLS 1.0 are enabled.
What's wrong? and how to send code encrypted to the mvc4 controller? I do not want to make changes in web.config.
I have tried other solutions like explained below, but nothing works for me:
building own action filter: ASP.NET MVC RequireHttps
using form action instead of html.beginform:
How to change POST to SSL URL in MVC 4
but nothing works...
Related
I'm using ASP.NET Identity and OpenIddict for a custom authorization server in MVC / Razor Pages. I'm supporting most OpenID flows with OpenIddict and user and admin areas with ASP.NET Identity.
The site is accessed directly from desktop browsers, mobile apps and other projects, especially the endpoints for starting a login and a authroization flow.
Everything is working as expected.
Now I would like to add partial multi-tenancy by only switching CSS based on the tenant. The rest of the app will remain untouched. I was going to start by modifying the _Styles.cshtml file by adding the following:
#using Microsoft.AspNetCore.Http
#inject IHttpContextAccessor accessor
#{
var ok = accessor.HttpContext.Request.Headers.TryGetValue("X-Tenant-Id", out var values);
var tenantId = values.FirstOrDefault();
}
Then depending on the tenant I will add the CSS file.
How, through the whole app, in views and in controller actions there redirects and links to other pages (eg. from login page to register page or forgot password). This is a problem because the custom header above is not persisted in further requests.
So I was thinking of added the header to the session state. Currently I'm not using sessions, but I would be doing something similar to what is described in this MS Article.
Any request that has the above header will update the session state. As this is an essential cookie but I still have to use cookie consent policy and data protection, is it possible to flag it as essential to avoid cookie consent?
Will the work or is there a better solution?
Is it possible to flag it as essential to avoid cookie consent
If you marked the session cookie as essential ,you would reveive a cookie name of .AspNetCore.Session
builder.Services.AddSession(options =>
{
options.Cookie.IsEssential = true;
})
and if you configured cookieConsent as below
builder.Services.Configure<CookiePolicyOptions>(op =>
{ op.CheckConsentNeeded = _ => true;
});
you won't receive the cookies which are not marked as essential
I tried as below:
public IActionResult Index()
{
HttpContext.Response.Cookies.Append("SomeKey", "SomeValue" ,new CookieOptions() { IsEssential=false});
HttpContext.Session.SetString("SomeKey", "SomeVal");
return View();
}
public IActionResult Privacy()
{
string? cookieval;
HttpContext.Request.Cookies.TryGetValue("SomeKey", out cookieval);
var sessionval=HttpContext.Session.GetString("SomeKey");
return View();
The Result:
I am working on an Identity Server implementation that makes use of ASP.net Core 2.1 and IdentityServer4 libraries. In the context of OAuth2 protocol, the identity server is implemented in a way to return an AuthorizationCode as soon as the customer provides his/her login credentials through a server provided web-form. The code is returned by the server to a redirectURI that the customer has provided earlier when he first made the login request (see below shown sample login request).
1) EXAMPLE SCENARIO
Sample Login Request:
http://exampleABC.com:5002/connect/authorize?client_id=XYZ&scope=myscope&response_type=code&redirect_uri=http://exampleXYZ.com
Once above like request is issued in browser, the browser opens up a client login page where user is asked to type in his customerid and password. Then, an SMS token page is opened where the customer enters the SMS he has received at his cell phone. The customer then enters the SMS in the browser. Finally, the server redirects the customer's browser to the page at the redirectURI where the browser shows the AuthorizationCode (i.e. code) in the address bar as shown in the following:
https://exampleXYZ.com/?code=89c0cbe1a2cb27c7cd8025b6cc17f6c7cf9bc0d4583c5a63&scope=myscope
Here, the code "89c0cbe1a2cb27c7cd8025b6cc17f6c7cf9bc0d4583c5a63" can be now used to request an AccessToken from the identity server.
2) PROBLEM STATEMENT
If I re-issue the above indicated sample login request in the same client browser (e.g. chrome), then the browser redirects the user to the redirectURI immediately without re-asking the client login credentials. This is a problem because I have to open up a fresh login screen every time the login request is made considering that there can be customers who have different login credentials. Therefore, I have provided a logout endpoint in my IdentityServer implementation where I intend to clean out the entire client cache and then sign out the customer as shown in the following code block. Here, I delete the cookies first and then create a new one with same key and past expiration date in order that the cookie is removed from the client browser cache in addition to the server cache. My aim here is to bring the login web form up-front in the browser at all times with no caching in place if a logout request is issued in order that the login form is displayed every time a new comer customer arrives.
public async Task<IActionResult> Logout()
{
var vm = await BuildLoggedOutView();
string url = Url.Action("Logout", new { logoutId = vm.LogoutId });
try
{
if (HttpContext.Request != null && HttpContext.Request.Cookies != null && HttpContext.Request.Cookies.Keys != null && HttpContext.Request.Cookies.Keys.Count > 0)
{
foreach (var key in _accessor.HttpContext.Request.Cookies.Keys)
{
//!!!! Cookie Removal !!!!!!
//Here I delete the cookie first and then recreate it
//with an expiry date having the day before.
_accessor.HttpContext.Response.Cookies.Delete(key);
_accessor.HttpContext.Response.Cookies.Append(
key,
string.Empty,
new CookieOptions()
{
Expires = DateTime.Now.AddDays(-1)
});
}
}
//!!!! Explicit sign out!!!!!!
await _accessor.HttpContext.SignOutAsync();
}
catch (NotSupportedException ex) // this is for the external providers that don't have signout
{
}
catch (InvalidOperationException ex) // this is for Windows/Negotiate
{
}
return View("Logged out", vm);
}
3) QUESTION:
Although I delete the cookies and override them on server side, the client browser keeps returning into the page at redirect uri where a new authorization code is shown without enforcing the customer to login (which is undesired). So, my question here is what am I missing in the above code block? It looks neither cookie override with old expiry date nor the explicit call to SignoutAsync method does not help to sign out the customer completely. Is there some more explicit strategy you might suggest in order to clean out everything both on client and server side completely once logged out?
I've had the same issue with cookies not being deleted properly. In my case it was because I defined a specific path for the authentication cookies. Let's say my path was /path, in that case you have to specify the same path within your delete:
foreach (var cookie in Request.Cookies.Keys)
{
Response.Cookies.Delete(cookie, new CookieOptions()
{
Path = "/path",
// I also added these options, just to be sure it matched my existing cookies
Expires = DateTimeOffset.Now,
Secure = true,
SameSite = SameSiteMode.None,
HttpOnly = true
});
}
Also, I do not know if the .Append() is necessary. By using .Delete() it already sent a set-cookie header in my case.
I created an ASP.Net Core 2.0 MVC using authentication providers as described here: https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/
On localhost (i.e. when run via Visual Studio 2017) all works well. However, after deploying to Azure I found that the login providers stopped working (despite my setting up appropriate callback URIs; e.g. for Google I have https://localhost:44357/signin-google but also https://mysite.azurewebsites.net/signin-google, https://example.com/signin-google, and https://www.example.com/signin-google (as well as having setup the example.com domain and its www subdomain in Azure and configured SSL covering these domains). For Twitter I've changed the setup to the www subdomain only (as only 1 callback URL's allowed), and for LinkedIn I only have the domain and subdomain (i.e. I had to remove localhost; as LinkedIn only allows callback URI's under a single domain). I've also configured those keys/values which had been in my secrets.json under the Azure App Service's Application Settings.
Symptoms
On first login (aka registration), the user clicks the relevant provider's button after which new user entry appears in the AspNetUsers and AspNetUserLogins tables, and the user is directed to the page where they can associate their email. However, they're not logged in at that point; just registered. Subsequent attempts take them back to the email registration form; only clicking the Register button then returns an error message stating that the email's already registered (which is correct); but the user's still not signed in to the site.
I have the same issue with all providers; though after proving this focussed most of my ongoing on Google, just to limit the number of changing variables.
The only significant change I've made from the example was to refactor code in Startup.cs so that each provider's encapsulated in it's own method; so ConfigureServices contains:
ConfigureServicesAuthFacebook(services);
ConfigureServicesAuthGoogle(services);
ConfigureServicesAuthTwitter(services);
ConfigureServicesAuthMicrosoft(services);
ConfigureServicesAuthLinkedIn(services);
... and those methods look like this:
#region Authentication Providers
public void ConfigureServicesAuthFacebook(IServiceCollection services)
{
services.AddAuthentication().AddFacebook(x =>
{
x.AppId = Configuration["Authentication:Facebook:Id"];
x.AppSecret = Configuration["Authentication:Facebook:Secret"];
});
}
public void ConfigureServicesAuthGoogle(IServiceCollection services)
{
services.AddAuthentication().AddGoogle(x =>
{
x.ClientId = Configuration["Authentication:Google:Id"];
x.ClientSecret = Configuration["Authentication:Google:Secret"];
});
}
public void ConfigureServicesAuthTwitter(IServiceCollection services)
{
services.AddAuthentication().AddTwitter(x =>
{
x.ConsumerKey = Configuration["Authentication:Twitter:Id"];
x.ConsumerSecret = Configuration["Authentication:Twitter:Secret"];
});
}
public void ConfigureServicesAuthMicrosoft(IServiceCollection services)
{
services.AddAuthentication().AddMicrosoftAccount(x =>
{
x.ClientId = Configuration["Authentication:Microsoft:Id"];
x.ClientSecret = Configuration["Authentication:Microsoft:Secret"];
});
}
public void ConfigureServicesAuthLinkedIn(IServiceCollection services)
{
services.AddAuthentication().AddOAuth("LinkedIn", x =>
{
x.ClientId = Configuration["Authentication:LinkedIn:Id"];
x.ClientSecret = Configuration["Authentication:LinkedIn:Secret"];
x.CallbackPath = new PathString("/signin-linkedin");
x.AuthorizationEndpoint = "https://www.linkedin.com/oauth/v2/authorization";
x.TokenEndpoint = "https://www.linkedin.com/oauth/v2/accessToken";
x.UserInformationEndpoint = "https://api.linkedin.com/v1/people/~:(id,formatted-name,email-address,picture-url)";
//x.Scope = { "r_basicprofile", "r_emailaddress" };
});
}
#endregion Authentication Providers
Question
How can I debug this issue given I cannot recreate the problem on localhost. Any hints on what the issue may be?
The way it works is your user first must assoicate their Google account with the user on your system. It sounds like this is working for you.
After that is done your code should preform some kind of ExternalLoginSignInAsync however this kind of depends on how you have your system set up.
Out of the box, where IsNotAllowed is true this means the email or phone number associated with the account which needs to be confirmed has not yet been confirmed. See ASN.NET Core 2.0 Facebook authentication ExternalLoginSignInAsync Fails (IsNotAllowed)
Take a look at the AccountController method ExternalLoginConfirmation and you'll see:
var user = new ApplicationUser(model.Email) { Email = model.Email };
Assuming you're happy for those signing up with existing logon providers, amend this to:
var user = new ApplicationUser(model.Email) { Email = model.Email, EmailConfirmed = true };
I'm currently working on a website with the ASP.net MVC framework for my own amusement and I decided
to stop working with a local database and to publish my web application on azure with it's associated database.
Now I have a strange issue with authentication. Identity.IsAuthenticated is always true.
At first I got the following error :
A claim of type 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier' or 'http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider' was not present on the provided ClaimsIdentity. To enable anti-forgery token support with claims-based authentication, please verify that the configured claims provider is providing both of these claims on the ClaimsIdentity instances it generates. If the configured claims provider instead uses a different claim type as a unique identifier, it can be configured by setting the static property AntiForgeryConfig.UniqueClaimTypeIdentifier.
Without understanding too much I found a solution which was to configure the AntiForgery token in the global.cs file
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;
At the point the error stopped showing but the user identified don't exist in the AspNetUsers table. Of course when I try to get info from the user in a section where authentification is needed the application crashes since no entry exists.
On my layout, I have a section of code to display some data if the user is authenticated
#if (User.Identity.IsAuthenticated)
{
using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm", #class = "navbar-right" }))
{
#Html.AntiForgeryToken()
<ul class="nav navbar-nav navbar-right">
<li>
#Html.ActionLink("Hello " + User.Identity.GetUserName() + "!", "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage" })
</li>
<li>Log off</li>
</ul>
}
}
The User.Identity.GetUserName() always returns live.com#[MyAzureAccount]#outlook.com and it's impossible for me to logoff.
This seems like an identity issue but as I didn't touch the code generated when I started the project and as it works in local I would've expected it to work.
The solution was simply to download the publishing files from azure instead of filling by hand the fields for publishing. I don't really know why it worked but it solved the problem.
I am building a ASP.Net MVC application that can work both in Web and JQuery mobile. So i am creating a seperate view for Web and JQuery mobile application. I have placed all my primary business logic services as a Web Api calls which are called by both the clients using the AngularJs which is working fine so far.
Now I was looking to introduce the security in to the application, and realized that Basic authentication is the quickest way to get going and when I looked around I found very nice posts that helped me build the same with minimal effort. Here are 3 links that I primarily used:
For the Client Side
HTTP Auth Interceptor Module : a nice way to look for 401 error and bring up the login page and after that proceed from where you left out.
Implementing basic HTTP authentication for HTTP requests in AngularJS : This is required to ensure that I am able reuse the user credentials with the subsequent requests. which is catched in the $http.
On the Server Side :
Basic Authentication with Asp.Net WebAPI
So far so good, all my WebApi calls are working as expected,
but the issue starts when I have to make calls to the MVC controllers,
if I try to [Authorize] the methods/controllers, it throws up the forms Authentication view again on MVC even though the API has already set the Authentication Header.
So I have 2 Questions:
Can We get the WebApi and MVC to share the same data in the header? in there a way in the AngularJS i can make MVC controller calls that can pass the same header information with authorization block that is set in the $http and decode it in the server side to generate my own Authentication and set the Custom.
In case the above is not possible, I was trying to make a call to a WebApi controller to redirect to a proper view which then loads the data using the bunch of WebApi calls so that user is not asked to enter the details again.
I have decorated it with the following attribute "[ActionName("MyWorkspace")] [HttpGet]"
public HttpResponseMessage GotoMyWorkspace(string data)
{
var redirectUrl = "/";
if (System.Threading.Thread.CurrentPrincipal.IsInRole("shipper"))
{
redirectUrl = "/shipper";
}
else if (System.Threading.Thread.CurrentPrincipal.IsInRole("transporter"))
{
redirectUrl = "/transporter";
}
var response = Request.CreateResponse(HttpStatusCode.MovedPermanently);
string fullyQualifiedUrl = redirectUrl;
response.Headers.Location = new Uri(fullyQualifiedUrl, UriKind.Relative);
return response;
}
and on my meny click i invoke a angular JS function
$scope.enterWorkspace = function(){
$http.get('/api/execute/Registration/MyWorkspace?data=""')
.then(
// success callback
function(response) {
console.log('redirect Route Received:', response);
},
// error callback
function(response) {
console.log('Error retrieving the Redirect path:',response);
}
);
}
i see in the chrome developer tool that it gets redirected and gets a 200 OK status but the view is not refreshed.
is there any way we can at least get this redirect to work in case its not possible to share the WebApi and MVC authentications.
EDIT
Followed Kaido's advice and found another blog that explained how to create a custom CustomBasicAuthorizeAttribute.
Now I am able to call the method on the Home controller below: decorated with '[HttpPost][CustomBasicAuthorize]'
public ActionResult MyWorkspace()
{
var redirectUrl = "/";
if (System.Threading.Thread.CurrentPrincipal.IsInRole("shipper"))
{
redirectUrl = "/shipper/";
}
else if(System.Threading.Thread.CurrentPrincipal.IsInRole("transporter"))
{
redirectUrl = "/transporter/";
}
return RedirectToLocal(redirectUrl);
}
Again, it works to an extent, i.e. to say, when the first call is made, it gets in to my method above that redirects, but when the redirected call comes back its missing the header again!
is there anything I can do to ensure the redirected call also gets the correct header set?
BTW now my menu click looks like below:
$scope.enterMyWorkspace = function(){
$http.post('/Home/MyWorkspace')
.then(
// success callback
function(response) {
console.log('redirect Route Received:', response);
},
// error callback
function(response) {
console.log('Error retrieving the Redirect path:',response);
}
);
}
this finally settles down to the following URL: http://127.0.0.1:81/Account/Login?ReturnUrl=%2fshipper%2f
Regards
Kiran
The [Authorize] attribute uses forms authentication, however it is easy to create your own
BasicAuthenticationAttribute as in your third link.
Then put [BasicAuthentication] on the MVC controllers instead of [Authorize].