Programatically authenticate AzureAd/OpenId to an MVC controller using C# and get redirect uri - asp.net-mvc

I have overridden the built in WebClient as below. Then I call it
public class HttpWebClient : WebClient
{
private Uri _responseUri;
public Uri ResponseUri
{
get { return _responseUri; }
}
protected override WebResponse GetWebResponse(WebRequest request)
{
WebResponse response = base.GetWebResponse(request);
_responseUri = response.ResponseUri;
return response;
}
}
Then I consume it like this:
using (HttpWebClient client = new HttpWebClient())
{
client.Headers[HttpRequestHeader.Authorization] = $"Bearer { _token }";
client.Headers[HttpRequestHeader.ContentType] = "application/json";
client.UploadData(_url, Encoding.UTF8.GetBytes(_data));
string queryString = client.ResponseUri.Query.Split('=').Last();
}
The response uri comes back with "https://login.microsoftonline" rather than url returned from the MVC controller with a query string, as it is authenticating first with that bearer token using AzureAd/OpenId. If i call it twice it returns the original _url but not the redirected one. If I remove AzureAd authentication it works fine. Is there a way to force the response uri to come back as what the MVC controller sets it to?

Assuming you use the 'UseOpenIdConnectAuthentication' and configuring it to use AAD authentication, you can modify the redirect uri by setting Notifications.RedirectToIdentityProvider, something like:
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = async _ =>
{
_.ProtocolMessage.RedirectUri = _.Request.Uri.ToString();
}
}
If you use something else , or maybe I didn't understand your problem - please supply more information

Related

How to open page in browser with cookie?

