Do I need a second HttpClient to make an API call to refresh JWT token inside custom DelegatingHandler? - xamarin

For each API call in my App, I want to check whether the user has an expired JWT, and if so, I want to get a new one using a refresh token, and then proceed with the original request to API. This is supposed to all work in the background without the APP user experiencing any interruptions or need to login again.
I create my HttpClient like this:
static DelegatingHandler handler = new AuthenticationHandler();
static HttpClient httpClient = new HttpClient(handler)
{
BaseAddress = new Uri("https://10.0.2.2:5001/api/v1")
};
AuthenticationHandler is a custom DelegatingHandler which has an override SendAsync method. Inside that method I check if request has status Unauthorised:
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
And if it does, I need to send another request to my API with the currently owned JWT and Refresh tokens to generate new pair of tokens... Since this is an API call in the middle of another API call (as it all happens inside the custom DelegatingHandler which is a parameter for constructing my main HttpClient) - does refreshing the token needs to happen using a second HttpClient that I need to create literally to make the refresh token call?
I can't see how can I use the same HttpClient for this, how is this usually being done?
EDIT:
I can't see how I could use the same HttpClient for refreshToken call from inside AuthenticationHandler, as the handler is used to construct the HttpClient. Feels like a circular reference. I just have no idea how others do it in their code... I currently implemented it by using that second HttpClient which I only use for that one refreshToken call, and it works, but I have a feeling that there is a cleaner way to achieve this?
Btw, my (not refactored yet) SendAsync method inside AuthenticationHandler looks like this currently:
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
HttpResponseMessage response = new HttpResponseMessage();
request = CheckForAuthToken(request);
response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
for (int i = 1; i == _maxRefreshAttempts; i++)
{
// Here I make a call to the API to refresh and return a new JWT, The authApiService uses a different HttpClient
RefreshTokenRequestModel refreshTokenRequestModel = new RefreshTokenRequestModel
{
Token = await SecureStorage.GetAsync("jwtToken"),
RefreshToken = await SecureStorage.GetAsync("refreshToken")
};
var apiResponse = await authApiService.RefreshToken(refreshTokenRequestModel);
if (apiResponse.IsSuccessStatusCode)
{
await SecureStorage.SetAsync("jwtToken", apiResponse.Content.Token);
await SecureStorage.SetAsync("refreshToken", apiResponse.Content.RefreshToken);
request = CheckForAuthToken(request);
response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
}
}
}
return response;
}
catch (Exception e)
{
throw e;
}
}

Related

How to make https webAPI call?

I'm trying to make https webAPI call, specifically - Google Directions API. Putting the uri directly inside browser gives me the result that I want, so I'm 100% sure my uri is correct.
Now, how do I call the webapi inside my PCL? Using modernhttp and HttpClient now, but am open to whatever options there are out there.
private async Task<string> GetJsonObjFromUrl(string urlRoutes)
{
HttpClient c = new HttpClient(new NativeMessageHandler());
var resp = await c.SendAsync(new HttpRequestMessage(HttpMethod.Get, new Uri(urlRoutes)));
if (resp.IsSuccessStatusCode)
{
var json = await resp.Content.ReadAsStringAsync();
return json;
}
return null;
}
What am I doing wrong?
Edit: Just putting this here because this was driving me crazy whole night. Ends up the caller way, way above forgot to put await. The execution continues straight after and never returns to get the result. That's why I never got any results... :\
The code just don't go hit anywhere below client.SendAsync / GetStringAsync
I suspect that further up your call stack, your code is calling Result / Wait / GetAwaiter().GetResult() on a task. If called from a UI thread, this will deadlock, as I explain on my blog.
The deadlock is caused by the async method attempting to resume on the UI context, but the UI thread is blocked waiting for the task to complete. Since the async method must complete in order to complete its task, there's a deadlock.
The proper fix is to replace that Result / Wait with await.
In your PCL use:
HttpClient httpClient = new HttpClient();
var json = await httpClient.GetStringAsync(Url);
In case of using HTTPS.
In Android, your main activity:
protected override void OnCreate(Bundle bundle)
{
ServicePointManager.ServerCertificateValidationCallback +=(sender, cert, chain, sslPolicyErrors) => true;
}
In iOS, in your AppDelegate:
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
return base.FinishedLaunching(app, options);
}

OAuth - Read the generated Access token and add cookie in response

