How to consume a secure Rest MVC web api - asp.net

I'm just a beginner on the .NET world and I've created a web api (.NET 4.5.2) and I'm using the annotation [Authorize] above my controllers like shown below:
[Authorize]
public class PhasesController : ApiController
{
private TestReportEntities db = new TestReportEntities();
// GET: api/Phases
public IQueryable<Phase> GetPhase()
{
return db.Phase;
}
}
I've already created my DB and I'm using the default tables that the web.api uses to manage the access, as you can see on this image:
My tables
I've already done a method to request to my web api, in another project/solution, it's working fine when I remove the annotation [Authorize] from my web api controllers.
this is an example about how I'm requesting my api:
public int GetCurrentIdPhase(int idProject)
{
int phaseId = -1;
WebRequest request = WebRequest.Create(string.Concat(URL, string.Format("api/phases/?idProject={0}", idProject)));
using (var resp = (HttpWebResponse)request.GetResponse())
{
using (var reader = new StreamReader(resp.GetResponseStream()))
{
string objText = reader.ReadToEnd();
var phase = JsonConvert.DeserializeObject<List<Phase>>(objText);
phaseId = phase[0].id;
}
}
if (phaseId != -1)
{
return phaseId;
}
else
{
throw new Exception("Phase not found");
}
}
At the end of the day my questions are:
How can I request a token to my api (POST - www.myApi/token) using the example above?
How can I use the token, once I've got it, on every request to my API?
if you can help me I would really appreciate it.
Thanks.

