Azure AD Authentication Breaking HTTP Post Actions When Session Times Out - c#

I recently changed from windows authentication to Azure AD using roughly the "out of the box" code;
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseKentorOwinCookieSaver();
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
//AuthenticationContext authContext = new AuthenticationContext(Authority, new ADALTokenCache(signedInUserID));
AuthenticationContext authContext = new AuthenticationContext(Authority);
return authContext.AcquireTokenByAuthorizationCodeAsync(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
}
}
});
}
Our users have started to get intermittent 404 errors when trying to submit certain forms. I think I have managed to recreate the issue by deleting cookies, so I suspect it's tied to when the session naturally times out.
If I look at the flow with a HTTP GET request it looks like;
HTTP GET https://myappurl/page?param1=value&param2=value
HTTP 302 response with redirect to https://login.microsoftonline.com (including various params; state, client_id etc)
HTTP 200 response (not quite sure how/why it then knows to redirect)
HTTP GET https://myappurl/
HTTP 302 response with redirect to original URL https://myappurl/page?param1=value&param2=value
HTTP GET https://myappurl/page?param1=value&param2=value
HTTP 200 response
Everything works a treat...
For a HTTP POST however;
HTTP POST to https://myappurl/another_page
HTTP 302 response with redirect to https://login.microsoftonline.com (including various params; state, client_id etc)
HTTP 200 response (not quite sure how/why it then knows to redirect)
HTTP GET https://myappurl/
HTTP 302 response with redirect to original URL https://myappurl/another_page
HTTP GET https://myappurl/another_page
HTTP 404 response
Fails because the endpoint only accepts HTTP POST requests.
Any idea if/how I can fix this? I would have thought the built in state tracking or whatever it is doing would store the original request and continue where it left off regardless...

It looks like you are not using the token cache. What this means is that a user's session will expire after about an hour after they sign into the application.
To address this issue you should use AcquireTokenSilentAsync whenever the application needs an access token. This method will automatically refresh the token for you using it's In Memory cache. For more details see https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/wiki/AcquireTokenSilentAsync-using-a-cached-token

Related

HttpClient send Auth header

I'm using HttpClient to call some REST API with authentication like this:
_networkCredential = new NetworkCredential(username, apiKey);
_httpClientHandler = new HttpClientHandler { Credentials = _networkCredential };
_httpClient = new HttpClient(_httpClientHandler);
This works nicely, but as defined in RFC2617 this will send every request without AUTH header, gets back a 401 and then sends the request a second time with AUTH header.
I'd like to get rid of the extra call without AUTH header to achieve better throughput. For BASIC AUTH I found the following example to manually add the BASIC AUTH header to every call:
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{username}:{apiKey}")));
But my service insists on DIGEST AUTH. How can I achieve this in DIGEST AUTH?
I'd guess I'd have to call the service once and cache the AUTH HEADER for subsequent calls. But if the nonce changes I'd need to refresh the AUTH HEADER? How could this be solved?

Authorization header is missing in request from client via Azure AD Application Proxy

I am facing an issue with missing Authorization header with Bearer token in the HTTP request from the client application to REST web service via Azure Active Directory Application Proxy.
The web service is hosted in on-premises and client application is consuming from internet using Azure AD application proxy URL and the request is authenticated against ADFS. The authentication header is added upon sending request to Azure AD application proxy URL and I guess it was removed by the proxy connector.
Please find the request content below and help. How can I include Authenticator header in the original request to Application proxy?
GET /TimelogSvc/rest/Timelog/GetTimeLog?EmailId=John#BizNext.com.sg&Fromdate=2019-04-21&Todate=2019-04-27 HTTP/1.1
Connection: Keep-Alive
Host: agileuat.BizNext.com.sg
X-Forwarded-For: 203.126.130.140
X-MS-Proxy: AzureAD-Application-Proxy
OS-Host: agileuat.BizNext.com.sg
OS-Path: /TimelogSvc
OS-Pta: /rest/Timelog
OS-Page: /GetTimeLog?EmailId=John08#BizNext.com.sg&Fromdate=2019-04-21&Todate=2019-04-27
Please also see my code below of the client.
AuthenticationContext authContext = new AuthenticationContext(authority + tenantID);
HttpClient httpClient = new HttpClient();
string s = string.Empty;
result = await authContext.AcquireTokenAsync(resourceUri, clientId, new Uri(redirectUri), new PlatformParameters(PromptBehavior.Auto));
// Append the token as bearer in the request header.
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
// Call the API.
HttpResponseMessage response = await httpClient.GetAsync("https://timelogapitest-BizNext.msappproxy.net/TimelogSvc/rest/Timelog/GetTimeLog?EmailId=John#bizNext.com.sg&Fromdate=2019-04-21&Todate=2019-04-27");
s = await response.Content.ReadAsStringAsync();
Mobile applications
I've worked around this by including the access token in a custom header (a second time). This assumes you have control over the backend so you can read that custom header. If necessary, you could even write this into the AuthHeader on the backend to ensure normal authorization flow from there.
HttpClient client = new HttpClient();
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, "API/URL/GoesHere");
// Add token to authentication header as per usual
message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
// Custom header containing the token a second time.
// Name if whatever you like, just make sure you look for the
// exact name on the backend
message.Headers.Add("JwtAccessToken", token);
HttpResponseMessage response = await client.SendAsync(message);
string responseString = await response.Content.ReadAsStringAsync();
Web Clients
This seems to work now:
https://feedback.azure.com/forums/169401-azure-active-directory/suggestions/32386468-forward-incoming-jwt-token-to-backend-service#{toggle_previous_statuses}

