Reverse proxy in .NET Core Middleware “set-cookie” response does not set in browser and not showing in HttpResponseMessage - asp.net

Here I am making a reverse proxy server to bypass an ASP.NET web application (following this tutorial). I am trying to read the session ID cookie from HttpResponseMessage. I used a cookie container as well but am unable to find it. Implemented in ASP.NET core invoke method, session is working properly but unable to catch session ID in request or response.
public async Task Invoke(HttpContext context, IBrowserDetector detector)
{
//context.Session.SetString(SessionKeyName, "The Doctor");
var browser = detector.Browser;
var targetUri = BuildTargetUri(context.Request);
if (context.Request.Method != HttpMethod.Get.Method)
{
var remoteIp = context.Connection.RemoteIpAddress;
//var gg= context.Request.Headers.ContainsKey.;
var clienttdatetime = context.Request.Headers["Date"].ToString();
//_logger.LogDebug("Request from Remote IP address: {RemoteIp}", remoteIp);
var badIp = true;
var bytes = remoteIp.GetAddressBytes();
//var testIp = IPAddress.Parse(address);
//if (testIp.GetAddressBytes().SequenceEqual(bytes))
//{
// badIp = false;
// break;
//}
if (remoteIp.IsIPv4MappedToIPv6)
{
remoteIp = remoteIp.MapToIPv4();
}
IPAddress remoteIpAddress = context.Request.HttpContext.Connection.RemoteIpAddress;
string result = "";
if (remoteIpAddress != null)
{
// If we got an IPV6 address, then we need to ask the network for the IPV4 address
// This usually only happens when the browser is on the same machine as the server.
if (remoteIpAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
{
remoteIpAddress = System.Net.Dns.GetHostEntry(remoteIpAddress).AddressList
.First(x => x.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);
}
result = remoteIpAddress.ToString();
}
if (badIp)
{
//_logger.LogWarning(
// "Forbidden Request from Remote IP address: {RemoteIp}", remoteIp);
//context.Response.StatusCode = StatusCodes.Status403Forbidden;
//return;
}
}
if (targetUri != null)
{
CookieContainer cookies = new CookieContainer();
HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = cookies;
var targetRequestMessage = CreateTargetMessage(context, targetUri);
using (var responseMessage = await _httpClient.SendAsync(targetRequestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted))
{
IEnumerable<Cookie> responseCookies = cookies.GetCookies(targetUri).Cast<Cookie>();
foreach (Cookie cookie_ in responseCookies)
Console.WriteLine(cookie_.Name + ": " + cookie_.Value);
// ExtractCookiesFromResponse(responseMessage);
context.Response.StatusCode = (int)responseMessage.StatusCode;
CopyFromTargetResponseHeaders(context, responseMessage);
await responseMessage.Content.CopyToAsync(context.Response.Body);
//if(responseMessage.RequestMessage.RequestUri.ToString()== "http://localhost:51125/Menu.aspx")
//{
//Uri uri = new Uri("http://localhost:5000/login.aspx");
//Build the request
//Uri site = targetUri;
// HttpWebRequest request = (HttpWebRequest)WebRequest.Create(site);
// CookieContainer cookiesq = new CookieContainer();
// request.CookieContainer = cookiesq;
// //Print out the number of cookies before the response (of course it will be blank)
// Console.WriteLine(cookiesq.GetCookieHeader(site),"1");
// //Get the response and print out the cookies again
// using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
// {
// Console.WriteLine(cookiesq.GetCookieHeader(site), "2");
// }
// Console.ReadKey();
//}
var cookie = context.Request.Cookies["ASP.NET_SessionId"];
}
return;
}
await _nextMiddleware(context);
}
------------------------------------------------------------------------------------
public static IDictionary<string, string> ExtractCookiesFromResponse(HttpResponseMessage response)
{
IDictionary<string, string> result = new Dictionary<string, string>();
IEnumerable<string> values;
if (response.Headers.TryGetValues("Set-Cookie", out values))
{
SetCookieHeaderValue.ParseList(values.ToList()).ToList().ForEach(cookie =>
{
result.Add(cookie.Name.ToString(), cookie.Value.ToString());
});
}
return result;
}

As far as I can see, you created the HttpClientHandler but didn't use it to build the HttpClient to make your request. You are still using the static _httpClient that nothing knows about the cookie container you created.
This should be the reason why you get the CookieContainer still empty.
Take a look here to learn how to get cookies from an HttpResponseMessage.

CookieContainer cookies = new CookieContainer();
HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = cookies;
_httpClient = new HttpClient(handler);
var targetRequestMessage = CreateTargetMessage(context, targetUri);
using (var responseMessage = await _httpClient.SendAsync(targetRequestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted))
{
//var responseCookies = cookies.GetCookies(targetUri).Cast<Cookie>();
IEnumerable<Cookie> responseCookies = cookies.GetCookies(targetUri).Cast<Cookie>();
foreach (Cookie cookie in responseCookies)
{
if(cookie.Name=="ASP.NET_SessionId")
{
Console.WriteLine(cookie.Name + ": " + cookie.Value);
context.Response.Headers.Add("Set-Cookie", cookie.Name+"="+cookie.Value);
}
}

Related

How can I make proper request for the Identity Server Token Endpoint?

I tried to send an api call to the identity server via .net 6 console application.
Here is the request:
public static async Task<WorkflowResponse> PostRequestToIdentityAsync()
{
var url = "http://didentity/connect/token";
IdentityRequestDataVM identityRequestDataVM = new IdentityRequestDataVM();
identityRequestDataVM.username = "csm";
identityRequestDataVM.password = "MjAyMjox";
identityRequestDataVM.grant_type = "password";
identityRequestDataVM.scope = "m_gln m_msd";
string jsonString = JsonConvert.SerializeObject(identityRequestDataVM);
using (var httpClient = new HttpClient())
{
var request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(url),
Content = new StringContent(jsonString),
Headers =
{
{"X-Login","override"}
}
};
var user = "gclt";
var password = "glsrt";
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes($"{user}:{password}")));
HttpResponseMessage message = await httpClient.SendAsync(request);
if (message.StatusCode == System.Net.HttpStatusCode.OK)
{
var contents = await message.Content.ReadAsStringAsync();
WorkflowResponse workflowResponse = JsonConvert.DeserializeObject<WorkflowResponse>(contents);
return workflowResponse;
}
else
{
throw new Exception(await message.Content.ReadAsStringAsync());
}
}
}
But, it returned 400 err code (Bad request), is there any mistake in the code snippet ?
It is working fine with postman.

