Web API Returns Wrong Values after being Published to IIS - entity-framework

Please help me. The API I created works fine when I launch it with Visual Studio but (the POST methods) has issues I deployed to IIS.
It returns wrong values and sometimes, null values. If i go back to test it in debug mode it works well.
THIS IS WHERE I'M CALLING THE API
try
{
string apiToken = "DEFAULTAPI";
EmployeeObject EmployeeObject = new EmployeeObject()
{
username = "John Doe"
password = "12345"
apiToken = apiToken
};
var emp = JsonConvert.SerializeObject(EmployeeObject);
string url = "http://localhost/PublishVersion/api/Company";
//the url variable holds the published version link
var response = client.PostAsync(url, new StringContent(emp, Encoding.UTF8, "application/json"));
response.Wait();
var result = response.Result;
if (result.IsSuccessStatusCode)
{
Uri employeeUrl = result.Headers.Location;
var statusMessage = result.Content.ReadAsStringAsync().Result;
if (statusMessage == "yes")
{
status = true;
}
else if (statusMessage == "no")
{
status = false;
}
}
return status;
}
AND THIS IS THE API
public string Post([FromBody] Employees employees)
{
string message = "no";
if (employees != null)
{
string emp = employees.username;
string password = employees.password
string apiToken = employees.apiToken
APIToken token = _dbContext.MyTable.Where(x => x.apitoken == apitoken).FirstOrDefault();
//APIToken is a table which has properties company and boss (both string)
if (token != null)
{
string company = token.company;
string boss = token.boss;
return message = "yes" + " " + company + "" + boss;
}
else
{
return message = "invalid token";
}
}
return message + employee.username;
}
The api returns "no John Doe" to the caller, which shouldn't be that way, since it displayed the username value which shows that the employee object is not null. Why doesn't it go into the block of code since it passed the null check? This issue only comes up when I deploy the API project to the IIS (version 10). Works fine in Visual Studio debug mode.
Also, the API and Winsform(where i'm calling it from) are on the same machine.
Thank you.

Related

Sending email using Microsoft graph with attachment. Microsoft code example unclear

HI I am trying to use Microsoft graph api to send messages.
Previously, I was sending messages/emails with the graph api without attachment. Now I need to attach 10 attachment each.
So I looked for examples and got to the Microsoft document and it shows the following code
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
var attachment = new FileAttachment
{
Name = "smile",
ContentBytes = Convert.FromBase64String("R0lGODdhEAYEAA7")
};
await graphClient.Me.Messages["{message-id}"].Attachments
.Request()
.AddAsync(attachment);
Link:
https://learn.microsoft.com/en-us/graph/api/message-post-attachments?view=graph-rest-1.0&tabs=csharp
My question is what it is showing is not clear I am not sure I would I use message-id. Also I dont see if the Message is created and how the attachment is created.
Can someone help please.
You may refer to this document to learn the example about how to send email with attachments. And the below is my test code, it worked for me, I used client credential flow to provide authentication..
using Azure.Identity;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Graph;
public class HomeController : Controller
{
private readonly IWebHostEnvironment _appEnvironment;
public HomeController(IWebHostEnvironment appEnvironment)
{
_appEnvironment = appEnvironment;
}
public async Task<string> sendMailAsync() {
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "your_tenant_name.onmicrosoft.com";
var clientId = "azure_ad_clientid";
var clientSecret = "client_secret";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var a = _appEnvironment.WebRootPath;//I have a file stored in my project
var file = a + "\\hellow.txt";
byte[] fileArray = System.IO.File.ReadAllBytes(#file);
//string base64string = Convert.ToBase64String(fileArray);
var message = new Message
{
Subject = "Meet for lunch?",
Body = new ItemBody
{
ContentType = BodyType.Text,
Content = "The new cafeteria is open."
},
ToRecipients = new List<Recipient>()
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = "xxx#outlook.com"
}
}
},
Attachments = new MessageAttachmentsCollectionPage()
{
new FileAttachment
{
Name = "attachment.txt",
ContentType = "text/plain",
ContentBytes = fileArray
}
}
};
await graphClient.Users["user_id"]
.SendMail(message, null)
.Request()
.PostAsync();
return "success";
}
}

Facebook login in asp.net core not working and return 500