How to pass authorization token to GET method to access authorized resource?

I am new to Token Based authentication. With reference to below links, I am trying to understand Token Based authentication.
http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/
If the user credentials are valid, I am getting the desired token.
[AcceptVerbs("POST")]
[HttpPost]
public string Post([FromBody]User user)
{
if(user.Username == "hello" && user.Password == "123")
{
var accessTokenResponse = GenerateLocalAccessTokenResponse(user.Username);
return accessTokenResponse.ToString();
}
else
{
return "User invalid";
}
}
Generated token
TWC1Q2rrenZC2p78KPnS4JblcepCg6q3XuxqBQIh7L003npbb6hlBAOYGRN03OvY_O55GWFkZp7UfCmhCgH9Z4rBsjvIrp8gyCp4HmxpP4axVKk10NM9fiG2ctgZWeSbw1jNOor42Wk3yMufbs4xP0RlNuvdpLtBLir52g9rPF053kiJtYryNGzPsbibXHRrNoy0wOR2384uLAJ5pNE9s1DwYpdPKB9uOLSAGhDQOVU,
Now when I try to access the secured resources
[Authorize]
[HttpGet]
// GET api/orders/5
public string Get()
{
return "This is a secure resource";
}
I get "Access Denied Error".
How do I use the token to access such resources.
Any help/suggestion highly appreciated.
Thanks.
usually you would not implement the token endpoint as a POST method in your controller, but create a separate class (SimpleAuthorizationServerProvide) for it as shown in the above mentioned tutorial.
If everything is setup correctly, you have to add an Authorization header to your http request
Authorization: Bearer TWC1Q2rrenZC2p78KP...
and get a reply with status code 200(OK)
To get a token send a request (for example with the tool fiddler) to your token endpoint
e.g. if your service is running on localhost on port 52180 it looks like this:
POST http://localhost:52180/token
grant_type=password&username=admin&password=123&client_id=abc
the grant_type part is the request body.
When you post the above request, you'll reach the token endpoint. Just as Taiseer wrote in Step 12 of the tutorial.
When you put a breakpoint at GrantResourceOwnerCredentials that should be reached as soon as you sent the above request.
The usual flow is:
- client requests a token from http://localhost:52180/token
server authenticates user credentials in GrantResourceOwnerCredentials and issues a token
client reads the access_token from the token response
client adds an authorization header containing the access_token to a request
http://localhost:52180/api/orders
Authorization: Bearer TWC1Q2rrenZC2p78KP...
server reads Authorization header and grants access (if token is valid)
server processes request, eg, the GET request
client receives status 200 and desired data
The api controller shown above looks ok
The [Authorize] attribute is all you need in your controller. That adds an AuthorizationFilter to the http request pipeline, which handles the authorization for you when the client adds the above mentioned Authoriztion header to the request.

Why are my AJAX requests not extending an OWIN MVC session?