Xamarin Http Request Timeout Issue

I have a mobile application based on Xamarin and a Web API based on .Net Core. Mobile app consumes methods of Web API via HttpClient. The code below is my base method to call any Web API method and the point is I want to set a timeout but could not achieved to set the exact timeout value whatever I have implemented. Tried Timespan.FromSeconds() or TimeSpan.FromMilliseconds() etc. When client makes a request to Web API, a loader is displayed to lock UI and removed after API response. Some clients gave me a feedback that the loader is displayed forever and request never ends. Maybe, the server is unreachable in this particular time or internet connection is broken for client etc. All I want to set a timeout and break the request and display an alert message to client. Of course, I googled and tried too much as mentioned but no result. If anyone can help me, will be appreciated.
public async Task<BaseResponseModel> Post(BasePostModel postModel)
{
var responseModel = new BaseResponseModel();
var json = postModel.ToString();
var jsonParam = new StringContent(json, Encoding.UTF8, "application/json");
var isPosted = true;
var clientHandler = new HttpClientHandler()
{
AllowAutoRedirect = true,
};
var url = GetURL(postModel.UrlKey);
var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore,
ContractResolver = new DefaultContractResolver(),
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
var client = new HttpClient(clientHandler);
//client.Timeout = TimeSpan.FromSeconds(10);
//var cancellationTokenSource = new CancellationTokenSource();
//cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(10));
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("X-Env", "MOBILE_API");
AttachToken(ref client, responseModel.Id);
try
{
if (Preferences.ContainsKey("UserJwtExprieDate"))
{
var expiryDate = Preferences.Get("UserJwtExprieDate", null);
if (DateTime.Now > DateTime.Parse(expiryDate))
{
Preferences.Remove("UserJwtExprieDate");
Preferences.Remove("HomePageInformation");
int index = Xamarin.Forms.Application.Current.MainPage.Navigation.NavigationStack.Count - 1;
Page currPage = Xamarin.Forms.Application.Current.MainPage.Navigation.NavigationStack[index];
if (currPage as SigninForFactorOne != null)
{}
else
{
App.LogoutUser();
}
}
else
{
var response = await client.PostAsync(url, jsonParam);
if (response.IsSuccessStatusCode)
{
string result = response.Content.ReadAsStringAsync().Result;
var resultModel = JsonConvert.DeserializeObject<BaseResponseModel>(result, settings);
if (resultModel.ErrorType == APIErrorTypes.NULL)
{
if (resultModel.IsSucceed)
{
responseModel.Data = resultModel.Data;
}
else
{
responseModel.Error = resultModel.Error;
}
responseModel.Message = resultModel.Message;
}
else
{
responseModel.Error = "Token Expried Date.";
Preferences.Remove("UserJwtExprieDate");
Preferences.Remove("HomePageInformation");
App.LogoutUser();
}
}
else
{
new AppException(new Exception("HTTP Client response is not succeed!"), responseModel.Id);
isPosted = false;
}
}
}
else
{
var response = await client.PostAsync(url, jsonParam);
if (response.IsSuccessStatusCode)
{
string result = response.Content.ReadAsStringAsync().Result;
var resultModel = JsonConvert.DeserializeObject<BaseResponseModel>(result, settings);
if (resultModel.ErrorType == APIErrorTypes.NULL)
{
if (resultModel.IsSucceed)
{
responseModel.Data = resultModel.Data;
}
else
{
responseModel.Error = resultModel.Error;
}
responseModel.Message = resultModel.Message;
}
else
{
responseModel.Error = "Token Expried Date.";
Preferences.Remove("UserJwtExprieDate");
Preferences.Remove("HomePageInformation");
App.LogoutUser();
}
}
else
{
new AppException(new Exception("HTTP Client response is not succeed!"), responseModel.Id);
isPosted = false;
}
}
}
catch (Exception ex)
{
new AppException(ex, responseModel.Id, 500, "anonymous.user", "Unable to post data to API!");
isPosted = false;
}
finally
{
if (!isPosted)
{
responseModel.Error = AppConfiguration.GetSystemMessage(contactYourSystemAdministratorMessage);
responseModel.Message = AppConfiguration.GetSystemMessage(contactYourSystemAdministratorMessage);
}
}
return responseModel;
}
I've used the solution below to manually set a time-out which works fine.
internal class TimeOutHandler : DelegatingHandler
{
private readonly TimeSpan TimeOut;
public TimeOutHandler(TimeSpan timeOut) => TimeOut = timeOut;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage req, CancellationToken ct)
{
using (var ctTimeOut = CancellationTokenSource.CreateLinkedTokenSource(ct))
{
ctTimeOut.CancelAfter(TimeOut);
try
{
return await base.SendAsync(req, ctTimeOut.Token);
}
catch (OperationCanceledException) when (!ct.IsCancellationRequested)
{
throw new TimeoutException();
}
}
}
}
How to use
var interval = TimeSpan.FromSeconds(10);
var handler = new TimeOutHandler(interval)
{
InnerHandler = new HttpClientHandler()
};
var client = new HttpClient(handler);
For more information, check out: https://thomaslevesque.com/2018/02/25/better-timeout-handling-with-httpclient/