I am using OAuth in ASP.NET Web Api to return access token to the caller of the application.
I have inherited my OAuth provider class from OAuthAuthorizationServerProvider and once the user is authenticated inside the GrantResourceOwnerCredentials function, I want to read the generated access token, create it's hash with some salt value and then add the created hash into a cookie.
Below is the simplified definition of my GrantResourceOwnerCredentials function.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager, CookieAuthenticationDefaults.AuthenticationType);
//Add claims required on client side.
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
//Generate the token behind the scene for given ticket
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
SetCsrfCookie(context);
}
private void SetCsrfCookie(OAuthGrantResourceOwnerCredentialsContext context)
{
var accessToken = "<READ THE GENERATED ACCESS TOKEN HERE>"; //<------ How?
if(string.IsNullOrEmpty(accessToken)) return;
var csrfToken = Helper.GetHash(accessToken);
context.Response.Cookies.Append("XSRF-TOKEN", csrfToken, new CookieOptions {HttpOnly = false});
}
I am facing two issues here.
First one is how to read the generated access token in the SetCsrfCookie function in the code above.
Generated cookie is not received on the client side.
I know its possible to intercept the response in a some OwinMiddleware inherited class and then I may be able to generate the required cookie and attach to the response but first I have not tried that and secondly, it seems better option to handle this case inside my OAuth provider class as some people suggest that deriving from the OwinMiddleware is not a good practice.
I finally managed to fix the cookie issue by adding the below line of code on angular side
$httpProvider.defaults.withCredentials = true;
On the Web Api side I just set the Access-Control-Allow-Credentials response header to true inside the WebApiConfig.Register method like below:
var cors = new EnableCorsAttribute(ConfigurationManager.AppSettings["ALLOWED_ORIGIN"], "*", "*")
{
SupportsCredentials = true
};
config.EnableCors(cors);
This solved my cookie problem.
For accessing the generated access token I inherited a class from OwinMiddleware and inside the Invoke function I access the response body to read the access token like below:
public override async Task Invoke(IOwinContext context)
{
var path = context.Request.Path;
var stream = context.Response.Body;
var buffer = new MemoryStream();
context.Response.Body = buffer;
await Next.Invoke(context);
var reqStream = new StreamReader(context.Request.Body);
reqStream.BaseStream.Position = 0;
var data = reqStream.ReadToEnd();
if (path.Equals(new PathString("/token"),StringComparison.CurrentCultureIgnoreCase))
{
buffer.Seek(0, SeekOrigin.Begin);
var reader = new StreamReader(buffer);
var responseBody = await reader.ReadToEndAsync();
//check if the response body contains access token if so then do your processing
}
buffer.Seek(0, SeekOrigin.Begin);
await buffer.CopyToAsync(stream);
}

Web API - Get information encrypted inside token, ticket ExpiresUtc and IssuedUtc