I have been using facebook login for one of my asp.net core projects. However, it stopped working for Facebook login suddenly. I am getting HTTP 500 error.
The issue is, even in the debug more, asp.net core is not mentioning any error. It is just same 500 error code. Nothing else.
When I tried to set breakpoint in the first line of public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null) function, I realized that it is not even hitting that and failing even before that.
I am not sure how to debug this further. Am I missing anything here? Or is there any change from FB in the login side?
The return URL being hit is by Facebook is:
https://localhost:44300/signin-facebook?code=AQBxGGw7ZCoa9xtXc3CCsVGRD9TJLL428bZ_eJpUu4CtVu3K4UrfOZuYYdwFBXzGZ6GOGXpOi2Nme_jfbewB84otVZhKZfs4i7Dhi9Y3E_rloU9ouLeIvuOsm29jr7IDCtTj_HM7rKuKjj3zmc4yz5i_fniZ9ZhMfXtSus5KyKa4EFkZTsmKrz2ngMlGQalUAob_52GJNhvSIXDlmiNSrZLJV3m7Zbkf9eXETQkqhu2L1kgXPvWkMzVP8EN00GwRCYB3xT1kQMOimDANRKhziZjoVS5QZFUJTP0Faj47tE1xNfmAzb30iuwcaRORCOTMipUrnRvOO4nGRo8JuUNdPJaO&state=CfDJ8EHIO3qHMHFClr5BAt4EC1Wj7LyAs5Pg1XOqKo4uFiJM2Jr1rNyooxLIu2fbXr6Z3X5_kqbF_7WwFfvF3L3H4xgyooo-3Y9BV8Zh1S5wXlLJDAyCT5_LwkPJ1j8Zrwx4umQJp6NOl76GwRXpi1_BHlWGRxnh_naTL35iqeGovOa8oEDC0jOQ4trRe7YG3fV_ptjWk4yOnvJnsI81O-6wfyhdc3jm-LTP7ZO7-duf_lPZXZ8mL42XyLXDTIyOJ__S2yLYdvwItdDVntsM8Hwq94goXdU-RaH7ZkDA8iAzeCl3Ke0tWAdYBKy9vooJIXmE9Q#_=_
Based on this article, it should have state_token too in the URL. But that seems to be missing here. How can I figure out here what is the actual error?
I am using asp.net core RC2 release.
My callback function is:
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null)
{
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
return RedirectToAction(nameof(Login));
}
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
if (result.Succeeded)
{
_logger.LogInformation(5, "User logged in with {Name} provider.", info.LoginProvider);
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl });
}
if (result.IsLockedOut)
{
return View("Lockout");
}
else
{
// If the user does not have an account, then ask the user to create an account.
ViewData["ReturnUrl"] = returnUrl;
ViewData["LoginProvider"] = info.LoginProvider;
var email = info.ExternalPrincipal.FindFirstValue(ClaimTypes.Email);
if (email == null)
{
return View("Error");
}
/* Determine user from external login info */
var name = info.ExternalPrincipal.FindFirstValue(ClaimTypes.Name);
string firstName;
string lastName = "";
if (!string.IsNullOrWhiteSpace(name))
{
firstName = name.Split(' ').Length > 1? name.Split(new[] { ' ' }, 2)[0] : name;
lastName = name.Split(' ').Length > 1 ? name.Split(new[] { ' ' }, 2)[1] : "";
}
else
firstName = email.Split('#')[0];
var user = await _userManager.FindByEmailAsync(email);
if (user == null)
{
/* No user with same email ID. So, create a new user.*/
var newUser = new ApplicationUser
{
UserName = email,
Email = email,
FirstName = firstName,
LastName = lastName,
PasswordLastModifiedTime = DateTime.UtcNow,
UserSignUpDate = DateTime.UtcNow
};
var userCreationResult = await _userManager.CreateAsync(newUser);
if (userCreationResult.Succeeded)
{
userCreationResult = await _userManager.AddLoginAsync(newUser, info);
if (userCreationResult.Succeeded)
{
// Add user claims TODO:// Test if the claims are added successfully.
await _userManager.AddClaimAsync(newUser, new Claim("FirstName", newUser.FirstName));
await _userManager.AddClaimAsync(newUser, new Claim("LastName", newUser.LastName));
// Set user email to confirmed. This is more of work around
var code = await _userManager.GenerateEmailConfirmationTokenAsync(newUser);
userCreationResult = await _userManager.ConfirmEmailAsync(newUser, code);
if (userCreationResult.Succeeded)
{
//Create Subscription for user
var planService = new PlanServices();
var plan = planService.Find((int)SubscriptionType.Basic);
await _subscriptionService.CreateSubscription(newUser, plan, null);
await _signInManager.SignInAsync(newUser, isPersistent: false);
_logger.LogInformation(6, "User created an account using {Name} provider.",
info.LoginProvider);
await _emailSender.SendWelcomeEmailAsync(newUser.Email, newUser.FirstName);
return RedirectToLocal(returnUrl);
}
}
}
}
else
{
/* A user with email ID exists. Associate the account with that.*/
var loginAddResult = await _userManager.AddLoginAsync(user, info);
if (loginAddResult.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
}
}
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email, FirstName = firstName, LastName = lastName});
}
}
And ConfigureServices method is:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddScoped<ApplicationDbContext>();
services.AddIdentity<ApplicationUser, IdentityRole>(o =>
{
o.Password.RequireDigit = false;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
o.Password.RequireNonLetterOrDigit = false;
o.Password.RequiredLength = 8;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddCaching();
services.AddSession();
services.AddMvc();
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
services.AddTransient<ISubscriptionService, SubscriptionService>();
services.Configure<AuthMessageSenderOptions>(Configuration);
services.Configure<RecaptchaOptions>(Configuration);
__serviceProvider = services.BuildServiceProvider();
}
The thing is, it worked well for a long time and has stopped working now. Also, it is not even hitting ExternalLoginCallback, so I am not sure where to head for debugging it further.

ConfirmEmailAsync() method is not working

I am having issue in confirming new user email. the Confirm email link works for first 20 minutes , but after 50 minutes the link expires. I have set the token expiration time to 24 hours. Please help me in resolving this issue. I am stuck on it for last 2 days:(.My code is as follows:
I am setting the token lifetime in Create() method in ApplicationUserManager as following:
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
userManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = _settings.ConfirmationAndResetTokenExpirationTimeSpan
};
}
And then In AccountsController, the Create method for new user is geiven below. The SendEmailAsync method consist of email subject, email body, generated password and the callback uri.
[Authorize(Roles = Roles.Bam.Name.Admin)]
[HttpPost]
[Route(Routes.Accounts.Template.Create, Name = Routes.Accounts.Name.Create)]
public async Task<IHttpActionResult> Create(CreateUserBindingModel createUserBindingModel)
{
IHttpActionResult result;
var memberNameExists = UserManager.Users.Any(x => x.MemberName.ToLower() == createUserBindingModel.MemberName.ToLower());
if (!memberNameExists)
{
var applicationUser = new ApplicationUser
{
UserName = createUserBindingModel.Email,
Email = createUserBindingModel.Email,
FirstName = createUserBindingModel.FirstName,
LastName = createUserBindingModel.LastName,
Company = createUserBindingModel.Company,
Location = createUserBindingModel.Location,
PhoneNumber = createUserBindingModel.PhoneNumber,
MemberName = createUserBindingModel.MemberName,
LastLoginDate = SqlDateTime.MinValue.Value,
CreateDate = DateTime.Now,
CreatedBy = User.Identity.GetUserId(),
UpdateDate = DateTime.Now,
UpdatedBy = User.Identity.GetUserId(),
TwoFactorEnabled = createUserBindingModel.TwoFactorEnabled,
SecurityResetRequired = true,
PasswordExpirationDate = DateTime.Now.AddDays(Convert.ToDouble(ConfigurationManager.AppSettings["PasswordExpirationDays"]))
};
if (!string.IsNullOrEmpty(createUserBindingModel.AvatarBase64))
{
var avatarBytes = Convert.FromBase64String(createUserBindingModel.AvatarBase64);
var resizedAvatarBytes = ImageResizer.ResizeImage(avatarBytes, _avatarWidth, _avatarHeight);
applicationUser.UserAvatar = new ApplicationUserAvatar
{
Avatar = resizedAvatarBytes
};
}
var generatedPassword = PasswordGenerator.GenerateStrongPassword(10, 10);
var identityResult = await UserManager.CreateAsync(applicationUser, generatedPassword);
if (identityResult.Succeeded)
{
await UserManager.AddToRolesAsync(applicationUser.Id, createUserBindingModel.Roles.ToArray());
var token = await UserManager.GenerateEmailConfirmationTokenAsync(applicationUser.Id);
var callbackUri = string.Format("{0}?userId={1}&token={2}", createUserBindingModel.EmailConfirmationCallbackUri, applicationUser.Id, HttpUtility.UrlEncode(token));
await UserManager.SendEmailAsync(applicationUser.Id, Email.Confirmation.Subject, string.Format(Email.Confirmation.Body, string.Format("{0} {1}", applicationUser.FirstName, applicationUser.LastName), callbackUri, generatedPassword, _settings.AccessTokenExpirationTimeSpan.TotalHours));
var userUrl = new Uri(Url.Link(Routes.Accounts.Name.Get, new { id = applicationUser.Id }));
var roles = await UserManager.GetRolesAsync(applicationUser.Id);
var contract = _accountsMapper.ToContract(applicationUser, roles);
result = Created(userUrl, contract);
}
else
{
result = GetErrorResult(identityResult);
}
}
else
{
ModelState.AddModelError(string.Empty, "Member Name already exists!");
result = BadRequest(ModelState);
}
return result;
}
Once the email is generated the UI has following JS angular code which gets executed and the provide the userid and token to service.
Angular JS code:
angular.module('confirmEmailModule').factory('confirmEmailFactory', function ($http) {
var factory = {};
factory.confirmEmail = function(userId, token) {
var encodedToken = encodeURIComponent(token);
var uri = '/identity/api/accounts/confirmemail?userId=' + userId + '&token=' + token;
return $http.post(uri);
}
return factory;
});
and the Service is :
[AllowAnonymous]
[HttpPost]
[Route(Routes.Accounts.Template.ConfirmEmail, Name = Routes.Accounts.Name.ConfirmEmail)]
public async Task<IHttpActionResult> ConfirmEmail([FromUri] string userId, [FromUri] string token)
{
//var decodedToken = HttpUtility.UrlDecode(token);
var identityResult = await UserManager.ConfirmEmailAsync(userId, token);
var result = identityResult.Succeeded ? StatusCode(HttpStatusCode.NoContent) : GetErrorResult(identityResult);
return result;
}
Please advice.
I found the solution to this issue. I am posting it if somebody faced the same issue. In my case the services and web API were on different servers. Different machine keys caused this issue. So I generated the machine key for my Web application and posted the same machine key in web.config file of Identity service. After that it worked. For more information on generating machine key, following link is helpful.
http://gunaatita.com/Blog/How-to-Generate-Machine-Key-using-IIS/1058
This is what worked for me. Hope it helps out;
public async Task<IActionResult> ConfirmEmail(string userId, string token)
{
if (userId == null || token == null)
{
return RedirectToAction("employees", "home");
}
var user = await userManager.FindByIdAsync(userId);
if (user == null)
{
ViewBag.ErrorMessage = $"The User ID {userId} is invalid";
return View("NotFound");
}
var result = await userManager.ConfirmEmailAsync(user, Uri.EscapeDataString(token));
if (result != null)
{
user.EmailConfirmed = true;
await userManager.UpdateAsync(user);
return View();
}
}