.NET Core reverse proxy middleware and signalR

I need to do a reverse proxy middleware in ASP.NET Core. I have something like this. But it doesn't work with SignalR.
Reverse proxy has address 192.168.187.72:5000; SignalR 192.168.187.62:5000. Client connects to proxy, proxy changes the url to SignalR hub url, but in client I always get this error:
The server disconnected before the handshake could be started
Code:
public ReverseProxyMiddleware(RequestDelegate nextMiddleware,ILogger<ReverseProxyMiddleware> logger)
{
_nextMiddleware = nextMiddleware;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
var targetUri = BuildTargetUri(context.Request);
if (targetUri != null)
{
var targetRequestMessage = CreateTargetMessage(context, targetUri);
using (var responseMessage = await _httpClient.SendAsync(targetRequestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted))
{
context.Response.StatusCode = (int)responseMessage.StatusCode;
CopyFromTargetResponseHeaders(context, responseMessage);
await responseMessage.Content.CopyToAsync(context.Response.Body);
}
return;
}
await _nextMiddleware(context);
}
private HttpRequestMessage CreateTargetMessage(HttpContext context, Uri targetUri)
{
var requestMessage = new HttpRequestMessage();
CopyFromOriginalRequestContentAndHeaders(context, requestMessage);
requestMessage.RequestUri = targetUri;
requestMessage.Headers.Host = targetUri.Host;
requestMessage.Method = GetMethod(context.Request.Method);
return requestMessage;
}
private void CopyFromOriginalRequestContentAndHeaders(HttpContext context, HttpRequestMessage requestMessage)
{
var requestMethod = context.Request.Method;
var streamContent = new StreamContent(context.Request.Body);
requestMessage.Content = streamContent;
foreach (var header in context.Request.Headers)
{
requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
}
}
private void CopyFromTargetResponseHeaders(HttpContext context, HttpResponseMessage responseMessage)
{
foreach (var header in responseMessage.Headers)
{
context.Response.Headers[header.Key] = header.Value.ToArray();
}
foreach (var header in responseMessage.Content.Headers)
{
context.Response.Headers[header.Key] = header.Value.ToArray();
}
context.Response.Headers.Remove("transfer-encoding");
}
private static HttpMethod GetMethod(string method)
{
if (HttpMethods.IsDelete(method)) return HttpMethod.Delete;
if (HttpMethods.IsGet(method)) return HttpMethod.Get;
if (HttpMethods.IsHead(method)) return HttpMethod.Head;
if (HttpMethods.IsOptions(method)) return HttpMethod.Options;
if (HttpMethods.IsPost(method)) return HttpMethod.Post;
if (HttpMethods.IsPut(method)) return HttpMethod.Put;
if (HttpMethods.IsTrace(method)) return HttpMethod.Trace;
return new HttpMethod(method);
}
private Uri BuildTargetUri(HttpRequest request)
{
Uri targetUri = null;
StringValues clientId = string.Empty;
targetUri = new Uri($"http://192.168.187.62:5000{request.Path}{request.QueryString}");
_logger.LogInformation($"Request URL {request.Host + request.Path + request.QueryString} Target URL {targetUri.ToString()}");
return targetUri;
}
Are you sure you want to create such functionality manually?
There are many great libraries for reverse proxying, Microsoft has a new project called YARP (https://microsoft.github.io/reverse-proxy/) which is an open source reverse proxy told to be high performance and highly customizable.
If you like I can show you a sample how you can use yarp in an ASP.NET Core app.

Xamarin Forms HttpClient Multipart/FormData returns 403 Forbidden

I'm trying to make chat app with XamarinForms and I'm trying to upload a file with parameters to server. But I'm getting always 403 Forbidden message. (There is no authentication, there is only token key for now).
If I try to get or send any data to server, it works as well. When I try to send a file with data it returns 403 Forbidden message. I also tried to send same data with Postman. it's worked as well. I'm writing part of code, Could you please tell me, I made it wrong where?
Thanks in advance.
private async Task<HttpClient> GetClient()
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("X-Token-Key", ServiceToken);
client.DefaultRequestHeaders.UserAgent.Add(new System.Net.Http.Headers.ProductInfoHeaderValue("Chrome", "41.0.2228.0"));
return client;
}
If I send text message, it works as well.
public async Task<MobileResult> SendConversationTextMessage(MessageModel message)
{
HttpClient client = await GetClient();
string param = JsonConvert.SerializeObject(message);
var response = await client.PostAsync(Url + "conversation/message_add_text", new StringContent(param, Encoding.UTF8, "application/json"));
var mobileResult = JsonConvert.DeserializeObject<MobileResult>(await response.Content.ReadAsStringAsync());
return mobileResult;
}
If I send message with data, it returns 403 Forbidden
public async Task<MobileResult> SendConversationFileMessage(
FileModel FileMessage,
int UserRemoteId,
int ConversationId,
int ToUserId,
string SendedTime,
MessageModel.MessageType Type,
MessageModel.MessageStatus Status,
string MessageType)
{
HttpClient client = await GetClient();
string PostUrl = Url + "conversation/message_add_" + MessageType;
MultipartFormDataContent content = new MultipartFormDataContent();
ByteArrayContent baContent = new ByteArrayContent(FileMessage.BinaryData);
StringContent UserIdContent = new StringContent(UserRemoteId.ToString());
StringContent ConversationIdContent = new StringContent(ConversationId.ToString());
StringContent ToUserIdContent = new StringContent(ToUserId.ToString());
StringContent SendedTimeContent = new StringContent(SendedTime.ToString());
StringContent TypeContent = new StringContent(Type.ToString());
StringContent StatusContent = new StringContent(Status.ToString());
content.Add(baContent, "AttachedFile", FileMessage.Name);
content.Add(UserIdContent, "serId");
content.Add(ConversationIdContent, "ConversationId");
content.Add(ToUserIdContent, "ToUserId");
content.Add(SendedTimeContent, "SendedTime");
content.Add(TypeContent, "Type");
content.Add(StatusContent, "Status");
try
{
var response = await client.PostAsync(PostUrl, content);
string result = await response.Content.ReadAsStringAsync();
var mobileResult = JsonConvert.DeserializeObject<MobileResult>(result);
return mobileResult;
}
catch (Exception e)
{
return new MobileResult
{
Result = false,
Data = null,
Message = e.ToString()
};
}
}
Postman-Screenshot
Edit: I've tested to send multipart/form-data different way but result is same I'm writing below code:
MultipartFormDataContent content = new MultipartFormDataContent();
var UserIdContent = new StringContent(UserId.ToString());
UserIdContent.Headers.Add("Content-Disposition", "form-data; name=\"UserId\"");
UserIdContent.Headers.Remove("Content-Type");
content.Add(UserIdContent, "UserId");
var ConversationIdContent = new StringContent(ConversationId.ToString());
ConversationIdContent.Headers.Add("Content-Disposition", "form-data; name=\"ConversationId\"");
ConversationIdContent.Headers.Remove("Content-Type");
content.Add(ConversationIdContent, "ConversationId");
var ToUserIdContent = new StringContent(ToUserId.ToString());
ToUserIdContent.Headers.Add("Content-Disposition", "form-data; name=\"ToUserId\"");
ToUserIdContent.Headers.Remove("Content-Type");
content.Add(ToUserIdContent, "ToUserId");
var SendedTimeContent = new StringContent(SendedTime.ToString());
SendedTimeContent.Headers.Add("Content-Disposition", "form-data; name=\"SendedTime\"");
SendedTimeContent.Headers.Remove("Content-Type");
content.Add(SendedTimeContent, "SendedTime");
var TypeContent = new StringContent(Type.ToString());
TypeContent.Headers.Add("Content-Disposition", "form-data; name=\"Type\"");
TypeContent.Headers.Remove("Content-Type");
content.Add(TypeContent, "Type");
var StatusContent = new StringContent(Status.ToString());
StatusContent.Headers.Add("Content-Disposition", "form-data; name=\"Status\"");
StatusContent.Headers.Remove("Content-Type");
content.Add(StatusContent, "Status");
var streamContent = new StreamContent(file.InputStream);
streamContent.Headers.Add("Content-Disposition", "form-data; name=\"AttachedFile\"; filename=\"" + file.FileName + "\"");
streamContent.Headers.Add("Content-Type", "video/mp4");
content.Add(streamContent, "AttachedFile", file.FileName);