I am using Web API as my back-end and implemented the token security using the built in mechanism. In the template code, when issuing the access token, I can get the issued and expired dates of the token:
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
var issued = context.Properties.IssuedUtc;
var expired = context.Properties.ExpiresUtc;
.
.
.
}
Now when a request is made to a method that requires authorization I want to do something similar:
[Authorize]
public async Task<string> GetTokenInfo()
{
//var issued = GetCurrentTicket().Properties.ExpiresUtc;
//var issued = GetCurrentTicket().Properties.IssuedUtc;
.
.
.
}
So how can I get the information encrypted inside the token, more specifically the ExpireUtc and IssuedUtc ?
You can easily retrieve the AuthenticationProperties dictionary using IAuthenticationManager.AuthenticateAsync, which returns a AuthenticateResult object: https://msdn.microsoft.com/en-us/library/dn270674(v=vs.113).aspx
From a Web API controller, you'll need the GetOwinContext extension to get the OWIN context from the request message and use IOwinContext.Authentication: https://msdn.microsoft.com/en-us/library/system.net.http.owinhttprequestmessageextensions.getowincontext(v=vs.118).aspx
var context = Request.GetOwinContext();
var result = await context.Authentication.AuthenticateAsync(OAuthDefaults.AuthenticationType);
if (result == null) {
throw new InvalidOperationException();
}
var properties = result.Properties;
(of course, you also need to have a properly configured app.UseOAuthBearerAuthentication call in your Startup class, but I assume it's the case here).

ASP.NET Web API - Passing a UserToken(string) to a LoginController using a DelegatingHandler

I have an ASP.NET Web API project and on initial user login, the username and password are sent in an http header over SSL and validated by the server.
The server creates a database record with the UserId, a randmon 64 character string (UserToken), expiration date and the client IP address.
The UserToken is then sent back to the client and then be stored in a cookie.
All subsequent requests send the UserToken in an http header and that is validated using the calling IP address by the server.
This way, the username and password are only sent once, and all calls using the UserToken are logged.
I have created two custom DelegatingHandlers - LoginAuthenticationHandler, and TokenAuthenticationHandler - which process the http headers and send an appropriate 200 or 400 http response.
////////////////
Seems my only problem is that I want the LoginAuthenticationHandler to also return the UserToken to the client, so it can store the cookie.
Sorry for the verbosity :-\
Also - I'm new to Web API - so maybe this is not the best place for this to be done - but it would be very convenient if the UserToken can be passed back to the LoginController in this way.
Thanks for any input :-)
Some related SO posts:
DelegatingHandler for response in WebApi
Is it possible to pass data from DelegatingHandler to Controller in ASP.NET Web API?
////////////////
public class LoginAuthenticationHandler : DelegatingHandler
{
public const string BasicScheme = "Basic";
public const string ChallengeAuthenticationHeaderName = "WWW-Authenticate";
public const char AuthorizationHeaderSeparator = ':';
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
// Get Authorization Http Header
var authHeader = request.Headers.Authorization;
if (authHeader == null)
{
// Unauthorized
return CreateUnauthorizedResponse();
}
// Check if Basic Authentication
if (authHeader.Scheme != BasicScheme)
{
// Unauthorized
return CreateUnauthorizedResponse();
}
// Decode UserName + Password from Http Header
var encodedCredentials = authHeader.Parameter;
var credentialBytes = Convert.FromBase64String(encodedCredentials);
var credentials = Encoding.ASCII.GetString(credentialBytes);
var credentialParts = credentials.Split(AuthorizationHeaderSeparator);
if (credentialParts.Length != 2)
{
// Unauthorized
return CreateUnauthorizedResponse();
}
var username = credentialParts[0].Trim();
var password = credentialParts[1].Trim();
// Authenticate Username + Password and Return UserToken
var userId = new Users().GetUserIdFromUserNamePassword(username, password);
if (userId == 0)
{
// Unauthorized
return CreateUnauthorizedResponse();
}
// User is Authorized - Create New UserToken
var ipAddress = HttpContext.Current.Request.UserHostAddress;
var userToken = new Users().CreateUserToken(ipAddress, userId);
return base.SendAsync(request, cancellationToken).ContinueWith(task =>
{
var response = task.Result;
//======================================================
// Return UserToken to Login Controller to be Stored as Cookie on the Client
// response.Content = userToken ??
// maybe set header for userToken ??
// HttpRequestMessage Properties ??
return response;
//======================================================
});
}
private static Task<HttpResponseMessage> CreateUnauthorizedResponse()
{
// Send Back Http Unauthorized if Authentication Fails
var response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
response.Headers.Add(ChallengeAuthenticationHeaderName, BasicScheme);
var taskCompletionSource = new TaskCompletionSource<HttpResponseMessage>();
taskCompletionSource.SetResult(response);
return taskCompletionSource.Task;
}
}
}
Generally, HTTP services are stateless and the concept of login does not apply. LoginController is for the MVC controllers and not web API. What you are trying to do is not a good practice, even though it is technically possible to achieve.
If you really want to do what you are trying to do, do not think along the lines of sending the session data (what you call the user token) to LoginController. You can write the cookie into the response from your message handler itself. See this. You must only store encrypted data into a cookie in that case. Instead of creating your own cookie and all that, you can use Forms Authentication and create a cookie with FA ticket. See this.
BTW, it is possible and easy to spoof client IP addresses.
Perhaps you could login using the controller without using a DelegatingHandler: you could return the token to the client to be added to the header of future API calls, or add it to the header in the controller using the Request.Headers.Add function.
Then you would not need two custom DelegatingHandlers, the TokenAuthenticationHandler would be sufficient. But you would want to specify that all requests other than the initial login are funneled through the TokenAuthenticationHandler.
To do that, you will need to customize the WebAPI routes. In the default Web API projects, this is currently done in the WebApiConfig.Register method in WebApiConfig.cs (called from Global.asax.cs). First, have all your API calls route through your TokenAuthenticationHandler; then add the login route plainly such that it does not funnel through your TokenAuthenticationHandler:
//this message handler chain is used to put TokenAuthenticationHandleron all API requests and not Login
DelegatingHandler[] handlers = new DelegatingHandler[] {
new TokenAuthenticationHandler()
};
var routeHandlers = HttpClientFactory.CreatePipeline(new HttpControllerDispatcher(config), handlers);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}",
defaults: null,
constraints: null,
handler: routeHandlers
);
//login route
config.Routes.MapHttpRoute(
name: "Login",
routeTemplate: "login/{action}",
defaults: new { Controller = "Login" }
);
Now, you can validate the token in the TokenAuthenticationHandler using request.Headers.TryGetValues to get it:
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
//token validation
IEnumerable<string> foundValues = null;
if (request.Headers.TryGetValues("AuthenticationToken", out foundValues))
{
if (foundValues.Count() == 1)
{
string token = foundValues.Single();
AuthenticationDAO dao = new AuthenticationDAO();
if (dao.AuthenticateUser(token))
{
//add values to request.Properties for use in Web API controllers
request.Properties.Add(new KeyValuePair<string, object>("SomeValue", 4));
//Engage!
return base.SendAsync(request, cancellationToken);
}
}
}
//fail if token not present or not valid
var tcs = new TaskCompletionSource<HttpResponseMessage>();
tcs.SetResult(new HttpResponseMessage(HttpStatusCode.Forbidden)
{
Content = new StringContent("Missing or invalid authorization token.")
});
return tcs.Task;
}
As per your original question of passing values from the DelegatingHandler to the Controller, that is easily possible using the request.Properties.Add function as demonstrated above.
Some additional considerations:
I am not sure that sending the login credentials in the header is any
more secure than just as content in the request, since it is all over
SSL.
You should consider implementing an AntiForgeryToken. This
article is a good starter, and this SO post points out how
you could use DelegatingHandler to also only check for it on web
requests (allowing your api to be accessed from native apps).
You can easily add a DelegatingHandler that applies to all requests
that enforces HTTPS.
Hope that helps. What I've outlined is the way I'm doing it, so I hope for some comments if it's wrong.