I've created a method to get the Token from my Web API, this is the method:
var request = (HttpWebRequest)WebRequest.Create(string.Concat(URL, "token"));
var postData = "grant_type=password";
postData += string.Format("&userName={0}", user);
postData += string.Format("&password={0}", pass);
var data = Encoding.ASCII.GetBytes(postData);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
using (var stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
var response = (HttpWebResponse)request.GetResponse();
string objText = new StreamReader(response.GetResponseStream()).ReadToEnd();
var requestedToken = (JObject)JsonConvert.DeserializeObject(objText);
token = string.Concat(token, requestedToken["access_token"].Value<string>());
And to request something to my API all I need to do is just add the token on the header of all requests like shown on the line below:
request.Headers.Add(HttpRequestHeader.Authorization, getToke());
Hope it can help someone else who is beginning to work with .NET web API like me.
Regards.

Im assuming the "GetCurrentIdPhase" call is from an unrelated app with unrealted auth - if any auth.
The difficulty here is in using Authorize and the traidtional browser authentication flow. Here's an example of changing the pipeline a bit to use a different auth form for using console/desktop apps. You don't say where you are calling GetCurrentIdPhase from so I'll have to assume either a separate app. If its a web app and you are authenticated using the same tables, then you will have to share the token between them using for ex. the url blackice provided above.
If the app is a desktop/console/etc (not another app that the user had to auth against the same tables) then you can try this approach to change how auth is done to make it easier to access.
MVC WebAPI authentication from Windows Forms

Related

WebRequest is forbidden on Sharepoint Online

I need to create web request to specific page in SharePoint Online using Access Token in order to get certain Header information, but I keep getting 403 and I'm not sure why. After toying with access requests some more, I managed to only get 403 using clientContext, and 401 using Postman.
I can get response from grah api in my app and with postman using access token, but the problem is I need to do a web request to get real web page headers like SpRequestDuration and SPIISLatency.
I've followed steps to create my Azure AD and application.
I request user login to get authentication code for my application
loginScopes = [
'User.Read.All',
'Directory.Read.All',
'Group.Read.All',
'Sites.Read.All',
'Reports.Read.All'
'offline_access',
'https://www.sharepoint.com/AllSites.FullControl'
];
const encodedScopes = encodeURIComponent(loginScopes.join(' '));
const encodedRedirectUri = encodeURIComponent(redirectUri);
let url = `https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize?
response_type=code&
client_id=${clientId}
&redirect_uri=${encodedRedirectUri}
&scope=${encodedScopes}
&prompt=select_account`;
After this returns authentication code I create ConfidentialClientApplication with authority : 'https://login.microsoftonline.com/organizations' and with my application clientId, secret, redirect etc. With newly created ConfidentialClientApplication I acquire token silently with scope '{tenantUrl}/AllSites.FullControl'and another token with other scopes.
Now with access token I successfully create ClientContext, which retrieves data from sharepoint site and I can easily make a graph API request and everything will be fine. But the problem is with creating a WebRequest to a specific site (example. {tenantUrl}/SitePages/Forms/ByAuthor.aspx)
var manager = new AuthenticationManager();
var clientContext = manager.GetAzureADAccessTokenAuthenticatedContext(tenantUrl, accessToken);
clientContext.Load(clientContext.Site);
clientContext.Load(oWebsite.Lists);
clientContext.ExecuteQuery(); // Works fine
var request = clientContext.WebRequestExecutorFactory.CreateWebRequestExecutor(clientContext,{tenantUrl}/SitePages/Forms/ByAuthor.aspx).WebRequest;
request.Method = "GET";
//request.Headers.Add("Authorization", $"Bearer {accessToken}");
clientContext.ExecutingWebRequest += delegate (object sender, WebRequestEventArgs e)
{
e.WebRequestExecutor.WebRequest.Headers.Add("Authorization", "Bearer " + accessToken);
};
await request.GetResponseAsync(); // Throws 403
with sharepoint online
string siteUrl = ConfigurationManager.AppSettings["siteURL"];
SecureString passWord = new SecureString();
string pass = ConfigurationManager.AppSettings["userPassword"];
string user = ConfigurationManager.AppSettings["userName"];
foreach (char c in pass.ToCharArray()) passWord.AppendChar(c);
ClientContext clientContext = new ClientContext(siteUrl);
clientContext.Credentials = new SharePointOnlineCredentials(user, passWord);
return clientContext;
without
clientContext.ExecutingWebRequest

When calling AcquireTokenByRefreshToken on the AuthenticationContext instance with Microsoft.IdentityModel.Clients.ActiveDirectory?

I am developing a multi-tenant application registered on my Azure AD that consumes Office 365 apis, Graph API etc.
I followed this Microsoft sample to build my work which uses ADAL .NET library and OpenIdConnect: Microsoft.IdentityModel.Clients.ActiveDirectory, Version=2.19.0.0
In ADAL.NET, we use an AuthenticationContext instance with a custom inherited class for the TokenCache (see code the sample code here).
For each request to the authorized resources, depending on the API, we invoke one of these methods (see code below) to get the auth_token that will be put in the request Bearer parameter. Is it the correct way to do it?
We never make use of the method AcquireTokenByRefreshTokenAsync, does it mean that our application never uses the refresh_token? Does it mean that our user will have to relog after one hour? Should we implement a kind of refreshing procedure with AcquireTokenByRefreshTokenAsync in the catch statement? Can it be made without prompting anything to the end-user?
REMARK: I posted a question regarding OpenIdConnect authentication ticket lifetime. To me these two questions are unrelated but they may be.
string signInUserId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
string userObjectId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
string tenantId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
public async Task<string> AcquireOutlook365TokenAsync()
{
AuthenticationContext authContext = new AuthenticationContext(string.Format("{0}/{1}", SettingsHelper.AuthorizationUri, tenantId), new ADALTokenCache(signInUserId));
try
{
var result = await authContext.AcquireTokenSilentAsync(#"https://outlook.office365.com/",
new ClientCredential(SettingsHelper.ClientId, SettingsHelper.AppKey),
new UserIdentifier(userObjectId, UserIdentifierType.UniqueId));
return result.AccessToken;
}
catch (AdalException exception)
{
//handle token acquisition failure
if (exception.ErrorCode == AdalError.FailedToAcquireTokenSilently)
{
authContext.TokenCache.Clear();
}
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized));
}
}
public async Task<string> AcquireAzureGraphTokenAsync()
{
AuthenticationContext authContext = new AuthenticationContext(string.Format("{0}/{1}", SettingsHelper.AuthorizationUri, tenantId), new ADALTokenCache(signInUserId));
try
{
var result = await authContext.AcquireTokenSilentAsync(#"https://graph.windows.net/",
new ClientCredential(SettingsHelper.ClientId, SettingsHelper.AppKey),
new UserIdentifier(userObjectId, UserIdentifierType.UniqueId));
return result.AccessToken;
}
catch (AdalException exception)
{
//Same as other method
}
}
ADAL uses the stored refresh tokens automatically and transparently, you aren't required to perform any explicit action. AcquireTOkenByRefreshToken is in the ADAL surface for legacy reasons, and has been removed from version 3.x. More background at http://www.cloudidentity.com/blog/2015/08/13/adal-3-didnt-return-refresh-tokens-for-5-months-and-nobody-noticed/

Using Hammock to Call Recurly REST API with BasicAuthCredentials