Alexa skill and Azure AD authentication

I am trying to build an alexa skill that connects to an enterprise app that uses Azure AD authentication. We set everything up like in this article https://blogs.msdn.microsoft.com/premier_developer/2016/12/12/amazon-alexa-skills-development-with-azure-active-directory-and-asp-net-core-1-0-web-api/, but I am having problems with moving the token from the body of the request to the headers.
The request comes through fine, I can parse it, I add the token to the header but on the test page in amazon I get this message: "There was an error calling the remote endpoint, which returned HTTP 302 : Found"
This is the code for adding the token to the headers:
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.Use(async (context, next) =>
{
string path = string.Format(ConfigurationManager.AppSettings["ExchangeTraceDrivePath"], "AlexaRequest", DateTime.Now.ToFileTime(), "");
var stream = context.Request.Body;
try
{
using (var buffer = new MemoryStream())
{
await stream.CopyToAsync(buffer);
var bodyBuffer = new byte[buffer.Length];
buffer.Position = 0L;
buffer.Read(bodyBuffer, 0, bodyBuffer.Length);
var body = Encoding.UTF8.GetString(bodyBuffer);
using (var sw = new StreamWriter(path))
{
sw.WriteLine(DateTime.Now.ToString() + " body: " + body);
sw.WriteLine("---------------------------------------------------------------------------------------------");
foreach (var header in context.Request.Headers)
{
sw.WriteLine(DateTime.Now.ToString() + " header key: " + header.Key);
foreach (var val in header.Value)
{
sw.WriteLine(DateTime.Now.ToString() + " header value: " + val);
}
}
sw.WriteLine("---------------------------------------------------------------------------------------------");
dynamic json = JObject.Parse(body);
sw.WriteLine(DateTime.Now.ToString() + " parsed body: " + json);
sw.WriteLine("---------------------------------------------------------------------------------------------");
if (json?.session?.user?.accessToken != null)
{
sw.WriteLine(DateTime.Now.ToString() + " access accessToken found " +
json?.session?.user?.accessToken);
sw.WriteLine("---------------------------------------------------------------------------------------------");
context.Request.Headers.Add("Authorization",
new string[] { string.Format("Bearer {0}", json?.session?.user?.accessToken) });
foreach (var header in context.Request.Headers)
{
sw.WriteLine(DateTime.Now.ToString() + " header key: " + header.Key);
foreach (var val in header.Value)
{
sw.WriteLine(DateTime.Now.ToString() + " header value: " + val);
}
}
sw.WriteLine("---------------------------------------------------------------------------------------------");
}
buffer.Position = 0L;
context.Request.Body = buffer;
}
}
}
catch
{
}
finally
{
await next.Invoke();
// Restore the original stream.
context.Request.Body = stream;
}
});
//ExpireTimeSpan and SlidinExpiration only work when UseTokenLifetime = false
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
// This is NOT ASP.NET Session Timeout (that should be set to same value in web.config)
// This is the expiration on the cookie that holds the Azure AD token
ExpireTimeSpan = TimeSpan.FromMinutes(Convert.ToDouble(expirationTimeSpan)),
// Set SlidingExpiration=true to instruct the middleware to re-issue a new cookie
// with a new expiration time any time it processes a request which is more than
// halfway through the expiration window.
SlidingExpiration = true,
Provider = new CookieAuthenticationProvider
{
// This method is called every time the cookie is authenticated, which
// is every time a request is made to the web app
OnValidateIdentity = CookieAuthNotification.OnValidateIdentity
}
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
UseTokenLifetime = false,
/*
* Skipping the Home Realm Discovery Page in Azure AD
* http://www.cloudidentity.com/blog/2014/11/17/skipping-the-home-realm-discovery-page-in-azure-ad/
*/
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = OpenIdConnectNotification.RedirectToIdentityProvider,
MessageReceived = OpenIdConnectNotification.MessageReceived,
SecurityTokenReceived = OpenIdConnectNotification.SecurityTokenReceived,
SecurityTokenValidated = OpenIdConnectNotification.SecurityTokenValidated,
AuthorizationCodeReceived = OpenIdConnectNotification.AuthorizationCodeReceived,
AuthenticationFailed = OpenIdConnectNotification.AuthenticationFailed
},
});
}
I ended up creating a separate middleware to move the token from the body to the header
public class AlexaJWTMiddleware : OwinMiddleware
{
private readonly OwinMiddleware _next;
public AlexaJWTMiddleware(OwinMiddleware next) : base(next)
{
_next = next;
}
public override Task Invoke(IOwinContext context)
{
var stream = context.Request.Body;
if (context.Request.Headers.ContainsKey("SignatureCertChainUrl")
&& context.Request.Headers["SignatureCertChainUrl"]
.Contains("https://s3.amazonaws.com/echo.api/echo-api-cert-4.pem")
&& !context.Request.Headers.ContainsKey("Authorization"))
{
try
{
using (var buffer = new MemoryStream())
{
stream.CopyToAsync(buffer);
var bodyBuffer = new byte[buffer.Length];
buffer.Position = 0L;
buffer.Read(bodyBuffer, 0, bodyBuffer.Length);
var body = Encoding.UTF8.GetString(bodyBuffer);
dynamic json = JObject.Parse(body);
if (json?.session?.user?.accessToken != null)
{
context.Request.Headers.Add("Authorization",
new string[] { string.Format("Bearer {0}", json?.session?.user?.accessToken) });
}
buffer.Position = 0L;
context.Request.Body = buffer;
}
}
catch
{
}
finally
{
// Restore the original stream.
context.Request.Body = stream;
}
}
else
{
return _next.Invoke(context);
}
return _next.Invoke(context);
}
}
and then adding jwt authentication besides the openId one
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.Use(typeof(AlexaJWTMiddleware));
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = domain,
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:AppIdUri"]
},
AuthenticationType = "OAuth2Bearer",
});
//ExpireTimeSpan and SlidinExpiration only work when UseTokenLifetime = false
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
// This is NOT ASP.NET Session Timeout (that should be set to same value in web.config)
// This is the expiration on the cookie that holds the Azure AD token
ExpireTimeSpan = TimeSpan.FromMinutes(Convert.ToDouble(expirationTimeSpan)),
// Set SlidingExpiration=true to instruct the middleware to re-issue a new cookie
// with a new expiration time any time it processes a request which is more than
// halfway through the expiration window.
SlidingExpiration = true,
Provider = new CookieAuthenticationProvider
{
// This method is called every time the cookie is authenticated, which
// is every time a request is made to the web app
OnValidateIdentity = CookieAuthNotification.OnValidateIdentity
}
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
UseTokenLifetime = false,
/*
* Skipping the Home Realm Discovery Page in Azure AD
* http://www.cloudidentity.com/blog/2014/11/17/skipping-the-home-realm-discovery-page-in-azure-ad/
*/
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = OpenIdConnectNotification.RedirectToIdentityProvider,
MessageReceived = OpenIdConnectNotification.MessageReceived,
SecurityTokenReceived = OpenIdConnectNotification.SecurityTokenReceived,
SecurityTokenValidated = OpenIdConnectNotification.SecurityTokenValidated,
AuthorizationCodeReceived = OpenIdConnectNotification.AuthorizationCodeReceived,
AuthenticationFailed = OpenIdConnectNotification.AuthenticationFailed
},
});
}

Resources