Before download the file, user need to enter the password. So I want to show the message if password is correct and in the same time start the download the file to the user.
public async Task<IActionResult> OnPostAsync()
{
var getFileUpload = await _context.FileUpload.FirstAsync(c => c.Guid == Guid && c.ExpiredOn.HasValue);
if (!ModelState.IsValid)
{
var message = string.Join(" | ", ModelState.Values
.SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage));
return BadRequest(message);
}
if (DateTime.Today > getFileUpload.ExpiredOn.Value.AddDays(1))
{
Exception = "File already expired. Please ask administrator to share again";
return Page();
}
try
{
bool verified = BCrypt.Net.BCrypt.Verify(Password, getFileUpload.PasswordHash);
if (!verified)
{
Exception = "Password is wrong, please enter correct password";
return Page();
}
byte[] fileBytes = System.IO.File.ReadAllBytes(getFileUpload.Path);
var fileName = getFileUpload.FileName;
File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
Success = true;
return Page();
}
catch
{
Exception = "Failed to download the data";
return Page();
}
}
I can see the message, but file cannot download.
But when I change return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);, file able to download but cannot not alert the message.
Any idea how I can fix this?
You can verify the password with ajax first, and get the file after success. This is the pagemodel code.
public async Task<IActionResult> OnPostAsync()
{
//other code
if (true)
{
return new JsonResult("success");
}
else
{
return BadRequest();
}
}
public IActionResult OnGetFileAsync()
{
//get file from header
StringValues filename;
Request.Headers.TryGetValue("filename", out filename);
var stream = System.IO.File.OpenRead("file path");
string fileExt = Path.GetExtension("1.png");
var provider = new FileExtensionContentTypeProvider();
var memi = provider.Mappings[fileExt];
return File(stream, memi, Path.GetFileName("filename"));
}
Ajax in the page.
function verify() {
$.ajax({
url: '/?handler',
method: 'post',
headers: {
RequestVerificationToken: $('input:hidden[name="__RequestVerificationToken"]').val()
},
success: function (data,status) {
fetch("/?handler=file", {
//Write the filename to be obtained into the http header
headers: {
'filename': data
}
}).then(res => res.blob().then(blob => {
var a = document.createElement('a');
var url = window.URL.createObjectURL(blob);
var filename = res.headers.get('content-disposition').split(';')[1].split('=')[1]
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
}));
//set successful message
},
error: function () {
console.log('e')
//set the error message in the page
}
})
}
Related
I am working on a financial project which needs high security for signing user in.
In this project I didn't use default authentication when creating project and I have implement it manually. Now when user logs in his panel all functionalities run without error and SignInManager signs him in normally but when I want to redirect him to his dashboard with [Authorize] annotation again hi redirects to Login page.
What is the problem? It is about implementing identity manually?
here is my code for login:
public async Task<IActionResult> Login(LoginViewModel model)
{
try
{
string targetUrl = "";
if (ModelState.IsValid)
{
var user = await userManager.FindByEmailAsync(model.Email);
if (user == null)
{
_logger.Log(LogLevel.Warning, $"User ({model.Email}) tried to login with went wrong info");
return Json(new { result = "error", target = "login", message = "Wrong Email or Password. Please fill the form with valid information" });
}
var loginAttempt = await signInManager.CheckPasswordSignInAsync(user, model.Password, true);
if (loginAttempt.Succeeded)
{
if (user.TwoFactorEnabled)
{
TempData["loginInfo"] = JsonConvert.SerializeObject(user);
TempData.Keep("loginInfo");
targetUrl = "/Account/TwoStepVerification";
}
else
{
await signInManager.SignInAsync(user, false);
var login = new UserLogin
{
IpAddress = HttpContext.Connection.RemoteIpAddress.ToString(),
TimeStamp = DateTime.Now.Ticks,
UserId = user.Id
};
DbContext.UserLogins.Add(login);
await DbContext.SaveChangesAsync();
targetUrl = model.ReturnUrl ?? "/Account/Dashboard";
}
return Json(new { result = "success", target = "login", url = targetUrl, message = "You have successfully logged in. Wait some seconds..." });
}
return Json(new { result = "error", target = "login", message = "Wrong Email or Password." });
}
return Json(new { result = "error", target = "register", message = "Something went wrong. Bad input" });
}
catch (Exception exc)
{
_logger.Log(LogLevel.Critical, $"Failed Login : {exc.Message}");
return Json(new { result = "error", target = "register", message = "Something went wrong. Please try again after some minutes" });
}
}
here is two step verification :
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> TwoStepVerification(string code)
{
TempData.Keep("loginInfo");
if (code == null)
{
return Json(new { result = "error", target = "2faverified", message = "Please 2fa code" });
}
var user = JsonConvert.DeserializeObject<ApplicationUser>(TempData["loginInfo"].ToString());
GoogleAuthenticatorHelper google2FA = new(user.Id, user.Email);
if (google2FA.IsUserInputValid(code))
{
await signInManager.SignInAsync(user, false);
var login = new UserLogin
{
IpAddress = HttpContext.Connection.RemoteIpAddress.ToString(),
TimeStamp = DateTime.Now.Ticks,
UserId = user.Id
};
DbContext.UserLogins.Add(login);
await DbContext.SaveChangesAsync();
return Json(new { result = "success", url = "/Account/Dashboard", target = "2faverified", message = "You have successfully validate 2fa. Please wait..." });
}
return Json(new { result = "error", target = "2faverified", message = "Validation failed." });
}
here is ConfigureService in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddDefaultIdentity<ApplicationUser>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequireUppercase = true;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = true;
options.User.RequireUniqueEmail = true;
options.SignIn.RequireConfirmedEmail = false;
}).AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();
services.ConfigureApplicationCookie(options =>
{
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
options.LoginPath = new PathString("/Account/Login");
options.AccessDeniedPath = new PathString("/Account/AccessDenied");
options.SlidingExpiration = true;
});
services.Configure<DataProtectionTokenProviderOptions>(options =>
{
options.TokenLifespan = TimeSpan.FromDays(1);
});
services.AddControllersWithViews();
}
Sorry I had forgotten to add app.UseAuthentication(); code to my Configure function in Startup.cs
I want to upload image by multipart File request. With using this code When I pass two image files then it's working fine. But when I want to pass one image file and another is null then it's not working.
where is the problem? How can I solve this ?
Here is my code -
Future<Map<String, dynamic>> updateprofile(
UpdateProfileInfo updateProfileInfo,
File imageFile,
File signatureFile) async {
String url = "$baseAPIUrl/update-profile-info";
String _token = await SavedData().loadToken();
String authorization = "Bearer $_token";
final headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
"Authorization": authorization
};
var request = http.MultipartRequest("POST", Uri.parse(url));
request.headers.addAll(headers);
request.fields.addAll(updateProfileInfo.toJson());
request.files
.add(await http.MultipartFile.fromPath('image', imageFile.path));
request.files.add(
await http.MultipartFile.fromPath('signature', signatureFile.path));
print(" Update Profile Json ${updateProfileInfo.toJson()}");
print("Request Fields ${request.fields}");
http.StreamedResponse response = await request.send();
String respStr = await response.stream.bytesToString();
dynamic respJson;
try {
respJson = jsonDecode(respStr);
} on FormatException catch (e) {
print(e.toString());
}
print('API ${response.statusCode}\n $respJson');
bool isSuccess = response.statusCode == 200;
var data = json.decode(respStr);
return {
'isSuccess': isSuccess,
"message": isSuccess ? data["success"]["message"] : null,
"name": isSuccess ? data["success"]["name"] : null,
"classgroup": isSuccess ? data["success"]["classgroup"] : null,
"image": isSuccess ? data["success"]["image"] : null,
"error": isSuccess ? null : data['error']['message'],
};
}
Here is postman Screenshot
1.
2. POSTMAN Generated code for Dart - http
when one of your file is null, you should avoid adding it to the request body.
if(imageFile != null){
request.files
.add(await http.MultipartFile.fromPath('image', imageFile.path));
}
if(signatureFile != null){
request.files.add(
await http.MultipartFile.fromPath('signature', signatureFile.path));
}
its because signatureFile.path is going to cause an error here
using dio package should work
Future<bool> updateImage(var pickedFile) async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();` <br/>
var token = sharedPreferences.getString("token");
Dio dio = Dio();
final File file = File(pickedFile.path);
String fileName = file.path.split('/').last;
dio.options.headers["authorization"] = token;
FormData formData = FormData.fromMap({
"image": await MultipartFile.fromFile(file.path),
});
try {
var response = await dio.post(API.kBASE_URL, data: formData);
if (response.statusCode == 200) {
return true;
} else {
return false;
}
} catch (r) {
return false;
}
}
Today I create some Login Page with Session to make dynamic menu base on database, the web working fine but if I open specified page like localhost/Student/List before login I throw in to login page and after I'm login the page redirect me into localhost/Home/Index who is the default redirect after login page.
My question is how can i enter the specific address after i login? like using Identity.
I'm Sory I will update my Question.
I'm Using Ajax for my login page, if the result of ajax is true then i will redirect to Home/Index.
That's My Validate Function Login cshtml js
function Validate() {
if ($("#username").val() == null || $("#username").val() == "") {
$("#message").text("User Name Required!");
}
else if ($("#password").val() == null || $("#password").val() == "") {
$("#message").text("Password Required!");
}
else{
$.ajax({
type: "POST",
url: '#Url.Action("SignIn", "Account")',
data: {
username : $('#username').val(),
password : $('#password').val()
},
error: function (result) {
$("#message").text("There is a Problem, Try Again!");
},
success: function (result) {
console.log(result);
if (result.status == true) {
window.location.href = '#Url.Action("Index", "Home")';
}
else {
$("#message").text(result.message);
}
}
});
}
}
nah, you can see the window.location.href.
and then my sign in controller
public async Task<IActionResult> SignIn(LoginModel model)
{
var UserLogin = _dbContext.AspNetUsers.Where(a => a.UserName == model.username).FirstOrDefault();
if(UserLogin != null)
{
if(UserLogin.EmailConfirmed == true)
{
var result = await _signInManager.PasswordSignInAsync(model.username, model.password, lockoutOnFailure: false, isPersistent: false);
if (result.Succeeded)
{
HttpContext.Session.SetString("email", UserLogin.Email);
HttpContext.Session.SetString("username", UserLogin.UserName);
HttpContext.Session.SetString("id", UserLogin.Id);
HttpContext.Session.SetString("roleId", UserLogin.RolesId);
int roleId = Convert.ToInt32(Convert.ToDouble(HttpContext.Session.GetString("roleId")));
List<Menus> menus = _dbContext.LinkRolesMenu.Where(a => a.RolesId == roleId).Select(a => a.Menus).ToList();
DataSet ds = new DataSet();
ds = ToDataSet(menus);
DataTable table = ds.Tables[0];
DataRow[] parentMenus = table.Select("ParentId = 0");
var sb = new StringBuilder();
string menuString = GenerateUL(parentMenus, table, sb);
HttpContext.Session.SetString("menuString", menuString);
HttpContext.Session.SetString("menus", JsonConvert.SerializeObject(menus));
return Json(new { status = true, message = "Login Successfull!" });
}
else
{
return Json(new { status = false, message = "Invalid Password!" });
}
}
else
{
return Json(new { status = false, message = "Email Not Confirmed!" });
}
}
else
{
return Json(new { status = false, message = "Invalid UserName!" });
}
}
nah, that's the return return Json(new { status = true, message = "Login Successfull!" });
how can i return that into specific page
I open specified page like localhost/Student/List before login I throw in to login page
In my opinion, the only point is that you need to pass the current url (Student/List) as parameter to your login page.Refer to my demo using Custom Authorize Filter
1.Custom Authorize Filter: judge whether use has already logged in,if not, redirect to login page
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Routing;
public class MyAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext filterContext)
{
//your judgement to if user has logged in
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
//redirect to Account/Login
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new
{
controller = "Account",
action = "SignIn",
returnUrl = filterContext.HttpContext.Request.Path.ToUriComponent()
}));
}
}
}
2. StudentController:
[MyAuthorizeAttribute]
public class StudentController: Controller
3.AccountController
public IActionResult SignIn(string returnUrl = null)
{
// receive returnUrl and pass to View
ViewBag.ReturnUrl = returnUrl;
return View();
}
[HttpPost]
public async Task<IActionResult> SignIn(LoginModel model, string returnUrl = null)
{
//logic to login
return Json(new { status = true, message = "Login Successfull!" ,returnUrl = returnUrl });
}
4.SignIn.cshtml:redirect to returnUrl in success callback function
$.ajax({
type: "POST",
url: '#Url.Action("SignIn", "Account")',
data: {
username : $('#username').val(),
password : $('#password').val(),
returnUrl: '#ViewBag.ReturnUrl'
},
error: function (result) {
$("#message").text("There is a Problem, Try Again!");
},
success: function (result) {
console.log(result);
if (result.status == true) {
window.location.href = result.returnUrl;
}
else {
$("#message").text(result.message);
}
}
});
I'm fairly new to token based authentication and I have a problem of how to maintain login state after I login.
I want to create a SPA website for which I am using Knockoutjs for my front end and SammyJS for routing and changing the views.
After I login in and get the token I store it in localStorage and set the username into an observable which I am displaying.
My problem is that after I close the tab or browser and I go back to the site, the token is in the localStorage but I can't see the user logged in.
I want to maintain the login state until the token expires. My question is what should I do with the token from the localStorage when I enter the site in order to maintain the login state of that user?
Do I need to make something in the startup class or to check if that user exists in the DB?
Thanks in advance!
Here is my code:
StartupAuth.cs
[assembly: OwinStartup(typeof(EventHub.PL.WebUI.Startup))] namespace EventHub.PL.WebUI {
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get;private set; }
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
public const string TokenEndpointPath = "/api/token";
public static string PublicClientId { get; private set; }
// 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(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.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.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString(TokenEndpointPath),
Provider = new ApplicationOAuthProvider(PublicClientId),
//AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
//app.UseOAuthBearerTokens( OAuthOptions );
app.UseOAuthAuthorizationServer(OAuthOptions);
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
}
}
AccountController.cs
[HttpPost]
[AllowAnonymous]
[Route("Login")]
public async Task<IHttpActionResult> Login(LoginUser model)
{
var request = HttpContext.Current.Request;
var tokenServiceUrl = request.Url.GetLeftPart(UriPartial.Authority) + request.ApplicationPath + "/api/Token";
using (var client = new HttpClient())
{
var requestParams = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", model.Email),
new KeyValuePair<string, string>("password", model.Password)
};
var requestParamsFormUrlEncoded = new FormUrlEncodedContent(requestParams);
var tokenServiceResponse = await client.PostAsync(tokenServiceUrl, requestParamsFormUrlEncoded);
var responseString = await tokenServiceResponse.Content.ReadAsStringAsync();
var json = JsonConvert.DeserializeObject<TokenResponse>(responseString);
var responseCode = tokenServiceResponse.StatusCode;
if (responseCode == HttpStatusCode.OK)
{
RegisterUser user = userRepository.GetNameById(json.Id);
var data = new
{
status = "success",
json.access_token,
user.Lastname
};
return Json(data);
}
return Json(new { status = "failed" });
}
}
here is the KO part:
var LoginApp = function () {
var instance = this;
instance.mainViewModel = new MainViewModel();
instance.loginViewModel = new LoginViewModel();
instance.loginRepository = new LoginRepository();
instance.loginViewModel.signIn = function() {
$('.loader-header').show();
var postData = {
email: instance.loginViewModel.email(),
password: instance.loginViewModel.password
}
instance.loginRepository.SignIn(SignInSuccess, postData);
};
instance.SignInSuccess = function(response) {
if (response.status === 'success') {
instance.mainViewModel.username(response.Lastname);
instance.mainViewModel.isVisible(true);
var userData = {
token: response.access_token,
username: response.Lastname
};
localStorage.setItem('AuthorizationData', JSON.stringify(userData));
$('.loader-header').hide();
dialog.close();
} else {
$('.loader-header').hide();
}
};
instance.init = function () {
ko.applyBindings(instance.loginViewModel, document.getElementById("signin-form"));
ko.applyBindings(instance.mainViewModel, document.getElementById("main-wrapper"));
}
instance.init();
}
$(document).ready(function () {
var loginApp = LoginApp();
});
UPDATE
here is my routing also
var appRoot = root;
(function ($) {
var app = $.sammy('#page', function () {
this.get('#/home', function (context) {
document.title = 'Home - ' + title;
var url = getUrlFromHash(context.path);
loadView(url, new MainViewModel(), MainApp);
//context.load(url).swap();
});
this.get('#/about', function (context) {
var url = getUrlFromHash(context.path);
loadView(url, new AboutViewModel(), AboutApp);
});
this.get('#/manage', function (context) {
var url = getUrlFromHash(context.path);
loadView(url, new AboutViewModel(), AboutApp);
});
});
$(function () {
app.run('#/home');
});
})(jQuery);
function loadView(url, viewModel, callback) {
$.get(url, function (response) {
var $container = $('#page');
//var $view = $('#page').html(response);
$container.html(response);
callback();
});
}
function getUrlFromHash(hash) {
var url = hash.replace('#/', '');
if (url === appRoot)
url = 'home';
return url;
}
Right now all you're doing is storing the user's credentials in localStorage but not using them to perform authorization. One alternative is to use the Sammy.OAuth2 plugin (which you can find it here).
You can define a route to make the authentication like:
app.post("#/oauth/login", function(context) {
this.load('http://yourwebsite/login',
{
cache: false,
type: 'post',
data: {
email: $("input[name=email]").val(),
password: $("input[name=password]").val()
}
})
.then(function(content) {
if(content != false){
if(app.getAccessToken() == null){
app.setAccessToken(token());
}
}else{
app.trigger("oauth.denied");
return false;
}
});
});
In 'protected' routes you can check if the user is already logged in like this:
app.get("#/profile", function(context) {
if(app.getAccessToken() != null)
context.render('view/profile.template');
else
this.requireOAuth();
});
This examples will have to be modified to populate the token according to your scenario. Here's a complete tutorial on Sammy.Oath2.
I'm having some trouble with authenticating with ServiceStack using the BasicAuthProvider. All works well when I authenticate using the provider route 'auth/myauth' but when I go to one of my other service DTOS that use the [Authenticate] attribute e.g. /hello, I always get a 401 Unauthorized error even when I always supply the basic authentication details in the 'Authorization' header using beforeSend with jQuery.
Basically, I'm building an API for a mobile app that involves credential authentication on the first time(or if a supplied token isn't expired), then subsequently basic authentication of supplied token for other requests. I'm trying to authenticate every request, as described here. Also here. Here's my code:
Custom Provider
public class MyAuthProvider : BasicAuthProvider
{
public new static string Name = "MyAuth";
public new static string Realm = "/auth/myauth";
public MyAuthProvider()
{
this.Provider = Name;
this.AuthRealm = Realm;
}
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
var httpReq = authService.RequestContext.Get<IHttpRequest>();
var basicAuth = httpReq.GetBasicAuthUserAndPassword();
if (basicAuth == null)
throw HttpError.Unauthorized("Invalid BasicAuth credentials");
var us = basicAuth.Value.Key;
var ps = basicAuth.Value.Value;
if (ps == "password")
{
return true;
}
return false;
}
}
Service
public class HelloService : Service
{
//handle OPTIONS in preflight - http://joeriks.com/2013/01/12/cors-basicauth-on-servicestack-with-custom-authentication/
public object Options(Hello request) { return true; }
[Authenticate("MyAuth")]
public object Post(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}
[Authenticate("MyAuth")]
public object Get(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
Configure Method
public override void Configure(Container container)
{
Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[] {
new MyAuthProvider()
}));
//register any dependencies your services use, e.g:
container.Register<ICacheClient>(new MemoryCacheClient() { FlushOnDispose = false });
//set endpoint information
SetConfig(new EndpointHostConfig
{
GlobalResponseHeaders =
{
{"Access-Control-Allow-Origin","http://localhost"},
{"Access-Control-Allow-Methods","GET, POST, PUT, DELETE, OPTIONS"},
{"Access-Control-Allow-Headers", "Content-Type, Authorization, Accept, Origin" }
},
});
}
This works
function make_base_auth(user, password) {
var tok = user + ':' + password;
var hash = btoa(tok);
return "Basic " + hash;
}
////
$.ajax({
url: 'http://localhost:61750/auth/myauth?format=json',
type: 'POST',
beforeSend: function(xhr) {
xhr.setRequestHeader("Authorization", make_base_auth("id#email.com","password"));
}
}).done(function (data) {
if( console && console.log ) {
console.log("Sample of data:", data);
}
});
But this doesn't
$.ajax({
url: 'http://localhost:61750/hello?format=json',
data: { Name:"Foo" },
type: 'POST',
beforeSend: function(xhr) {
xhr.setRequestHeader("Authorization", make_base_auth("id#email","password"));
}
}).done(function (data) {
if( console && console.log ) {
console.log("Sample of data:", data);
}
});
Thanks for your help.
I had to create a custom authenticate attribute with guidance from this gist -> https://gist.github.com/joeriks/4518393
In the AuthenticateIfBasicAuth method, I set provider to use MyAuthProvider.Name
Then,
[CustomAuthenticate]
public object Post(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}