Office365 Rest Structure - ChildFolders

I am trying to figure out a way of returning messages from a sub folder in outlook office 365 api.
Everything seems to point to this;
HttpResponseMessage response = await client.GetAsync("https://outlook.office365.com/api/v1.0/me/folders/inbox/childfolders/Odata/messages");
But I always get a bad request returned.
Here is my resource.
MSDN
Thanks Scott
The URL syntax is:
https://outlook.office365.com/api/v1.0/me/folders/<FOLDER ID>/Messages
So you need to get the ID for the folder you want to query. For example, if it's a subfolder of the Inbox, you could do a GET to:
https://outlook.office365.com/api/v1.0/me/folders/inbox/childfolders
And you'd get back something like:
{
"#odata.context": "https://outlook.office365.com/api/v1.0/$metadata#Me/Folders('inbox')/ChildFolders",
"value": [
{
"#odata.id": "https://outlook.office365.com/api/v1.0/Users('JasonJ#contoso.com')/Folders('AAMkADNhMjcxM2U5LWY2MmItNDRjYy05YzgwLWQwY2FmMTU1MjViOAAuAAAAAAC_IsPnAGUWR4fYhDeYtiNFAQCDgDrpyW-uTL4a3VuSIF6OAAAeY0W3AAA=')",
"Id": "AAMkADNhMjcxM2U5LWY2MmItNDRjYy05YzgwLWQwY2FmMTU1MjViOAAuAAAAAAC_IsPnAGUWR4fYhDeYtiNFAQCDgDrpyW-uTL4a3VuSIF6OAAAeY0W3AAA=",
"ParentFolderId": "AAMkADNhMjcxM2U5LWY2MmItNDRjYy05YzgwLWQwY2FmMTU1MjViOAAuAAAAAAC_IsPnAGUWR4fYhDeYtiNFAQCDgDrpyW-uTL4a3VuSIF6OAAAAAAEMAAA=",
"DisplayName": "New Subfolder",
"ChildFolderCount": 0
}
]
}
Then take the value of the Id field and plug it into the URL:
https://outlook.office365.com/api/v1.0/me/folders/AAMkADNhMjcxM2U5LWY2MmItNDRjYy05YzgwLWQwY2FmMTU1MjViOAAuAAAAAAC_IsPnAGUWR4fYhDeYtiNFAQCDgDrpyW-uTL4a3VuSIF6OAAAeY0W3AAA=/Messages
public void EnsureConnectionValid()
{
if (AuthenticationContext == null)
{
AuthenticationContext = new AuthenticationContext(authority);
AuthenticationResult = AuthenticationContext.AcquireToken(resource, clientId, new Uri(redirectUri), PromptBehavior.Auto);
}
}
public async Task<string> GetFolderId(string Path)
{
EnsureConnectionValid();
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AuthenticationResult.AccessToken);
var restCommand = "https://outlook.office365.com/api/v1.0/me/folders/Inbox/childfolders?$filter=DisplayName eq " + "'" + Path + "'";
HttpResponseMessage response = await client.GetAsync(restCommand);
response.EnsureSuccessStatusCode();
string jsonMessage;
using (var responseStream = await response.Content.ReadAsStreamAsync())
{
jsonMessage = new StreamReader(responseStream).ReadToEnd();
}
var folderObject = JObject.Parse(jsonMessage)["value"].ToObject<FoldersList[]>();
return folderObject.Select(r => r.Id).SingleOrDefault();
}

