How to resolve Xamarin iOS SecKeyChain InteractionNotAllowed issue? - ios

In my Xamarin.iOS project I used SecRecord/SecKeyChain to store my token values and app version. From production log I found keychain related exceptions with status code 'InteractionNotAllowed' when try to write/read items in keychain. Apple documents states that to resolve InteractionNotAllowed error we need to change the default kSecAttrAccessible attribute value from ‘WhenUnlocked' to ‘Always’.
But in my existing code when I changed accessible attribute to 'Always' app log out because it failed to read token from keychain. It return’s 'Item not found' when read. But when I tried to save token again it returns 'Duplicate item'. So again I tried to remove same item but this time it again returns 'Item not found'. That’s really strange I can’t delete it and I can’t read it with same key.
Below is the code snippet -
private SecRecord CreateRecordForNewKeyValue(string accountName, string value)
{
return new SecRecord(SecKind.GenericPassword)
{
Service = App.AppName,
Account = accountName,
ValueData = NSData.FromString(value, NSStringEncoding.UTF8),
Accessible = SecAccessible.Always //This line of code is newly added.
};
}
private SecRecord ExistingRecordForKey(string accountName)
{
return new SecRecord(SecKind.GenericPassword)
{
Service = App.AppName,
Account = accountName,
Accessible = SecAccessible.Always //This line of code is newly added.
};
}
public void SetValueForKeyAndAccount(string value, string accountName, string key)
{
var record = ExistingRecordForKey(accountName);
try
{
if (string.IsNullOrEmpty(value))
{
if (!string.IsNullOrEmpty(GetValueFromAccountAndKey(accountName, key)))
RemoveRecord(record);
return;
}
// if the key already exists, remove it before set value
if (!string.IsNullOrEmpty(GetValueFromAccountAndKey(accountName, key)))
RemoveRecord(record);
}
catch (Exception e)
{
//Log exception here -("RemoveRecord Failed " + accountName, e,);
}
//Adding new record values to keychain
var result = SecKeyChain.Add(CreateRecordForNewKeyValue(accountName, value));
if (result != SecStatusCode.Success)
{
if (result == SecStatusCode.DuplicateItem)
{
try
{
//Log exception here -("Error adding record: {0} for Account-" + accountName, result), "Try Remove account");
RemoveRecord(record);
}
catch (Exception e)
{
//Log exception here -("RemoveRecord Failed after getting error SecStatusCode.DuplicateItem for Account-" + accountName, e);
}
}
else
throw new Exception(string.Format("Error adding record: {0} for Account-" + accountName, result));
}
}
public string GetValueFromAccountAndKey(string accountName, string key)
{
try
{
var record = ExistingRecordForKey(accountName);
SecStatusCode resultCode;
var match = SecKeyChain.QueryAsRecord(record, out resultCode);
if (resultCode == SecStatusCode.Success)
{
if (match.ValueData != null)
{
string valueData = NSString.FromData(match.ValueData, NSStringEncoding.UTF8);
if (string.IsNullOrEmpty(valueData))
return string.Empty;
return valueData;
}
else if (match.Generic != null)
{
string valueData = NSString.FromData(match.ValueData, NSStringEncoding.UTF8);
if (string.IsNullOrEmpty(valueData))
return string.Empty;
return valueData;
}
else
return string.Empty;
}
}
catch (Exception e)
{
// Exception logged here -("iOS Keychain Error for account-" + accountName, e);
}
return string.Empty;
}
Any help would be great! Thanks

The property Service is also an unique identification when we store or retrieve data using KeyChain. You didn't post your GetValueFromAccountAndKey() method, so we don't know what is the key used for? But in your case, you should use the same Service to retrieve value:
string GetValueFromAccountAndKey(string accoundName, string service)
{
var securityRecord = new SecRecord(SecKind.GenericPassword)
{
Service = service,
Account = accoundName
};
SecStatusCode status;
NSData resultData = SecKeyChain.QueryAsData(securityRecord, false, out status);
var result = resultData != null ? new NSString(resultData, NSStringEncoding.UTF8) : "Not found";
return result;
}
Since you just make a hard code in your CreateRecordForNewKeyValue()( the Service has been written as a constant ), if you want to retrieve your value you should also set the Service as App.AppName in the method GetValueFromAccountAndKey().
It return’s 'Item not found' when read. But when I tried to save token
again it returns 'Duplicate item'.
This is because when we use the same Account but different Service to retrieve data, KeyChain can't find the corresponding SecRecord. This made you thought the SecRecord didn't exist, then use the same Account to store value. The Duplicate item result throws out. For a SecRecord, the Account and Service must both be unique.