I'm trying to call the Recurly REST API using the Hammock C# library for .NET. The API calls require an Authorization header on the HttpRequest, and the authentication scheme is Basic authentication with the Base64 encoded API key in the username portion of the header. I thought that I could create a new Hammock BasicAuthCredentials object with the encoded key in the Username property of the object, then assign the object to the Credentials property of either the RestClient or RestRequest objects. However, this does not seem to generate an Authorization header on the outbound HttpRequest.
If I add the Authorization header manually using the AddHeader method on one of those objects, the API call succeeds. If I use the Credentials property with the BasicAuthCredentials object, I get an Access Denied error from Recurly.
This seems pretty basic, so I know I'm doing something wrong. So, in Hammock, is the Credentials property on either the RestClient or RestRequest object supposed to create an Authorization header on the Http request?
Thanks for any help from a super Hammock user!
The code that fails:
class Program
{
public static void Main(string[] args)
{
string encodedAPIKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("APIKeyHere"));
BasicAuthCredentials credentials = new BasicAuthCredentials
{
Username = encodedAPIKey
};
RestClient client = new RestClient
{
Authority = "https://api.recurly.com",
VersionPath = "v2"
};
client.AddHeader("Accept", "application/xml");
RestRequest request = new RestRequest
{
Credentials = credentials,
Path = "plans"
};
RestResponse response = client.Request(request);
Console.WriteLine(response.Content);
Console.ReadLine();
}
}
The code that succeeds:
class Program
{
public static void Main(string[] args)
{
string encodedAPIKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("APIKeyHere"));
RestClient client = new RestClient
{
Authority = "https://api.recurly.com",
VersionPath = "v2"
};
client.AddHeader("Accept", "application/xml");
client.AddHeader("Authorization", "Basic " + encodedAPIKey);
RestRequest request = new RestRequest
{
Path = "plans"
};
RestResponse response = client.Request(request);
Console.WriteLine(response.Content);
Console.ReadLine();
}
}
After getting no answers to my question, I did a search for alternative Rest libraries for .NET and found RestSharp. I was able to get it working with Recurly using its built-in Basic Authorization implementation on the first try, so I will be implementing using RestSharp. The code looks very similar, so the migration should be an easy one.

Securing ASP.NET MVC controller action which returns JSON

I have an MVC3 application, and my controller actions are secured using the [Authorize] attribute. So far, so good, forms auth works great. Now I want to add a JSON API to my application so some actions are accessible to non-browser clients.
I'm having trouble figuring out the 'right' design.
1) Each user has secret API key.
2) User ID 5 calls http://myapp.com/foocontroller/baraction/5?param1=value1&param2=value2&secure_hash=someValue. Here, secure_hash is simply the SHA1 hash of param1 and param2's values appended with the secret API key for the user
2) /foocontroller/baraction will be decorated with [CustomAuthorize]. This will be an implementation of AuthorizeAttribute which will check if the request is coming in as JSON. If it is, it will check the hash and see if it matches. Otherwise, if the request is HTML, then I call into existing authorization.
I am not at all sure if this will work. Is it normal to pass a secure hash in the query string or should I be passing it in as an HTTP header? Is it better to use HTTP basic auth instead of a hash made using the secret API key?
Tips from anyone who has made a web API using ASP.NET MVC would be welcome!
I pass the secret API key along with username and password in the request body. Once authorized, a token is generated and the client has to pass that in the Authorization header. This gets checked in the base controller on each request.
Client calls myapp.com/authorize which return auth token.
Client stores auth token locally.
Client calls myapp.com/anycontroller, with authtoken in Authorization header.
AuthorizeController inherits from controller.
Anycontroller inherits from a custom base controller which performs the authorization code.
My example requires the following route which directs POST requests to an ActionResult named post in any controller. I am typing this in by hand to simplify it as much as possible to give you the general idea. Don't expect to cut and paste and have it work :)
routes.MapRoute(
"post-object",
"{controller}",
new { controller = "Home", action = "post" {,
new { httpMethod = new HttpMethodConstraint("POST")}
);
Your auth controller can use this
public class AuthorizationController : Controller
{
public ActionResult Post()
{
string authBody;
var request = ControllerContext.HttpContext.Request;
var response = ControllerContext.HttpContext.Response;
using(var reader = new StreamReader(request.InputStream))
authBody = reader.ReadToEnd();
// authorize based on credentials passed in request body
var authToken = {result of your auth method}
response.Write(authToken);
}
}
Your other controllers inherit from a base controller
public class BaseController : Controller
{
protected override void Execute(RequestContext requestContext)
{
var request = requestContext.HttpContext.Request;
var response = requestContext.HttpContext.Response;
var authToken = Request.Headers["Authorization"];
// use token to authorize in your own method
var authorized = AmIAuthorized();
if(authorized = false) {
response.StatusCode = 401;
response.Write("Invalid token");
return;
}
response.StatusCode = 200; // OK
base.Execute(requestContext); // allow inheriting controller to continue
}
}
Sample code to call the api
public static void ExecutePostRequest(string contentType)
{
request = (HttpWebRequest)WebRequest.Create(Uri + Querystring);
request.Method = "POST";
request.ContentType = contentType; // application/json usually
request.Headers["Authorization"] = token;
using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))
writer.Write(postRequestData);
// GetResponse reaises an exception on http status code 400
// We can pull response out of the exception and continue on our way
try
{
response = (HttpWebResponse)request.GetResponse();
}
catch (WebException ex)
{
response = (HttpWebResponse)ex.Response;
}
finally
{
using (StreamReader reader =
new StreamReader(response.GetResponseStream()))
responseText = reader.ReadToEnd();
httpcontext = HttpContext.Current;
}
}