Handling concurrency exceptions when passing the objects ids and timestamps using jQuery

I have the following business scenario inside my Asp.net MVC 4 asset management system :-
Scenario 1) A user selects multiple servers , then he selects a Rack Tag ,and click on
assign . so the selected servers will be assigned to the new Rack.
Scenario 2) And i want to check for any concurrency exception , if for example the selected
servers have been modified by another user since they were retrieved .
so i have wrote the following jQuery which will send the object ids+timestamps to the action method:-
$('body').on("click", "#transferSelectedAssets", function () {
var boxData = [];
$("input[name='CheckBoxSelection']:checked").each(function () {
boxData.push($(this).val());
});
var URL = "#Url.Content("~/Server/TransferSelectedServers")";
$.ajax({
type: "POST",
url: URL,
data: { ids: boxData.join(","), rackTo: $("#rackIDTo").val()}
,
success: function (data) {
addserver(data); })});
and inside the action method i have the following code:-
public ActionResult TransferSelectedServers(string ids, int? rackTo)
{
if (ModelState.IsValid)
{
try
{
var serverIDs = ids.Split(',');
int i = 0;
foreach (var serverinfo in serverIDs)
{
var split = serverinfo.Split('~');
var name = split[0];
//System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
byte[] bytearray = Encoding.Default.GetBytes(split[1]);
i++;
var server = repository.FindServer_JTechnology(Int32.Parse(name));
if (server == null)
return Json(new { IsSuccess = false, reload = true, description = " Some Servers might have been deleted, Transferre process has been cancelled .", rackid = rackFrom }, JsonRequestBehavior.AllowGet);
server.RackID = rackTo;
server.timestamp = bytearray;
string ADusername = User.Identity.Name.Substring(User.Identity.Name.IndexOf("\\") + 1);
repository.InsertOrUpdateServer(server, ADusername, server.Technology.IT360ID.Value, server.IT360SiteID, new bool(), server.Technology);
}
repository.Save();
return Json(new { IsSuccess = true, description = i + " Server/s Transferred Successfully To Rack " + }, JsonRequestBehavior.AllowGet);
}
catch (DbUpdateConcurrencyException e)
{
return Json(new { IsSuccess = false, reload = true, description = "records has been modified by antoehr user" }, JsonRequestBehavior.AllowGet);
}
catch (Exception e)
{
return Json(new { IsSuccess = false, reload = true, description = " Server/s Can not Be Transferred to the Selected Rack " }, JsonRequestBehavior.AllowGet);
}
}
return RedirectToAction("Details", new { id = rackTo });
}
and the repository method looks as follow:-
public void InsertOrUpdateServer(TMSServer server, string username, long assetid, long? siteid = 0, bool isTDMHW = false, Technology t = null)
{
server.IT360SiteID = siteid.Value;
tms.Entry(server).State = EntityState.Modified;
var technology = tms.Technologies.Single(a => a.TechnologyID == server.TMSServerID);
technology.IsManaged = t.IsManaged;
tms.Entry(technology).State = EntityState.Modified;
InsertOrUpdateTechnologyAudit(auditinfo);
}
}
but currently if two users selects the same servers and assign them to tow different racks , no concurrency exception will be raised ?
Can anyone advice ? baring in mind that if two users edit single object then one of them will get an concurrent exception message. so my timestamp column is defined correctly.
Thanks