Windows Phone sends more than one web requests in order in a call

Reccently, I am working on a project in Windows Phone. and In this project, to validate a user, I need to check at 3 web API, the logic is like below:
Step 1: access web api 1 to get the token
Step 2: access web api 2 to get the username/password by the token retrieved in Step 1
Step 3: access web API 3 to validate the user name/password in step 2
you can see we need to access those 3 API in order. as well know, window phone now access the network asynchronously, which causes a big challenge on make those API access in order, and which make the soure code hard to maintainace.
I also consider the synchronous source code like below, but I found there are some problems to access the network,many exeption will be thrown. For example, when an exception is thrown, I try to use asynchronous web request to access the same URL, it is OK. I am strugglig in it now. And I have to introduce thread to call it to avoid to block the UI thread.
internal static class HttpWebRequestExtensions
{
public const int DefaultRequestTimeout = 60000;
public static bool IsHttpExceptionFound = false;
public static WebResponse GetResponse(this WebRequest request, int nTimeOut = DefaultRequestTimeout)
{
var dataReady = new AutoResetEvent(false);
HttpWebResponse response = null;
var callback = new AsyncCallback(delegate(IAsyncResult asynchronousResult)
{
try
{
response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
dataReady.Set();
}
catch(Exception e)
{
IsHttpExceptionFound = true;
}
});
request.BeginGetResponse(callback, request);
if (dataReady.WaitOne(nTimeOut))
{
return response;
}
return null;
}
public static WebResponse PostRequest(this HttpWebRequest request, String postData, int nTimeOut = DefaultRequestTimeout)
{
var dataReady = new AutoResetEvent(false);
HttpWebResponse response = null;
var callback = new AsyncCallback(delegate(IAsyncResult asynchronousResult)
{
Stream postStream = request.EndGetRequestStream(asynchronousResult); //End the operation.
byte[] byteArray = Encoding.UTF8.GetBytes(postData); //Convert the string into a byte array.
postStream.Write(byteArray, 0, postData.Length); //Write to the request stream.
postStream.Close();
dataReady.Set();
});
request.BeginGetRequestStream(callback, request);
if (dataReady.WaitOne(nTimeOut))
{
response = (HttpWebResponse)request.GetResponse(nTimeOut);
if (IsHttpExceptionFound)
{
throw new HttpResponseException("Failed to get http response");
}
return response;
}
return null;
}
}
Any suggestion on using asynchronous web request to solve my case?
There's an example here of using asynchronous web services in a chained manner to call the Microsoft Translator service on WP7
Maybe it will give you some pointers?
http://blogs.msdn.com/b/translation/p/wp7translate.aspx

Resources