How do I make a simple post to Twitter via ASP.NET (VB, preferably)?

I don't want to do anything fancy on Twitter except post to it via my site once a day. I have searched around a bit and there are all sorts of super-complex ways to do every little thing that Twitter does, but there seems to be little documentation on how to do the simplest thing, which is make a post!
Does anyone know how to do this? Or can you at least point me in the right direction? I don't need full wrappers or anything (http://apiwiki.twitter.com/Libraries#C/NET), just one simple function that will post to Twitter.
Thanks!
This is the easiest implementation ever. Up and running in under 2 minutes: Twitterizer
Its fairly simple; you just need to post an xml file to a web page using webrequest.create. This example is close (assumes you have the xml for the message in another place and just pass it into twitterxml variable as a string. The url might not be the right one; found it on this [page][1] which defines the interface
WebRequest req = null;
WebResponse rsp = null;
try
{
string twitterXML = "xml as string";
string uri = "http://twitter.com/statuses/update.format";
req = WebRequest.Create(uri);
//req.Proxy = WebProxy.GetDefaultProxy(); // Enable if using proxy
req.Method = "POST"; // Post method
req.ContentType = "text/xml"; // content type
// Wrap the request stream with a text-based writer
StreamWriter writer = new StreamWriter(req.GetRequestStream());
// Write the XML text into the stream
writer.WriteLine(twitterXML);
writer.Close();
// Send the data to the webserver
rsp = req.GetResponse();
}
[1]: http://apiwiki.twitter.com/Twitter-REST-API-Method%3A-statuses update
There are a couple different ways of doing this, they vary depending on the tools you want to use and have access to. Option 1 will work right out of the box, but the coding can be complicated. Option 3 you will have to download tools for, but once there installed and loaded you should be able to consume the twitter api very quickly.
Use WebRequest.Create to create/send messages to remote endpoints
Use WCF, create a mirror endpoint and access the twitter api using client only endpoint.
Use the WCF REST Starter Kit Preview 2, which has a new class called the HttpClient. I would have to recommend this technique if you can. Here is a great video Consuming a REST Twitter Feed in under 3 minutes.
Here is a sample of using the WCF REST Starter Kit's HttpClient:
public void CreateFriendship(string friend)
{
using (var client = new HttpClient())
{
var url = string.Format("http://www.twitter.com/friendships/create/{0}.xml?follow=true", friend);
client.Post(url)
.CheckForTwitterError()
.EnsureStatusIs(HttpStatusCode.OK);
}
}
Add a comment if you'd like more info about a particular method.
Update:
For Option #1 see this question: Remote HTTP Post with C#
There are a few ways of doing this, you can check out http://restfor.me/twitter and it will give you the code from RESTful documentation.
Essentially making any authenticated call you can follow this logic:
///
/// Executes an HTTP POST command and retrives the information.
/// This function will automatically include a "source" parameter if the "Source" property is set.
///
/// The URL to perform the POST operation
/// The username to use with the request
/// The password to use with the request
/// The data to post
/// The response of the request, or null if we got 404 or nothing.
protected string ExecutePostCommand(string url, string userName, string password, string data) {
WebRequest request = WebRequest.Create(url);
request.ContentType = "application/x-www-form-urlencoded";
request.Method = "POST";
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password)) {
request.Credentials = new NetworkCredential(userName, password);
byte[] bytes = Encoding.UTF8.GetBytes(data);
request.ContentLength = bytes.Length;
using (Stream requestStream = request.GetRequestStream()) {
requestStream.Write(bytes, 0, bytes.Length);
using (WebResponse response = request.GetResponse()) {
using (StreamReader reader = new StreamReader(response.GetResponseStream())) {
return reader.ReadToEnd();
}
}
}
}
return null;
}

Resources