Related

Model value not being set in Xamarin in iOS Only

I am working on a Xamarin App where I am facing an issue.
Case 1:
Mode: Debug
Error: System.NullReferenceException: 'Object reference not set to an instance of an object'
Explanation:
No value is being set and passed to ViewModel, Controller.
Even I tried setting static values to variables and parameters.
LoginModel logtest = new LoginModel();
logtest.Username = "Test#user.com"; //uName;
logtest.Password = "2342534"; //pWord;
Tried solution:
In the beginning, I tried many different solutions nothing worked.
Then I create a new project and moved all the code to the new project, then it started working.
Case 2:
Mode: release
Now the same issue I am getting in release mode.
No value is being set and passed to ViewModels and Controller.
I have already tried moving code to a new project.
How can I resolve this? I am not sure is this a Visual studio issue or Xamarin issue or Apple.
I have tried updating Visual Studio enterprise 2019 and Xcode on Mac.
Deleted and recreated Provisioning Profiles and Signing Identities.
Code From LoginPage.xaml.cs
private async void LoginButtonClicked(object sender, EventArgs e)
{
await ((LoginViewModel)BindingContext).Login();
}
protected async override void OnAppearing()
{
//App.Current.MainPage = new Navigation(new DashboardPage());
//load the login page
Device.BeginInvokeOnMainThread(() =>
{
try
{
((LoginViewModel)BindingContext).IsLoading = false;
((LoginViewModel)BindingContext).RememberMe = DeviceStorage.RememberMe;
if (DeviceStorage.RememberMe == true)
{
((LoginViewModel)BindingContext).UserName = "Test#user.ca"; //Trying set static values here
((LoginViewModel)BindingContext).Password = "password"; //Trying set static values here
}
//RememberMeToggle.Toggled += switcher_Toggled;
((LoginViewModel)BindingContext).Load();
}
catch (Exception ex)
{
//logger.Error(App.LogPrefix() + "Error opening Navigation Page: " + ex.Message);
Console.WriteLine(App.LogPrefix() + "Error opening Load(): " + ex.Message);
}
});
}
Code from LoginViewModel.cs
public async Task Login()
{
IsLoading = true;
await TokenController.GetAuthorizationToken(UserName, Password);
}
Code from TokenController.cs
public static async Task GetAuthorizationToken(string uName, string pWord)
{
bool tokenReturned = false;
string tokenGetResponse = string.Empty;
// Have tried setting them static values as well
LoginModel logtest = new LoginModel();
logtest.Username = uName;
logtest.Password = pWord;
logtest.AppType = Constants.AppDetails.APP_CODE;
logtest.SystemCode = Constants.AppDetails.SYSTEM_CODE;
logtest.Push.PushSystem = PushAddressModel.PushSystemCode.FireBase;
logtest.Push.Address = ((App)App.Current).PushNotificationToken;
logtest.Push.AppCode = Constants.AppDetails.APP_CODE;
string uri = URI.message_Chat_Endpoint;
(tokenReturned, tokenGetResponse) = await ApiFunctions.Post(logtest, URI.token_Endpoint, false);
if (tokenReturned)
{
try
{
//deserialize the return object
TokenModel token = JsonConvert.DeserializeObject<TokenModel>(tokenGetResponse);
((App)App.Current).token = token;
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
//logger.Error(App.LogPrefix() + "Error: " + ex.Message);
}
Console.WriteLine("Profile Token loaded: " + ((App)App.Current).token.access_token);
}
else
{
Console.WriteLine("Error: Error loading profile token ");
//logger.Error(App.LogPrefix() + "Error loading profile token");
}
}

Large File upload to ASP.NET Core 3.0 Web API fails due to Request Body to Large

