OData V4 client code generator and 401 response handling

I have a WebAPI 2.2 w / OData V4 service that uses a dedicated authentication mechanism. This is basically similar to OAuth Bearer authentication, so the request is made for a specific endpoint with a username and password, and a token is returned. The token is then included in the authorization header of all subsequent requests. I am using OData V4 (2.3.0) code generator and add the Authorization header manually using the DataServiceContext BuildingRequest event like this ...

private void GetData() {
    var context = new DataAccess.Default.Container(new Uri("http://myserver/API/"));

    context.BuildingRequest += onBuildingRequest;
    var data = context.Computers.ToList();
}
void onBuildingRequest(object sender, Microsoft.OData.Client.BuildingRequestEventArgs e)
{
    if (_token == null)
        _token = GetToken();
    e.Headers.Add("Authorization", "MyToken " + _token);
}

      

The problem I am facing is that the token expires after a certain amount of time, so after a while I will start getting a 401 response, which throws an exception that should be thrown at the moment the IQueryables from the context receive their GetEnumerator call (call ToList in the above code). I could wrap every place where I list the endpoint, but this is less than ideal. I found that I can detect a 401 in the ReceiveResponse event of the DataServiceContext and note that the token has expired, however this does not prevent the call from failing, it just makes subsequent calls work.

void context_ReceivingResponse(object sender, ReceivingResponseEventArgs e)
{
    if (e.ResponseMessage.StatusCode == 401)
    {
        _token = null;
    }
}

      

So at this point I'm trying to figure out a way to handle 401s without requiring every call (which will often be when enumerating an IQueryable) to be wrapped in try / catch. I tried to figure out a way for web requests to handle my own authentication in the same way that it handles Basic authentication (if the server responds with a 401 header and WWW-Authenticate and the credentials were specified for Basic authentication another request will be sent automatically with the correct authentication header) but no luck.

Any help / suggestions would be appreciated.

+3


source to share


2 answers


The solution is in the AuthenticaionManager class . By registering your own IAuthenticationModule implementation with Register , you can do what you are looking for.

You will also probably need another class that implements ICredentials and handles your authentication parameters like token endpoint url, etc., and then passes that to your DataServiceContext . You can even use it to cache your last token and its expiration date.

The only way I have found so far to use the processed token is to store the expiration time when retrieving the token from the authorization server and update it if it is close to expiring . And "close" I mean if it expires in n seconds. I preferred this threshold to be my network timeout for receiving the token. If it's expired, or even close to expiring, I'll get a new token from the server without even trying the last one. Feel free to use any of the measurements in this section.;) But there is no way to answer your authorization server request more than once, and it won't challenge you forever.



Take a look here for an example about the mentioned class and interfaces.

And BTW for using it in Portable Class Library you might be a little out of luck. There is no sign of AuthenticaionManager and IAuthenticationModule in pcl, Using some kind of abstraction might help.

+1


source


Perhaps you forgot to include context_ReceivingResponse in your GetData? (I don't see that) Otherwise, be sure to write down the http headers in the send and receive messages.



0


source







All Articles