For example, I have a web page (ASP.NET MVC), where I get cookie for some resource.
My MVC controller action code:
CookieContainer cookies = new CookieContainer();
HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = cookies;
string data = "<Request><MsgType>Authenticate</MsgType><SubMsgType>Login</SubMsgType><UserID>MYLOGIN</UserID><passwordNotEncrypted>MYPASSWORD</passwordNotEncrypted></Request>";
StringContent content = new StringContent(data, Encoding.UTF8, "text/xml");
HttpClient client = new HttpClient(handler);
Uri uri = new Uri("https://address/browserservices.aspx/login");
HttpResponseMessage response = client.PostAsync(uri, content).Result;
this request set auth cookie to cookies variable. And next request works fine:
var result = client.GetAsync("https://address/RemoteSupport.aspx?id=bla-bla-bla&pltFrmType=Android&agentversion=13.46").Result;
var text = result.Content.ReadAsStringAsync().Result;
(if I call it without cookie I get Unauthorized response)
Right now I want to do a redirect to this https://address/RemoteSupport.aspx?id=bla-bla-bla&pltFrmType=Android&agentversion=13.46 address with cookie. So, user should look at it redirected to this address. How to do it?
I tried:
foreach (Cookie cookie in responseCookies)
{
Response.Cookies.Append(cookie.Name, cookie.Value);
}
return Redirect($"https://address/RemoteSupport.aspx?id={id}&pltFrmType=Android&agentversion=13.46");
but it does not work
You can't just do a Redirect. In MVC that returns a 302 status code to the browser that instructs it to make it's own request to the 3rd party site.
Using the same instance of the HttpClient you need to make a second request to the 3rd party server. This should automatically add the cookie to the new request, and you can then format the answer and pass it on to your browser client.
Ideally you should not instantiate the HttpClient in the method, as this means that you have to make 2 requests each time. Perhaps something like this:
public class MyController : Controller
{
private static readonly HttpClient _httpClient;
public MyController()
{
if (_httpClient == null)
{
CookieContainer cookies = new CookieContainer();
HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = cookies;
_httpClient = new HttpClient(handler);
LoginWithClient();
}
}
private void LoginWithClient()
{
string data = "<Request><MsgType>Authenticate</MsgType><SubMsgType>Login</SubMsgType><UserID>MYLOGIN</UserID><passwordNotEncrypted>MYPASSWORD</passwordNotEncrypted></Request>";
StringContent content = new StringContent(data, Encoding.UTF8, "text/xml");
Uri uri = new Uri("https://address/browserservices.aspx/login");
HttpResponseMessage response = client.PostAsync(uri, content).Result;
// Maybe check the result here, but we should have the cookie by now
}
public JsonResult MyAction()
{
var result = client.GetAsync("https://address/RemoteSupport.aspx?id=bla-bla-bla&pltFrmType=Android&agentversion=13.46").Result;
var text = result.Content.ReadAsStringAsync().Result;
return Json(text, JsonRequestBehavior.AllowGet);
}
This should return the raw text string to the browser where you can display it, or parse it as Json or XML or whatever and work with the data.

#FrameworkEndpoint to oAuth 2.0 revokeToken

I have a #FrameworkEndpoint based controller in order to implement DELETE of token as follows
#FrameworkEndpoint
public class RevokeTokenEndpoint {
#Resource(name = "tokenServices")
ConsumerTokenServices tokenServices;
#RequestMapping(method = RequestMethod.DELETE, value = "/oauth/token")
#ResponseBody
public void revokeToken(HttpServletRequest request) {
String authorization = request.getHeader("Authorization");
if (authorization != null && authorization.contains("Bearer")) {
String tokenId = authorization.substring("Bearer".length() + 1);
System.out.println("tokenId : " + tokenId);
tokenServices.revokeToken(tokenId);
//tokenStore.removeRefreshToken(token);
}
}
}
My request is a DELETE (http://localhost:8081/oauth/token request with Authorization Bearer ce8b914d-57db-4ad7-86d9-be2d7f47b203
The problem is that the end-point does not get hit at all and the message returned back is "unauthorized". When I fire the request with Authorization Basic with client_id and secret then it does hit the end-point. But then in that case I will be forced to pass another parameter or header to carry the token and change the code to get the value of token from this another parameter or header.
I believe, the ideal way would be tell spring security to allow unauthorized calls to remove the access token. Does this make sense ? and if yes then how ?
I have implemented like this
#PostMapping("/token/revoke")
public ResponseEntity<Boolean> revokeToken(
#RequestHeader(value = "Authorization") final String tokenId) {
try {
final String[] token = tokenId.split("\\s+");
tokenServices.revokeToken(token[1]);
return new ResponseEntity<>(true, HttpStatus.OK);
} catch (final Exception e) {
LOGGER.info("Exception while removing access token");
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
tokenId contains Bearer b133b6ee-59db-4809-b546-e47cb879bea1. In this way i have implemented and its working fine

ServiceStack authentication request fails

I am trying to set up authentication with my ServiceStack service by following this tutorial.
My service is decorated with the [Authenticate] attribute.
My AppHost looks like this:
public class TestAppHost : AppHostHttpListenerBase
{
public TestAppHost() : base("TestService", typeof(TestService).Assembly) { }
public static void ConfigureAppHost(IAppHost host, Container container)
{
try
{
// Set JSON web services to return idiomatic JSON camelCase properties.
ServiceStack.Text.JsConfig.EmitCamelCaseNames = true;
// Configure the IOC container
IoC.Configure(container);
// Configure ServiceStack authentication to use our custom authentication providers.
var appSettings = new AppSettings();
host.Plugins.Add(new AuthFeature(() =>
new AuthUserSession(), // use ServiceStack's session class but fill it with our own data using our own auth service provider
new IAuthProvider[] {
new UserCredentialsAuthProvider(appSettings)
}));
}
}
where UserCredentialsAuthProvider is my custom credentials provider:
public class UserCredentialsAuthProvider : CredentialsAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
try
{
// Authenticate the user.
var userRepo = authService.TryResolve<IUserRepository>();
var user = userRepo.Authenticate(userName, password);
// Populate session properties.
var session = authService.GetSession();
session.IsAuthenticated = true;
session.CreatedAt = DateTime.UtcNow;
session.DisplayName = user.FullName;
session.UserAuthName = session.UserName = user.Username;
session.UserAuthId = user.ID.ToString();
}
catch (Exception ex)
{
// ... Log exception ...
return false;
}
return true;
}
}
In my user tests I initialize and start my TestAppHost on http://127.0.0.1:8888, then use JsonServiceClient to authenticate itself to the service like so:
var client = new JsonServiceClient("http://127.0.0.1:8888/")
var response = client.Send<AuthResponse>(new Auth
{
provider = UserCredentialsAuthProvider.Name,
UserName = username,
Password = password,
RememberMe = true
});
But getting the following exception:
The remote server returned an error: (400) Bad Request.
at System.Net.HttpWebRequest.GetResponse()
at ServiceStack.ServiceClient.Web.ServiceClientBase.Send[TResponse](Object request)...
The ServiceStack.ServiceInterface.Auth.Auth request contains the correct username and passsword, and the request is being posted to:
http://127.0.0.1:8888/json/syncreply/Auth
I am not sure why the URL is not /json/auth/credentials or what I might be doing wrong. Any suggestions?
UPDATE
Tracing the chain of events up the stack I found the following:
JsonDataContractSerializer.SerializeToStream correctly serializes the Auth request into Json. However, the System.Net.HttpRequestStream passed to JsonDataContractDeserializer by EndpointHandlerBase has a stream of the correct length that is filled with nulls (zero bytes). As a result, the request object passed to CredentialsAuthProvider.Authenticate has nulls in all its properties.
How can the HTTP stream get stripped of its data?
Got it!!!
The problem was the following pre-request filter that I added for logging purposes in TestAppHost.Configure:
PreRequestFilters.Add((httpReq, httpRes) =>
{
LastRequestBody = httpReq.GetRawBody();
});
as seen here.
When the GetRawBody() method reads the request InputStream it leaves it in the EOS state, and all subsequent read attempts return nothing.
So obviously GetRawBody() can only be safely used with buffered streams, but unfortunately it quietly causes a very nasty bug instead of throwing an exception when used with a non-buffered stream.

Post to Twitter from MVC

I saw couple of libraries which we can use to post to twitter. But I want to create my own as later we need to extend this for other social networks also .
I am using the RESTSharp to make things little easy for me.
In my controller, I wrote 2 methods..
public ActionResult TwitterLogin()
{
var authorizeUrl = TwitterService.Authorize();
if(!String.IsNullOrEmpty(authorizeUrl))
{
return Redirect(authorizeUrl);
}
else
{
return View();
}
}
public ActionResult AuthorizeCallback()
{
TwitterService.AuthorizeCallback();
return View();
}
In Twitter Service
public string Authorize()
{
client = new RestClient(BaseUrl) {Authenticator = OAuth1Authenticator.ForRequestToken(ConsumerKey, ConsumerSecret, CallbackUrl)};
var request = new RestRequest("oauth/request_token", Method.POST);
var response = client.Execute(request);
if(response.StatusCode == HttpStatusCode.OK)
{
var qs = HttpUtility.ParseQueryString(response.Content);
oauth_token = qs["oauth_token"];
oauth_token_secret = qs["oauth_token_secret"];
request = new RestRequest("oauth/authorize");
request.AddParameter("oauth_token", oauth_token);
return client.BuildUri(request).ToString();
}
return String.Empty;
}
public void AuthorizeCallback()
{
var verifier = "123456"; // <-- Breakpoint here (set verifier in debugger)
var request = new RestRequest("oauth/access_token", Method.POST);
client.Authenticator = OAuth1Authenticator.ForAccessToken(ConsumerKey, ConsumerSecret, oauth_token, oauth_token_secret, verifier);
var response = client.Execute(request);
var qs = HttpUtility.ParseQueryString(response.Content);
oauth_token = qs["oauth_token"];
oauth_token_secret = qs["oauth_token_secret"];
}
Now my concern is
Whether i am doing it right ?
Regarding Oauth, from what I understood , we create a request token from the twitter, ask the user to authorize it and use it to get an accesss Token and use it for signing other requests. I wrote this code primarly looking this link
https://github.com/restsharp/RestSharp/blob/master/RestSharp.IntegrationTests/oAuth1Tests.cs
Whether we can write this any better ?
Also somebody could guide me on this , how to use the RestSharp to create OAuth requests to use the API's like twitter. Most of the internet references are based on the custom libs

ASP.NET MVC - Using cURL or similar to perform requests in application

I'm building an application in ASP.NET MVC (using C#) and I would like to know how I can perform calls like curl http://www.mywebsite.com/clients_list.xml inside my controller
Basically I would like to build a kind of REST API to perform actions such as show edit and delete, such as Twitter API.
But unfortunately until now I didn't find anything besides that cURL for windows on this website: http://curl.haxx.se/
So I don't know if is there any traditional way to retrieve this kind of call from URL with methods like post delete and put on the requests, etc...
I just would like to know an easy way to perform commands like curl inside my controller on my ASP.NET MVC Application.
UPDATE:
Hi so I managed to make GET Requests but now I'm having a serious problem in retrieve POST Request for example, I'm using the update status API from Twitter that in curl would work like this:
curl -u user:password -d "status=playing with cURL and the Twitter API" http://twitter.com/statuses/update.xml
but on my ASP.NET MVC application I'm doing like this inside my custom function:
string responseText = String.Empty;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://twitter.com/statuses/update.xml");
request.Method = "POST";
request.Credentials = new NetworkCredential("username", "password");
request.Headers.Add("status", "Tweeting from ASP.NET MVC C#");
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (StreamReader sr = new StreamReader(response.GetResponseStream()))
{
responseText = sr.ReadToEnd();
}
return responseText;
Now the problem is that this request is returning 403 Forbidden,
I really don't know why if it works perfectly on curl
:\
UPDATE:
I finally manage to get it working, but probably there's a way to make it cleaner and beautiful, as I'm new on C# I'll need more knowledge to do it, the way the POST params are passed makes me very confused because is a lot of code to just pass params.
Well, I've created a Gist - http://gist.github.com/215900 , so everybody feel free to revise it as you will. Thanks for your help çağdaş
also follow the code here:
public string TwitterCurl()
{
//PREVENT RESPONSE 417 - EXPECTATION FAILED
System.Net.ServicePointManager.Expect100Continue = false;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://twitter.com/statuses/update.xml");
request.Method = "POST";
request.Credentials = new NetworkCredential("twitterUsername", "twitterPassword");
//DECLARE POST PARAMS
string headerVars = String.Format("status={0}", "Tweeting from ASP.NET MVC C#");
request.ContentLength = headerVars.Length;
//SEND INFORMATION
using (StreamWriter streamWriter = new StreamWriter(request.GetRequestStream(), ASCIIEncoding.ASCII))
{
streamWriter.Write(headerVars);
streamWriter.Close();
}
//RETRIEVE RESPONSE
string responseText = String.Empty;
using (StreamReader sr = new StreamReader(request.GetResponse().GetResponseStream()))
{
responseText = sr.ReadToEnd();
}
return responseText;
/*
//I'M NOT SURE WHAT THIS IS FOR
request.Timeout = 500000;
request.ContentType = "application/x-www-form-urlencoded";
request.UserAgent = "Custom Twitter Agent";
#if USE_PROXY
request.Proxy = new WebProxy("http://localhost:3000", false);
#endif
*/
}
Try using Microsoft.Http.HttpClient. This is what your request would look like
var client = new HttpClient();
client.DefaultHeaders.Authorization = Credential.CreateBasic("username","password");
var form = new HttpUrlEncodedForm();
form.Add("status","Test tweet using Microsoft.Http.HttpClient");
var content = form.CreateHttpContent();
var resp = client.Post("http://www.twitter.com/statuses/update.xml", content);
string result = resp.Content.ReadAsString();
You can find this library and its source included in the WCF REST Starter kit Preview 2, however it can be used independently of the rest of the stuff in there.
P.S. I tested this code on my twitter account and it works.
Example code using HttpWebRequest and HttpWebResponse :
public string GetResponseText(string url) {
string responseText = String.Empty;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (StreamReader sr = new StreamReader(response.GetResponseStream())) {
responseText = sr.ReadToEnd();
}
return responseText;
}
To POST data :
public string GetResponseText(string url, string postData) {
string responseText = String.Empty;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentLength = postData.Length;
using (StreamWriter sw = new StreamWriter(request.GetRequestStream())) {
sw.Write(postData);
}
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (StreamReader sr = new StreamReader(response.GetResponseStream())) {
responseText = sr.ReadToEnd();
}
return responseText;
}
This is the single line of code I use for calls to a RESTful API that returns JSON.
return ((dynamic) JsonConvert.DeserializeObject<ExpandoObject>(
new WebClient().DownloadString(
GetUri(surveyId))
)).data;
Notes
The Uri is generated off stage using the surveyId and credentials
The 'data' property is part of the de-serialized JSON object returned
by the SurveyGizmo API
The Complete Service
public static class SurveyGizmoService
{
public static string UserName { get { return WebConfigurationManager.AppSettings["SurveyGizmo.UserName"]; } }
public static string Password { get { return WebConfigurationManager.AppSettings["SurveyGizmo.Password"]; } }
public static string ApiUri { get { return WebConfigurationManager.AppSettings["SurveyGizmo.ApiUri"]; } }
public static string SurveyId { get { return WebConfigurationManager.AppSettings["SurveyGizmo.Survey"]; } }
public static dynamic GetSurvey(string surveyId = null)
{
return ((dynamic) JsonConvert.DeserializeObject<ExpandoObject>(
new WebClient().DownloadString(
GetUri(surveyId))
)).data;
}
private static Uri GetUri(string surveyId = null)
{
if (surveyId == null) surveyId = SurveyId;
return new UriBuilder(ApiUri)
{
Path = "/head/survey/" + surveyId,
Query = String.Format("user:pass={0}:{1}", UserName, Password)
}.Uri;
}
}
Look into the System.Net.WebClient class. It should offer the functionality you require. For finer grained control, you might find WebRequest to be more useful, but WebClient seems the best fit for your needs.

Resources