Im trying to follow this tutorial: http://bitoftech.net/2014/12/15/secure-asp-net-web-api-using-api-key-authentication-hmac-authentication/
On how to secure a webapi with HMAC. Somewhere in the tutorial code it it gives:
public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
var req = context.Request;
if (req.Headers.Authorization != null && authenticationScheme.Equals(req.Headers.Authorization.Scheme, StringComparison.OrdinalIgnoreCase))
{
var rawAuthzHeader = req.Headers.Authorization.Parameter;
var autherizationHeaderArray = GetAutherizationHeaderValues(rawAuthzHeader);
if (autherizationHeaderArray != null)
{
var APPId = autherizationHeaderArray[0];
var incomingBase64Signature = autherizationHeaderArray[1];
var nonce = autherizationHeaderArray[2];
var requestTimeStamp = autherizationHeaderArray[3];
var isValid = isValidRequest(req, APPId, incomingBase64Signature, nonce, requestTimeStamp);
if (isValid.Result)
{
var currentPrincipal = new GenericPrincipal(new GenericIdentity(APPId), null);
context.Principal = currentPrincipal;
}
else
{
context.ErrorResult = new UnauthorizedResult(new AuthenticationHeaderValue[0], context.Request);
}
}
else
{
context.ErrorResult = new UnauthorizedResult(new AuthenticationHeaderValue[0], context.Request);
}
}
else
{
context.ErrorResult = new UnauthorizedResult(new AuthenticationHeaderValue[0], context.Request);
}
return Task.FromResult(0);
}
I copied it of course, but mvc does not know about any unauthorized result with these parameters. So this line gives an error: context.ErrorResult = new UnauthorizedResult(new AuthenticationHeaderValue[0], context.Request);
I can't figure out how to give back the right result. Does anyone know?
I used the wrong namespace. Should be using System.Web.Http.Results;
Related
I keep coming back to this but what should I be returning from my database service call in my WeightLifting Service I have this code.
public async void CopySessionsPlayersToWeightLiftingBySessionId(int sessionID)
{
List<SessionPlayer> sessionsPlayers = new
List<SessionPlayer>();
sessionsPlayers = db.SessionPlayer.Where(w => w.SessionId ==
sessionID).ToList();
if (sessionsPlayers != null)
{
List<WeightLifting> weightLiftings = new List<WeightLifting>();
foreach (var sessionPlayer in sessionsPlayers)
{
var player = db.Players.Where(w => w.Id ==
sessionPlayer.PlayerId).FirstOrDefault();
WeightLifting weightLifting = new WeightLifting();
weightLifting.SessionStartDate = sessionPlayer.StartDate;
weightLifting.SessionEndDate = sessionPlayer.EndDate;
weightLifting.TeamId=sessionPlayer.TeamId;
weightLifting.SessionId = sessionPlayer.SessionId;
weightLifting.PlayersId = sessionPlayer.PlayerId;
weightLifting.PU = player.DefaultPU;
weightLifting.PUReps = Convert.ToInt32(player.DefaultPUReps);
weightLifting.BP = player.DefaultBP;
weightLifting.TB = player.DefaultTB;
weightLifting.TBReps = Convert.ToInt32(player.DefaultTBReps);
weightLifting.OP = player.DefaultOP;
weightLifting.OPReps = Convert.ToInt32(player.DefaultOPReps);
weightLifting.APU = player.DefaultAdvancedPu;
weightLifting.APUReps = Convert.ToInt32(player.DefaultAdvancedPuReps);
weightLifting.IsActive = true;
weightLifting.IsDeleted = false;
weightLiftings.Add(weightLifting);
}
db.AddRange(weightLiftings);
db.SaveChanges();
}
}
As I am just doing a save out my http client call is basically this.
public async Task<HttpStatusCode>
CopySessionsPlayersToWeightLiftingBySessionId(int Id)
{
EnsureHttpClientCreated();
var json = JsonConvert.SerializeObject(Id);
var httpContent = new StringContent(json, Encoding.UTF8,
"application/json");
var httpResponse = await httpClient.PostAsync(Constants.BaseUrl
+ Constants.ApiSegmant +
Constants.CopySessionsPlayersToWeightLiftingBySessionId +
$"?Id={Id}", httpContent);
return httpResponse.StatusCode;
}
My Create Http Client code
private void EnsureHttpClientCreated()
{
if (httpClient == null)
{
CreateHttpClient();
}
}
private void CreateHttpClient()
{
_httpClientHandler = new HttpClientHandler
{
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
};
httpClient = new HttpClient(_httpClientHandler, false)
{
Timeout = _timeout
};
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(ClientUserAgent);
if (!string.IsNullOrWhiteSpace(Constants.BaseUrl))
{
httpClient.BaseAddress = new Uri(Constants.BaseUrl);
}
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeJson));
}
At the min only the ui will get a ok but how should I handle sending back an error here yes I could try catch around the save changes then put a BadContent but that feels messy?.
Consider using exception filters. They basically serve as a middleware that runs after your endpoint throws an exception to catch it and turn it into a specific response.
I'm working on an application based on .NET Framework 4.8. I'm using Microsoft Batching API. The below are code snippets
public async Task<List<BatchResponse>> UpdateEventsInBatchAsync(string accessToken, Dictionary<int, Tuple<string, OfficeEvent>> absEvents)
{
var httpMethod = new HttpMethod("PATCH");
var batches = GetUpdateRequestBatches(absEvents, httpMethod);
var graphClient = GetGraphClient(accessToken);
var batchResponses = new List<BatchResponse>();
foreach (var batch in batches)
{
try
{
var batchResponseList = await ExecuteBatchRequestAsync(graphClient, batch).ConfigureAwait(false);
batchResponses.AddRange(batchResponseList);
}
catch (ClientException exc)
{
_logService.LogException("Error while processing update batch", exc);
batchResponses.Add(new BatchResponse
{ StatusCode = HttpStatusCode.InternalServerError, ReasonPhrase = exc.Message });
}
catch (Exception exc)
{
_logService.LogException("Error while processing update batch", exc);
batchResponses.Add(new BatchResponse { StatusCode = HttpStatusCode.InternalServerError, ReasonPhrase = exc.Message });
}
}
return batchResponses;
}
The respective methods used in the above code are mentioned below in respective order-
GetUpdateRequestBatches
private IEnumerable<BatchRequestContent> GetUpdateRequestBatches(Dictionary<int, Tuple<string, OfficeEvent>> absEvents, HttpMethod httpMethod)
{
var batches = new List<BatchRequestContent>();
var batchRequestContent = new BatchRequestContent();
const int maxNoBatchItems = 20;
var batchItemsCount = 0;
foreach (var kvp in absEvents)
{
System.Diagnostics.Debug.Write($"{kvp.Key} --- ");
System.Diagnostics.Debug.WriteLine(_serializer.SerializeObject(kvp.Value.Item2));
var requestUri = $"{_msOfficeBaseApiUrl}/me/events/{kvp.Value.Item1}";
var httpRequestMessage = new HttpRequestMessage(httpMethod, requestUri)
{
Content = _serializer.SerializeAsJsonContent(kvp.Value.Item2)
};
var requestStep = new BatchRequestStep(kvp.Key.ToString(), httpRequestMessage);
batchRequestContent.AddBatchRequestStep(requestStep);
batchItemsCount++;
// Max number of 20 request per batch. So we need to send out multiple batches.
if (batchItemsCount > 0 && batchItemsCount % maxNoBatchItems == 0)
{
batches.Add(batchRequestContent);
batchRequestContent = new BatchRequestContent();
batchItemsCount = 0;
}
}
if (batchRequestContent.BatchRequestSteps.Count < maxNoBatchItems)
{
batches.Add(batchRequestContent);
}
if (batches.Count == 0)
{
batches.Add(batchRequestContent);
}
return batches;
}
GetGraphClient
private static GraphServiceClient GetGraphClient(string accessToken)
{
var graphClient = new GraphServiceClient(new DelegateAuthenticationProvider(requestMessage =>
{
requestMessage
.Headers
.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
return Task.FromResult(0);
}));
return graphClient;
}
ExecuteBatchRequestAsync
private async Task<List<BatchResponse>> ExecuteBatchRequestAsync(IBaseClient graphClient, BatchRequestContent batch)
{
BatchResponseContent response = await graphClient.Batch.Request().PostAsync(batch);
Dictionary<string, HttpResponseMessage> responses = await response.GetResponsesAsync();
var batchResponses = new List<BatchResponse>();
var failedReqKeys = new Dictionary<string, TimeSpan>();
foreach (var key in responses.Keys)
{
using (HttpResponseMessage httpResponseMsg = await response.GetResponseByIdAsync(key))
{
var responseContent = await httpResponseMsg.Content.ReadAsStringAsync();
string eventId = null;
var reasonPhrase = httpResponseMsg.ReasonPhrase;
if (!string.IsNullOrWhiteSpace(responseContent))
{
var eventResponse = JObject.Parse(responseContent);
eventId = (string)eventResponse["id"];
// If still null, then might error have occurred
if (eventId == null)
{
var errorResponse = _serializer.DeserializeObject<ErrorResponse>(responseContent);
var error = errorResponse?.Error;
if (error != null)
{
if (httpResponseMsg.StatusCode == (HttpStatusCode)429)
{
System.Diagnostics.Debug.WriteLine($"{httpResponseMsg.StatusCode} {httpResponseMsg.Content}");
var executionDelay = httpResponseMsg.Headers.RetryAfter.Delta ?? TimeSpan.FromSeconds(5);
failedReqKeys.Add(key, executionDelay);
continue;
}
reasonPhrase = $"{error.Code} - {error.Message}";
}
}
}
var batchResponse = new BatchResponse
{
Key = key,
EventId = eventId,
StatusCode = httpResponseMsg.StatusCode,
ReasonPhrase = reasonPhrase
};
batchResponses.Add(batchResponse);
}
}
if (failedReqKeys.Count == 0) return batchResponses;
return await HandleFailedRequestsAsync(graphClient, failedReqKeys, batch, batchResponses).ConfigureAwait(false);
}
HandleFailedRequestsAsync
private async Task<List<BatchResponse>> HandleFailedRequestsAsync(IBaseClient graphClient, Dictionary<string, TimeSpan> failedReqKeys, BatchRequestContent batch, List<BatchResponse> batchResponses)
{
// Sleep for the duration as suggested in RetryAfter
var sleepDuration = failedReqKeys.Values.Max();
Thread.Sleep(sleepDuration);
var failedBatchRequests = batch.BatchRequestSteps.Where(b => failedReqKeys.Keys.Contains(b.Key)).ToList();
var failedBatch = new BatchRequestContent();
foreach (var kvp in failedBatchRequests)
{
failedBatch.AddBatchRequestStep(kvp.Value);
}
var failedBatchResponses = await ExecuteBatchRequestAsync(graphClient, failedBatch);
batchResponses.AddRange(failedBatchResponses);
return batchResponses;
}
I'm getting an error as on the first line in method ExecuteBatchRequestAsync as
Microsoft.Graph.ClientException: Code: invalidRequest
Message: Unable to deserialize content.
---> System.ObjectDisposedException: Cannot access a closed Stream.
Can anyone nudge me where I'm doing wrong?
I am using .net core 3.1 and System.Text.Json.
I have a string in a sub element of a json with element claims whom i wanted to load in some .net element and then validate the keys in it
{
"scope": "openid MyScop",
"claims": "{\"premiuminfo\":{\"country\":{\"value\":\"country1\"},\"town\":{\"value\":\"town1\"},\"given_name\":{\"value\":\"given_name1\"},\"postal_code\":{\"value\":\"postal_code1\"},\"family_name\":{\"value\":\"family_name1\"},\"houseno_or_housename\":{\"value\":\"test house number\"}}}",
}
I have able to load the claims object in JsonElement
JsonElement o = JsonSerializer.Deserialize<JsonElement>(s);
But unable to find any way to check the keys like premiuminfo , county e.t.c.
Can please someone help me in using it
You might be looking for TryGetProperty
TryGetProperty(ReadOnlySpan, JsonElement)
Looks for a property
named propertyName in the current object, returning a value that
indicates whether or not such a property exists. When the property
exists, the method assigns its value to the value argument.
Were using TryGetProperty wrongly
Following is the way i were able to validate
string s = "{\"premiuminfo\":{\"country\":{\"value\":\"country1\"},\"town\":{\"value\":\"town1\"},\"given_name\":{\"value\":\"given_name1\"},\"postal_code\":{\"value\":\"postal_code1\"},\"family_name\":{\"value\":\"family_name1\"},\"houseno_or_housename\":{\"value\":\"houseno_or_housename1\"}}}";
//s = jwtData.ContainsKey("claims");
try
{
JsonElement o = JsonSerializer.Deserialize<JsonElement>(s);
if (o.TryGetProperty("premiuminfo", out var premiuminfo))
{
if (!premiuminfo.TryGetProperty("name", out var _) && (!premiuminfo.TryGetProperty("given_name", out var _) || !premiuminfo.TryGetProperty("family_name", out var _)))
{
processingResult = new ProcessingResultObject { ErrorResult = new ErrorResult { error = ErrorTypes.invalid_request.ToString(), error_description = "name or given_name, family_name is missing in claims" }, StatusCode = 400 };
}
else if (premiuminfo.TryGetProperty("name", out var _) && (premiuminfo.TryGetProperty("given_name", out var _) || !premiuminfo.TryGetProperty("family_name", out var _)))
{
processingResult = new ProcessingResultObject { ErrorResult = new ErrorResult { error = ErrorTypes.invalid_request.ToString(), error_description = "name and (given_name, family_name) both exists in claims" }, StatusCode = 400 };
}
else if (!premiuminfo.TryGetProperty("address", out var _) && (!premiuminfo.TryGetProperty("houseno_or_housename", out var _) || !premiuminfo.TryGetProperty("postal_code", out var _)))
{
processingResult = new ProcessingResultObject { ErrorResult = new ErrorResult { error = ErrorTypes.invalid_request.ToString(), error_description = "address is missing in claims" }, StatusCode = 400 };
}
else if (premiuminfo.TryGetProperty("address", out var _) && (premiuminfo.TryGetProperty("houseno_or_housename", out var _) || premiuminfo.TryGetProperty("postal_code", out var _)))
{
processingResult = new ProcessingResultObject { ErrorResult = new ErrorResult { error = ErrorTypes.invalid_request.ToString(), error_description = "address and (houseno_or_housename, postal_code) both exists in claims" }, StatusCode = 400 };
}
else
{
processingResult.Result = true;
}
}
else
{
processingResult = new ProcessingResultObject { ErrorResult = new ErrorResult { error = ErrorTypes.invalid_request.ToString(), error_description = "premiuminfo are not found in claims" }, StatusCode = 400 };
}
}
catch (Exception ex)
{
processingResult = new ProcessingResultObject { ErrorResult = new ErrorResult { error = ErrorTypes.invalid_request.ToString(), error_description = "premiuminfo are not found in claims. ex" }, StatusCode = 400 };
}
I would recommend using Newtonsoft.Json library that gives you more functionality. You can parse your main json and then the string object you have for claims as well to access the properties within claims.
example
var obj = JObject.Parse(json);
var claims = JObject.Parse(obj["claims"].ToString());
Console.WriteLine(claims["premiuminfo"].ToString()); // Prints entire claim
Console.WriteLine(claims["premiuminfo"]["country"].ToString()); // prints the country.
I have implemented FormAuthentication in asp.net mvc 5 and create FormsAuthenticationticket on LogIn and it creates successfully but after few moments that cookie is showing in browser but in application it's getting null.
Please help to solve this issue.
Any help will be appreciated Thanks
LOGIN FORM
public ActionResult Login([Bind(Include = "Username, Password")] LoginModel loginModel, string ReturnUrl)
{
if (ModelState.IsValid)
{
Egov_Users eGov_Users = db.Egov_Users
.Where(p => p.UserType.Type != "O" && p.UserName == loginModel.Username)
.FirstOrDefault();
if (eGov_Users == null)
{
ModelState.AddModelError("", "Invalid username");
return View();
}
else
{
if (eGov_Users.Password != loginModel.Password)
{
ModelState.AddModelError("", "Invalid Password");
return View();
}
var loginDetail = new LoginDetails();
var serializer = new JavaScriptSerializer();
loginDetail.userID = eGov_Users.UserId;
loginDetail.username = eGov_Users.UserName;
loginDetail.firstName = eGov_Users.FirstName;
loginDetail.lastName = eGov_Users.LastName;
var userData = SerializeUserInfoInternal(loginDetail);
FormsAuthentication.SetAuthCookie(loginDetail.username, false);
var cookie = FormsAuthentication.GetAuthCookie(
FormsAuthentication.FormsCookieName, false);
var ticket = FormsAuthentication.Decrypt(cookie.Value);
var durationInHours = 8;
FormsAuthenticationTicket newTicket = new FormsAuthenticationTicket(
ticket.Version,
loginDetail.username,
DateTime.Now,
DateTime.Now.AddHours(durationInHours),
true,
userData);
// Encrypt the ticket.
string encTicket = FormsAuthentication.Encrypt(newTicket);
cookie.Value = encTicket;
Response.Cookies.Add(cookie);
int cookieSize = System.Text.UTF8Encoding.UTF8.GetByteCount(cookie.Values.ToString());
Session["CookieSize"] = cookieSize;
if (string.IsNullOrEmpty(ReturnUrl))
{
return RedirectToAction("Index", "Users");
}
}
}
return RedirectToAction("Login", "Login");
}
GLOBAL ASAX
protected void Application_PostAuthenticateRequest()
{
var ticket = GetTicketFromCurrentCookie();
if (ticket == null)
{
Global.WriteLog("Application_PostAuthenticateRequest", "ticket becomes null");
return;
}
var user = DeserializeUserInfoInternal(ticket.Name, ticket.UserData);
if (user == null)
{
return;
}
var principal = new AppUserPrincipal(user);
HttpContext.Current.User = principal;
}
private static FormsAuthenticationTicket GetTicketFromCurrentCookie()
{
var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (cookie == null)
{
Global.WriteLog("GetTicketFromCurrentCookie", "Cookie becomes null");
return null;
}
var ticket = FormsAuthentication.Decrypt(cookie.Value);
return ticket;
}
static LoginDetails DeserializeUserInfoInternal(string name, string userData)
{
var deserialize = new JavaScriptSerializer();
var loginDetails = deserialize.Deserialize<LoginDetails>(userData);
return loginDetails;
}
use below code to get the cookie value
HttpContext.Current.Request.Cookies.Get(".ASPXAUTH");
I'm trying to return an appropriate error message when a user tries to call the service when their authorization token has expired or if they have an invalid token.
The problem I'm having is the first time it is called, the message is sent properly, but after the first time the SendAsync method is called 4 times and the message data returns null.
I'm confused as to why it loops 4 times, and I tried stepping through it, but I can't get any further in the code.
Here is the code:
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (request.Headers != null)
{
// ....
if (request.Headers.GetValues(CustomTokenHeader).FirstOrDefault() == null)
{
//unauthorized response(401)
return FromResult(_unauthorizedResponse);
}
var authHeader = request.Headers.GetValues(CustomTokenHeader).FirstOrDefault();
if (String.IsNullOrWhiteSpace(authHeader))
{
//unauthorized response(401)
return FromResult(_unauthorizedResponse);
}
//authenticate token
return HandleTokenAuthentication(request, cancellationToken, authHeader);
}
}
static Task<T> FromResult<T>(T t)
{
var tcs = new TaskCompletionSource<T>();
tcs.SetResult(t);
return tcs.Task;
}
private Task<HttpResponseMessage> HandleTokenAuthentication(HttpRequestMessage request, CancellationToken cancellationToken, string authHeader)
{
//parse token
var token = ParseToken(authHeader);
if (String.IsNullOrWhiteSpace(token))
{
//unauthorized response(401)
return FromResult(_unauthorizedResponse);
}
//decrypt token
var tokenInfo = DecryptToken(token);
if (tokenInfo == null)
{
//unauthorized response(401)
return FromResult(_unauthorizedResponse);
}
//validate token
var claims = ValidateToken(tokenInfo, token);
if (claims == null)
{
//unauthorized response(401)
return FromResult(_unauthorizedTokenExpired);
}
var principal = CheckCustomAuthorization(claims);
if (principal == null)
{
//unauthorized response(401)
return FromResult(_unauthorizedResponse);
}
if (!principal.Identity.IsAuthenticated)
{
var loginFailureMessage = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{
Content = new StringContent(((AgencyClaims)principal.Identity).LoginFailureReason)
};
return FromResult(loginFailureMessage);
}
//assign principal
Thread.CurrentPrincipal = principal;
return base.SendAsync(request, cancellationToken)
.ContinueWith(task => AuthorizedResponse(request, task.Result));
}
static HttpResponseMessage AuthorizedResponse(HttpRequestMessage request, HttpResponseMessage response)
{
if ((request.Method == HttpMethod.Get && response.StatusCode == HttpStatusCode.OK
&& !response.Headers.Contains(CustomTokenHeader))
|| (request.Method == HttpMethod.Post && response.StatusCode == HttpStatusCode.Created
&& !response.Headers.Contains(CustomTokenHeader)))
{
var token = ((AgencyClaims) Thread.CurrentPrincipal.Identity).Token;
response.Headers.Add(CustomTokenHeader, Convert.ToBase64String(Encoding.ASCII.GetBytes(token)));
}
return response;
}
readonly HttpResponseMessage _unauthorizedResponse =
new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content = new StringContent("PROPER ERROR MESSAGE")};
Successful response:
<data contentType="text/plain; charset=utf-8" contentLength="21"><![CDATA[Authentication failed]]></data>
Here is the response after the first successful response:
<data contentType="null" contentLength="0"><![CDATA[]]></data>
Here is part of the request:
GET http://localhost:20559/api/Service?Name=Jack HTTP/1.1
Ok, I was able to resolve the issue. The _unauthorizedResponse class variable is somehow allowing the code to run once through successfully, but not a second time. This issue is not related to the readonly modifier, since it still doesn't work without it. I'm not sure how this works (and maybe someone on here can explain), but by moving them into the local scope within the method it is able to run correctly each time.