We have an ASP.NET MVC 5 application that has been using Forms Authentication with sliding expiration. We recently switched to OWIN Cookie Authentication and are experiencing issues with our sessions not extending properly.
Previously, a session could be extended from an AJAX xhr request. With this configuration, however, they are not extending. I'm receiving a 200 for every request (both GET and POST) that should be extending, even after the server has killed the session.
The current setup is:
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
UrlHelper url = new UrlHelper(HttpContext.Current.Request.RequestContext);
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = "Cookies",
CookieSecure = CookieSecureOption.SameAsRequest,
CookieName = Constants.CatalystPortalCookieName,
LoginPath = new PathString(url.Action(nameof(LoginController.Index), "Login")),
SlidingExpiration = true,
ExpireTimeSpan = TimeSpan.FromMinutes(20),
CookiePath = "/",
});
If I click a link that causes the entire page to be loaded as a document response, however, the server properly extends the session.
I ended up having some faulty assumptions. The AJAX requests were extending. The return of the 200 was throwing me off. After looking with fiddler at the actual response, I saw that with the change to OWIN, the 401 is actually moved in the response:
X-Responded-JSON: {"status":401,"headers":{...foo...}}
So, we ended up just setting the status of the response back to 401. That's probably horrible, but it fit our needs.
protected void Application_EndRequest() {
var context = new HttpContextWrapper(Context);
var header = context.Response.Headers["X-Responded-JSON"];
if (header != null && header.Contains("\"status\":401")) {
Context.Response.Clear();
Context.Response.StatusCode = 401;
}
Here's probably a more robust solution: OWIN: unauthorised webapi call returning login page rather than 401

User authentication when consuming a REST webservice with ServiceStack

The ServiceStack docs are full of examples on how to use server side implementation of authentication of a user. But how does one set the user credentials on the client side?
I use ServiceStack to consume a JSON REST service like this:
var restClient = new JsonServiceClient (baseUri);
var response = restClient.Get<MyResponse> ("/some/service");
How can I add any form of authentication to the request? The webservice I want to consume uses OAuth 1.0, but I am interested in adding custom authentication, too.
In my code, I have previously performed OAuth token exchange successfully, so I already own a valid access token and need to sign every REST request now using this access token and its token_secret.
ServiceStack's AuthTests shows different ways of authenticating when using the ServiceStack Service Clients. By default BasicAuth and DigestAuth is built into the clients, e.g:
var client = new JsonServiceClient(baseUri) {
UserName = UserName,
Password = Password,
};
var request = new Secured { Name = "test" };
var response = client.Send<SecureResponse>(request);
Behind the scenes ServiceStack will attempt to send the request normally but when the request is rejected and challenged by the Server the clients will automatically retry the same request but this time with the Basic/Digest Auth headers.
To skip the extra hop when you know you're accessing a secure service, you can tell the clients to always send the BasicAuth header with:
client.AlwaysSendBasicAuthHeader = true;
The alternative way to Authenticate is to make an explicit call to the Auth service (this requires CredentialsAuthProvider enabled) e.g:
var authResponse = client.Send<AuthResponse>(new Auth {
provider = CredentialsAuthProvider.Name,
UserName = "user",
Password = "p#55word",
RememberMe = true, //important tell client to retain permanent cookies
});
var request = new Secured { Name = "test" };
var response = client.Send<SecureResponse>(request);
After a successful call to the Auth service the client is Authenticated and if RememberMe is set, the client will retain the Session Cookies added by the Server on subsequent requests which is what enables future requests from that client to be authenticated.
Answering myself, as I've found a nice way to do it using the LocalHttpWebRequestFilter hook in the JsonServiceClient:
For securing a web service with OAuth 1.0a, every http request has to send a special Authorization: header. Within this header field, a hash (signature) must be send that uses some characteristics of the request as input data, like the hostname, request url and others.
Now it seems the LocalHttpWebRequestFilter is called by ServiceStack right before the http request is made, and exposes the underlying HttpWebRequest object, where one can add extra headers and access the required fields of the request.
So my solution is now basically:
var client = new JsonServiceClient (baseUri);
client.LocalHttpWebRequestFilter += (request) => {
// compute signature using request and a previously obtained
// access token
string authorization_header = CalculateSignature (request, access_token);
request.Headers.Add ("Authorization", authorization_header);
};
var response = client.Get<MySecuredResponse> ("/my/service");
Note that I use the Devdefined.OAuth library to do all the heavy stuff in CalculateSignature(). The creation of request token, obtaining user authorization, and exchanging the request token for access token as required by OAuth is done outside of ServiceStack, before the above service calls.

Categories