I have an ASP.NET Core 3.0 Web API endpoint that I have set up to allow me to post large audio files. I have followed the following directions from MS docs to set up the endpoint.
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.0#kestrel-maximum-request-body-size
When an audio file is uploaded to the endpoint, it is streamed to an Azure Blob Storage container.
My code works as expected locally.
When I push it to my production server in Azure App Service on Linux, the code does not work and errors with
Unhandled exception in request pipeline: System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Request body too large.
Per advice from the above article, I have configured incrementally updated Kesterl with the following:
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseKestrel((ctx, options) =>
{
var config = ctx.Configuration;
options.Limits.MaxRequestBodySize = 6000000000;
options.Limits.MinRequestBodyDataRate =
new MinDataRate(bytesPerSecond: 100,
gracePeriod: TimeSpan.FromSeconds(10));
options.Limits.MinResponseDataRate =
new MinDataRate(bytesPerSecond: 100,
gracePeriod: TimeSpan.FromSeconds(10));
options.Limits.RequestHeadersTimeout =
TimeSpan.FromMinutes(2);
}).UseStartup<Startup>();
Also configured FormOptions to accept files up to 6000000000
services.Configure<FormOptions>(options =>
{
options.MultipartBodyLengthLimit = 6000000000;
});
And also set up the API controller with the following attributes, per advice from the article
[HttpPost("audio", Name="UploadAudio")]
[DisableFormValueModelBinding]
[GenerateAntiforgeryTokenCookie]
[RequestSizeLimit(6000000000)]
[RequestFormLimits(MultipartBodyLengthLimit = 6000000000)]
Finally, here is the action itself. This giant block of code is not indicative of how I want the code to be written but I have merged it into one method as part of the debugging exercise.
public async Task<IActionResult> Audio()
{
if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
{
throw new ArgumentException("The media file could not be processed.");
}
string mediaId = string.Empty;
string instructorId = string.Empty;
try
{
// process file first
KeyValueAccumulator formAccumulator = new KeyValueAccumulator();
var streamedFileContent = new byte[0];
var boundary = MultipartRequestHelper.GetBoundary(
MediaTypeHeaderValue.Parse(Request.ContentType),
_defaultFormOptions.MultipartBoundaryLengthLimit
);
var reader = new MultipartReader(boundary, Request.Body);
var section = await reader.ReadNextSectionAsync();
while (section != null)
{
var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(
section.ContentDisposition, out var contentDisposition);
if (hasContentDispositionHeader)
{
if (MultipartRequestHelper
.HasFileContentDisposition(contentDisposition))
{
streamedFileContent =
await FileHelpers.ProcessStreamedFile(section, contentDisposition,
_permittedExtensions, _fileSizeLimit);
}
else if (MultipartRequestHelper
.HasFormDataContentDisposition(contentDisposition))
{
var key = HeaderUtilities.RemoveQuotes(contentDisposition.Name).Value;
var encoding = FileHelpers.GetEncoding(section);
if (encoding == null)
{
return BadRequest($"The request could not be processed: Bad Encoding");
}
using (var streamReader = new StreamReader(
section.Body,
encoding,
detectEncodingFromByteOrderMarks: true,
bufferSize: 1024,
leaveOpen: true))
{
// The value length limit is enforced by
// MultipartBodyLengthLimit
var value = await streamReader.ReadToEndAsync();
if (string.Equals(value, "undefined",
StringComparison.OrdinalIgnoreCase))
{
value = string.Empty;
}
formAccumulator.Append(key, value);
if (formAccumulator.ValueCount >
_defaultFormOptions.ValueCountLimit)
{
return BadRequest($"The request could not be processed: Key Count limit exceeded.");
}
}
}
}
// Drain any remaining section body that hasn't been consumed and
// read the headers for the next section.
section = await reader.ReadNextSectionAsync();
}
var form = formAccumulator;
var file = streamedFileContent;
var results = form.GetResults();
instructorId = results["instructorId"];
string title = results["title"];
string firstName = results["firstName"];
string lastName = results["lastName"];
string durationInMinutes = results["durationInMinutes"];
//mediaId = await AddInstructorAudioMedia(instructorId, firstName, lastName, title, Convert.ToInt32(duration), DateTime.UtcNow, DateTime.UtcNow, file);
string fileExtension = "m4a";
// Generate Container Name - InstructorSpecific
string containerName = $"{firstName[0].ToString().ToLower()}{lastName.ToLower()}-{instructorId}";
string contentType = "audio/mp4";
FileType fileType = FileType.audio;
string authorName = $"{firstName} {lastName}";
string authorShortName = $"{firstName[0]}{lastName}";
string description = $"{authorShortName} - {title}";
long duration = (Convert.ToInt32(durationInMinutes) * 60000);
// Generate new filename
string fileName = $"{firstName[0].ToString().ToLower()}{lastName.ToLower()}-{Guid.NewGuid()}";
DateTime recordingDate = DateTime.UtcNow;
DateTime uploadDate = DateTime.UtcNow;
long blobSize = long.MinValue;
try
{
// Update file properties in storage
Dictionary<string, string> fileProperties = new Dictionary<string, string>();
fileProperties.Add("ContentType", contentType);
// update file metadata in storage
Dictionary<string, string> metadata = new Dictionary<string, string>();
metadata.Add("author", authorShortName);
metadata.Add("tite", title);
metadata.Add("description", description);
metadata.Add("duration", duration.ToString());
metadata.Add("recordingDate", recordingDate.ToString());
metadata.Add("uploadDate", uploadDate.ToString());
var fileNameWExt = $"{fileName}.{fileExtension}";
var blobContainer = await _cloudStorageService.CreateBlob(containerName, fileNameWExt, "audio");
try
{
MemoryStream fileContent = new MemoryStream(streamedFileContent);
fileContent.Position = 0;
using (fileContent)
{
await blobContainer.UploadFromStreamAsync(fileContent);
}
}
catch (StorageException e)
{
if (e.RequestInformation.HttpStatusCode == 403)
{
return BadRequest(e.Message);
}
else
{
return BadRequest(e.Message);
}
}
try
{
foreach (var key in metadata.Keys.ToList())
{
blobContainer.Metadata.Add(key, metadata[key]);
}
await blobContainer.SetMetadataAsync();
}
catch (StorageException e)
{
return BadRequest(e.Message);
}
blobSize = await StorageUtils.GetBlobSize(blobContainer);
}
catch (StorageException e)
{
return BadRequest(e.Message);
}
Media media = Media.Create(string.Empty, instructorId, authorName, fileName, fileType, fileExtension, recordingDate, uploadDate, ContentDetails.Create(title, description, duration, blobSize, 0, new List<string>()), StateDetails.Create(StatusType.STAGED, DateTime.MinValue, DateTime.UtcNow, DateTime.MaxValue), Manifest.Create(new Dictionary<string, string>()));
// upload to MongoDB
if (media != null)
{
var mapper = new Mapper(_mapperConfiguration);
var dao = mapper.Map<ContentDAO>(media);
try
{
await _db.Content.InsertOneAsync(dao);
}
catch (Exception)
{
mediaId = string.Empty;
}
mediaId = dao.Id.ToString();
}
else
{
// metadata wasn't stored, remove blob
await _cloudStorageService.DeleteBlob(containerName, fileName, "audio");
return BadRequest($"An issue occurred during media upload: rolling back storage change");
}
if (string.IsNullOrEmpty(mediaId))
{
return BadRequest($"Could not add instructor media");
}
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
var result = new { MediaId = mediaId, InstructorId = instructorId };
return Ok(result);
}
I reiterate, this all works great locally. I do not run it in IISExpress, I run it as a console app.
I submit large audio files via my SPA app and Postman and it works perfectly.
I am deploying this code to an Azure App Service on Linux (as a Basic B1).
Since the code works in my local development environment, I am at a loss of what my next steps are. I have refactored this code a few times but I suspect that it's environment related.
I cannot find anywhere that mentions that the level of App Service Plan is the culprit so before I go out spending more money I wanted to see if anyone here had encountered this challenge and could provide advice.
UPDATE: I attempted upgrading to a Production App Service Plan to see if there was an undocumented gate for incoming traffic. Upgrading didn't work either.
Thanks in advance.
-A
Currently, as of 11/2019, there is a limitation with the Azure App Service for Linux. It's CORS functionality is enabled by default and cannot be disabled AND it has a file size limitation that doesn't appear to get overridden by any of the published Kestrel configurations. The solution is to move the Web API app to a Azure App Service for Windows and it works as expected.
I am sure there is some way to get around it if you know the magic combination of configurations, server settings, and CLI commands but I need to move on with development.

How to get SAML token from Application Pool Identity (for the configured user)?

Is there any way to get SAML token for the Application Pool Identity User (configured user)?
when we configure application pool dentity stores config entries (user name & password) in applicationHost.config under %systemroot%\System32\Inetsrv\config path.
When application starts, it picks the user name and encrypted password for authentication. After successful authentication, will it follow token based authentication for subsequent calls or will always follows basic authentication ?
If it token based then how can i get the SAML token for application pool identity user, after the first response?
if any links please let me know.
Thanks in advance.
To Get SAML Assertion for Application pool Identity Or Logged on user :
string rpLoginUrl = string.Format(SapConfiguration.AdfsSignInUrl, SapConfiguration.AdfsInstance, HttpUtility.UrlEncode(GetSapTokenServiceUrl));
string htmlContent;
try
{
do
{
var result = await Client.GetAsync(rpLoginUrl);
htmlContent = await result.Content.ReadAsStringAsync();
IEnumerable<string> values;
if (result.Headers.TryGetValues("location", out values))
{
foreach (string s in values)
{
if (s.StartsWith("/"))
{
rpLoginUrl = rpLoginUrl.Substring(0, rpLoginUrl.IndexOf("/adfs/ls", StringComparison.Ordinal)) + s;
}
else
{
rpLoginUrl = s;
}
}
}
else
{
rpLoginUrl = "";
}
} while (!string.IsNullOrEmpty(rpLoginUrl));
}
catch (Exception exp)
{
var additionalInfo = $" additionalInfo : [rpLoginUrl: {rpLoginUrl}]";
throw new SecurityException($"SapAuthorization.GetSamlResponseForProcessIdentityAsync is failed, {additionalInfo}", exp);
}
var reg = new Regex("SAMLResponse\\W+value\\=\\\"([^\\\"]+)\\\"");
var matches = reg.Matches(htmlContent);
string lastMatch = null;
foreach (Match m in matches)
{
lastMatch = m.Groups[1].Value;
}
return lastMatch;
Ans 1: By Using Adal flow to get Jwt token for Logged on User,
if (!AdfsConfiguration.IsInitialized) throw new SecurityException(Constants.AdfsConfigurationInitilizationExceptionMessage);
if (string.IsNullOrEmpty(AdfsConfiguration.AdfsAuthorityUrl)) throw new SecurityException(Constants.AdfsConfigurationAdfsAuthorityUrlInitilizationExceptionMessage);
try
{
var authenticationContext = new AuthenticationContext(string.Format(AdfsConfiguration.AdfsAuthorityUrl, AdfsConfiguration.AdfsInstance, AdfsConfiguration.Resource), false);
var asyncRequest = authenticationContext.AcquireTokenAsync(AdfsConfiguration.Resource, AdfsConfiguration.ClientId, new Uri(AdfsConfiguration.RedirectUri), new PlatformParameters(PromptBehavior.Auto));
var accessToken = asyncRequest.Result.AccessToken;
return accessToken;
}
catch (Exception exp)
{
var additionalInfo = $" additionalInfo : [authenticationContext : {string.Format(AdfsConfiguration.AdfsAuthorityUrl, AdfsConfiguration.AdfsInstance, AdfsConfiguration.Resource)}]";
throw new SecurityException($"AdfsAuthorization.GetAdfsOAuthJwtAccessTokenForWinAppUserUsingAdal is failed, {additionalInfo}", exp);
}
Ans 2: By Auth code flow to get Jwt token for logged on user or Application pool identity user.
step 1 : Get Auth code from Adfs server
var authUrl = string.Format(AdfsConfiguration.AdfsAuthUrl, AdfsConfiguration.AdfsInstance, AdfsConfiguration.ClientId, AdfsConfiguration.Resource, AdfsConfiguration.UrlEncodedRedirectUri);
var authCode = "";
try
{
do
{
var result = await Client.GetAsync(authUrl);
await result.Content.ReadAsStringAsync();
IEnumerable<string> values;
if (result.Headers.TryGetValues("location", out values))
{
foreach (string s in values)
{
if (s.Contains("code="))
{
authUrl = "";
authCode = s.Substring(s.IndexOf("code=", StringComparison.Ordinal) + 5);
}
else
{
authUrl = s;
}
}
}
else
{
authUrl = "";
}
} while (!string.IsNullOrEmpty(authUrl));
return authCode;
}
catch (Exception exp)
{
var additionalInfo = $"additionalInfo : [authUrl: {authUrl}]";
throw new SecurityException($"AdfsAuthorization.GetAuthCodeForWinAppUserAsync is failed, {additionalInfo}", exp);
}
Step 2 : Pass Auth code to get jwt token from Adfs server
if (!AdfsConfiguration.IsInitialized) throw new SecurityException(Constants.AdfsConfigurationInitilizationExceptionMessage);
var client = new WebClient();
try
{
if (AdfsConfiguration.UseProxy == "Y")
{
var proxyObject = new WebProxy("Proxy", 80) { Credentials = CredentialCache.DefaultNetworkCredentials };
client.Proxy = proxyObject;
}
//Uri address = new Uri(String.Format("https://{0}/adfs/oauth2/token/", AdfsInstance));
Uri address = new Uri(string.Format(AdfsConfiguration.AdfsTokenServiceUrl, AdfsConfiguration.AdfsInstance));
Uri redirectAddress = new Uri(AdfsConfiguration.RedirectUri);
NameValueCollection values = new NameValueCollection
{
{"client_id", AdfsConfiguration.ClientId},
{"grant_type", "authorization_code"},
{"code", code},
{"redirect_uri", redirectAddress.ToString()}
};
byte[] responseBytes = client.UploadValues(address, "POST", values);
string response = System.Text.Encoding.UTF8.GetString(responseBytes);
return response;
}
catch (Exception exp)
{
var additionalInfo = $" additionalInfo : [address: {string.Format(AdfsConfiguration.AdfsTokenServiceUrl, AdfsConfiguration.AdfsInstance) }, redirect Uri :{AdfsConfiguration.RedirectUri}]";
throw new SecurityException($"AdfsAuthorization.GetAdfsOAuthTokenByAuthCode is failed, {additionalInfo}", exp);
}
finally
{
client.Dispose();
}

Create to database using web api

I am trying to insert a new entry in my database using web api. I have two web projects: one is a UI project where all the user interaction will occur and the other is a services project which will handle all interactions with my database.
Below is my post method that will take in form data for creating a new team.
// POST: Api/Team/Create
[HttpPost]
public ActionResult Create(Team team)
{
try
{
if (ModelState.IsValid)
{
HttpEndPointContext httpEndPoint = new HttpEndPointContext()
{
AuthenticationMethod = HttpAuthenticationMethods.None,
Ssl = false,
HttpMethod = HttpMethod.Post,
Path = "localhost:32173/api/team/",
QueryStrings = null,
PayloadData = SerializationHelper.Current.Serialize(team.ToString(), SerializationTypes.Xml)
};
IProcessResult result = HttpConnectionManager.Current.SendMessage(httpEndPoint);
}
return RedirectToAction("Index");
}
catch
{
return View();
}
}
And this is my method for dealing with my PayloadStream/PayloadData attribute in the above method:
private void StreamPayload(HttpWebRequest webRequest, HttpEndPointContext httpEndPointContext)
{
if (httpEndPointContext.HttpMethod == new HttpMethod("GET"))
return;
//TODO: FIX MAYBE .... sometimes we want to post body with GET.
//Stream vs string
if (httpEndPointContext.PayloadStream == null)
{
//Wrap with SOAP Envelope and method if defined in SoapDefinition
string data = httpEndPointContext.PayloadData ?? String.Empty;
if (httpEndPointContext.SoapDefinition != null)
{
//If parameters is set, clear existing payload data.
data = String.Empty;
if (httpEndPointContext.SoapDefinition.Parameters != null)
foreach (var parameter in httpEndPointContext.SoapDefinition.Parameters)
{
data += String.Format("<{0}>{1}</{0}>", parameter.Key, parameter.Value);
}
data = String.Format("<s:Envelope xmlns:s='http://schemas.xmlsoap.org/soap/envelope/'>" +
"<s:Body><{0} xmlns='{2}'>" +
"{1}</{0}></s:Body></s:Envelope>",
httpEndPointContext.SoapDefinition.SoapMethod, data,httpEndPointContext.SoapDefinition.SoapGlobalKey);
}
byte[] byteArray = System.Text.Encoding.UTF8.GetBytes(data);
httpEndPointContext.PayloadStream = new MemoryStream(byteArray);
}
using (Stream requestStream = webRequest.GetRequestStream())
{
StreamHelper.Current.CopyStreams(httpEndPointContext.PayloadStream, requestStream);
requestStream.Close();
}
}
And the code for getting the server response. I'm currently getting an Internal Server (500) Error. Not sure why.
public IProcessResult SendMessage(HttpEndPointContext httpEndPointContext)
{
HttpWebRequest webRequest = CreateWebRequest(httpEndPointContext);
StreamPayload(webRequest, httpEndPointContext);
IProcessResult result = GetWebResponse(webRequest, httpEndPointContext);
return result;
}
private IProcessResult GetWebResponse(HttpWebRequest webRequest, HttpEndPointContext httpEndPointContext)
{
//Get Response
WebResponse response;
IProcessResult result = new ProcessResult(Statuses.Success);
try
{
response = webRequest.GetResponse();
}
catch (System.Net.WebException ex)
{
//Do exception handling. Still get the response for 500s etc.
result.Error.Exception = ex;
result.Status = Constants.Statuses.FailedUnknown;
result.ResponseCodeDescription = ex.Status.ToString();
result.ResponseCode = ex.Status.ToString();
result.Error.ErrorCode = ex.Status.ToString();
response = ex.Response;
//The error did not have any response, such as DNS lookup.
if (response == null)
return result;
}
try
{
//Get the response stream.
Stream responseData = response.GetResponseStream();
if (responseData == null)
throw new CoreException("No Response Data in GetWebResponse.",
"No Response Data in GetWebResponse. EndPoint:{0}", httpEndPointContext.ToString());
// Open the stream using a StreamReader for easy access.
var reader = new StreamReader(responseData);
// Read the content.
result.ResponseData = reader.ReadToEnd();
}
finally
{
response.Close();
}
result.ResponseCode = ((int)((HttpWebResponse)response).StatusCode).ToString();
result.ResponseCodeDescription = ((HttpWebResponse) response).StatusDescription;
return result;
}
And finally, my method for inserting to the database, found in my services project:
//POST api/controller/5
public IProcessResult Insert(Team team)
{
return TeamBusinessManager.Current.Insert(SecurityManager.Current.ConnectionContext, new Team());
}
I'm confused as to why I'm getting the 500 error. I'm not sure if it's the PayloadData attribute in my POST method or is it something wrong with my method in my services project.

RequestContext.Principal.Identity.Name is empty in web api 2 post

I'm new to web api and I seem to be having an issue with getting the name of the signed in user inside of my post method. Im using
RequestContext.Principal.Identity.Name
However, this only seems to be returning an empty string. It works fine in my get method, but not in the post. Here's my entire method
[Route("receive")]
[HttpPost]
public HttpResponseMessage Receive(PostmarkInboundMessage message)
{
if (message != null)
{
// To access message data
var headers = message.Headers ?? new List<Header>();
// To access Attachments
if (message.Attachments != null)
{
var attachments = message.Attachments;
var c = new CVService();
var user = string.IsNullOrEmpty(RequestContext.Principal.Identity.Name) ? "unknown" : RequestContext.Principal.Identity.Name;
c.UpdateLog(user);
foreach (var attachment in attachments)
{
// Access normal members, etc
var attachmentName = attachment.Name;
// To access file data and save to c:\temp\
//if (Convert.ToInt32(attachment.ContentLength) > 0)
//{
// byte[] filebytes = Convert.FromBase64String(attachment.Content);
// var fs = new FileStream(attachmentSaveFolder + attachment.Name,
// FileMode.CreateNew,
// FileAccess.Write,
// FileShare.None);
// fs.Write(filebytes, 0, filebytes.Length);
// fs.Close();
//}
}
}
// If we succesfully received a hook, let the call know
return new HttpResponseMessage(HttpStatusCode.Created); // 201 Created
}
else
{
// If our message was null, we throw an exception
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError) { Content = new StringContent("Error parsing Inbound Message.") });
}
}
Any help will be greatly appreciated.
Be sure you send the header (token) in both methods GET and POST and also, set the [Authorize] filter in both methods or the controller itself so you will be rejected if the token is not being send

Resources