I am converting an existing application from QBXML to use the QBOE API V3 for the QuickBooks Online accounts. I have managed to complete the OAuth and have what appears to be valid tokens/secrets using the DevDefined toolkit. I am stuck on generation of the oauth_signature. All the documentation points me to use the Intuit.Ipp DLL's however I can't because it is written to .net 4.0 framework and the server my application runs on only has 2.0 loaded. I can move my application but testing that upgrade would put me after the cut off deadline (4/16/2014). Is there a way I can build the signature using the DevDefined or some other option?
The application runs on IIS 6.0, DotNet Framework 2.0, with ASP.VB.
Is the cutoff 4/16 now? I didn't see that announced anywhere. I thought it was 3/15.
I haven't tested this on .NET 2.0 but this just uses DevDefined without all the overhead. It will handle the signing and the http request for you.
using System.Text;
using DevDefined.OAuth.Consumer;
using DevDefined.OAuth.Framework;
// etc...
public void doGet()
{
IOAuthSession session = CreateSession();
string resp = session.Request().Get().ForUrl("https://quickbooks.api.intuit.com/v3/company/"
+ realmId + "/query?query=select * from customer where DisplayName='" + name + "'").ToString();
}
public void doPost(string Name)
{
IOAuthSession session = CreateSession();
string reqBody = "<Customer xmlns=\"http://schema.intuit.com/finance/v3\" domain=\"QBO\" sparse=\"false\">";
reqBody += "<DisplayName>" + Name + "</DisplayName>";
reqBody += "</Customer>";
byte[] bytes = Encoding.UTF8.GetBytes(reqBody);
session.ConsumerContext.UseHeaderForOAuthParameters = true;
IConsumerRequest req = session.Request().WithRawContentType("application/xml").WithRawContent(bytes);
req = req.WithAcceptHeader("application/xml");
req = req.ForUrl("https://quickbooks.api.intuit.com/v3/company/"
+ realmI + "/customer");
string resp = req.Post().ToString();
}
private IOAuthSession CreateSession()
{
OAuthConsumerContext consumerContext = new OAuthConsumerContext
{
ConsumerKey = Properties.Settings.Default.consumerKey,
ConsumerSecret = Properties.Settings.Default.consumerSecret,
SignatureMethod = SignatureMethod.HmacSha1
};
IOAuthSession session = new OAuthSession(consumerContext);
session.AccessToken = myAccessToken;
return session;
}
Related
I have been working on a requirement, i.e. when a bug is created/inprogress in TFS post a HTTP call to Slack (third party collaboration tool).
When a bug is closed post one more HTTP call to Slack.
I had implemented TFS server side plugin, unfortunately we don't have complete access to TFS and cannot implement. So, planning to implement Webapi and host it (say in Docker container) and whenever bug created / closed event happens in TFS it should post HTTP call.
I have created a simple console app with a method and it's working fine.
any sample code or thoughts to convert it to web api?
if I host, can it monitor TFS events and posts some HTTP calls?
public class GetWI
{
static void Main(string[] args)
{
GetWI ex = new GetWI();
ex.GetWorkItemsByWiql();
}
public void GetWorkItemsByWiql()
{
string _personalAccessToken = "xxxx";
string _credentials = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", "", _personalAccessToken)));
//this is needed because we want to create a project scoped query
string project = "Agileportfolio";
//create wiql object
var wiql = new
{
query = "Select [State], [Title] " +
"From WorkItems " +
"Where [Work Item Type] = 'Bug' " +
"And [System.TeamProject] = '" + project + "' " +
"And [System.State] = 'New' " +
"Order By [State] Asc, [Changed Date] Desc"
};
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://test.visualstudio.com");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", _credentials);
//serialize the wiql object into a json string
var postValue = new StringContent(JsonConvert.SerializeObject(wiql), Encoding.UTF8, "application/json"); //mediaType needs to be application/json for a post call
var method = new HttpMethod("POST");
var httpRequestMessage = new HttpRequestMessage(method, "https://abrahamdhanyaraj.visualstudio.com/_apis/wit/wiql?api-version=2.2") { Content = postValue };
var httpResponseMessage = client.SendAsync(httpRequestMessage).Result;
if (httpResponseMessage.IsSuccessStatusCode)
{
WorkItemQueryResult workItemQueryResult = httpResponseMessage.Content.ReadAsAsync<WorkItemQueryResult>().Result;
//now that we have a bunch of work items, build a list of id's so we can get details
var builder = new System.Text.StringBuilder();
foreach (var item in workItemQueryResult.WorkItems)
{
builder.Append(item.Id.ToString()).Append(",");
}
//clean up string of id's
string ids = builder.ToString().TrimEnd(new char[] { ',' });
HttpResponseMessage getWorkItemsHttpResponse = client.GetAsync("_apis/wit/workitems?ids=" + ids + "&fields=System.Id,System.Title,System.State&asOf=" + workItemQueryResult.AsOf + "&api-version=2.2").Result;
if (getWorkItemsHttpResponse.IsSuccessStatusCode)
{
var result = getWorkItemsHttpResponse.Content.ReadAsStringAsync().Result;
//Read title
}
}
// Create Channel
string name = "xyzz3";
var payload = new
{
token = "xoxp-291239704800-292962676087-297314229698-a80e720d98e443c8afb0c4cb2c09e745",
name = "xyzz3",
};
var serializedPayload = JsonConvert.SerializeObject(payload);
var response = client.PostAsync("https://slack.com/api/channels.create" + "?token=test&name=" + name + "&pretty=1",
new StringContent(serializedPayload, Encoding.UTF8, "application/json")).Result;
if (response.IsSuccessStatusCode)
{
dynamic content = JsonConvert.DeserializeObject(
response.Content.ReadAsStringAsync()
.Result);
}
}
}
I use wcf service to listen events from TFS. You may find my project here: https://github.com/ashamrai/tfevents
For wcf service:
Update your ServiceName.svc file and add:
Factory="System.ServiceModel.Activation.WebServiceHostFactory"
Create web method to use json:
[OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
void WorkItemChangedEvent(Stream EventData);
Convert stream with Newtonsoft.Json to get information about event and work item:
StreamReader _reader = new StreamReader(pEventData, Encoding.UTF8);
string _eventStr = _reader.ReadToEnd();
WorkItemEventCore _wieventcorre = JsonConvert.DeserializeObject(_eventStr);
Then you have to create the subscription with url "http://host:port/service.svc/webmethod": https://learn.microsoft.com/en-us/vsts/service-hooks/services/webhooks
Instead of using a query and manually polling Visual Studio Team Services (VSTS), you can use a concept called WebHooks. You configure a WebHook in VSTS to listen for events and send these to a public endpoint. One event type is for Work Items. The endpoint can be any type of public endpoint, for example an Azure Function.
If the only thing you want to do is post the events to Slack, it's even easier because that's a standard integration point: Slack with VSTS.
This is much easier then using a server side plugin or writing your own Web API.
Hello Office / SharePoint Developers,
I am working on a project based on the Office Developer Patterns and Practices Sample where a console application accesses a WebAPI which then access SharePoint Online as the logged in user: The sample is here: https://github.com/SharePoint/PnP/tree/master/Samples/AzureAD.WebAPI.SPOnline
Question:
When I attempt to upload a file to the document library, I get an error 401 "The remote server returned an error: (401) Unauthorized".
The file read options such as listing the documents and querying for documents works fine.
The user credentials I supply are of a user that is the site collection admin, owner, and global admin on the tenant.
I get an access token from SharePoint online based on the token I get in the native client.
public string GetAccessToken(string accessToken)
{
string clientID = _clientId;
string clientSecret = _clientSecret;
var appCred = new ClientCredential(clientID, clientSecret);
var authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext("https://login.windows.net/common");
AuthenticationResult authResult = authContext.AcquireToken(new Uri(_spoUrl).GetLeftPart(UriPartial.Authority), appCred, new UserAssertion(accessToken));
return authResult.AccessToken;
}
This is the CSOM that uploads the file. I know it works as I can paste it into a console app and using (SharePointOnlineCredentails) it works fine.
string newToken = _tokenSvc.GetAccessToken(accessToken);
using (ClientContext cli = new ClientContext(_spoUrl))
{
cli.ExecutingWebRequest += (s, e) => e.WebRequestExecutor.WebRequest.Headers.Add("Authorization", "Bearer " + newToken);
cli.AuthenticationMode = ClientAuthenticationMode.Default;
using (var fs = new FileStream(#"c:\test.txt", FileMode.Open))
{
var fi = new FileInfo("test.txt");
var list = cli.Web.Lists.GetByTitle("documents");
cli.Load(list.RootFolder);
cli.ExecuteQuery();
var fileUrl = String.Format("{0}/{1}", list.RootFolder.ServerRelativeUrl, fi.Name);
Microsoft.SharePoint.Client.File.SaveBinaryDirect(cli, fileUrl, fs, true);
Web web = cli.Web;
Microsoft.SharePoint.Client.File newFile = web.GetFileByServerRelativeUrl(fileUrl);
cli.Load(newFile);
cli.ExecuteQuery();
ListItem item = newFile.ListItemAllFields;
item["CRUID"] = "CRU_1337";
item.Update();
cli.ExecuteQuery();
}
}...
TLDR: I get 401 on file upload. Reads work. I am using CSOM with an access token that is supposed to be a webAPI on behalf of the logged in user.
I look forward to hearing your advice!
Chris
I am not sure whether we could upload/download files from SP using access tokens with CSOM now , see discussion here two years ago . But we could use sharepoint online rest api to upload files to sharepoint online , i tried below code and it works fine in the code sample AzureAD.WebAPI.SPOnline :
string sharePointUrl = ConfigurationManager.AppSettings["SharePointURL"];
string newToken = GetSharePointAccessToken(sharePointUrl, this.Request.Headers.Authorization.Parameter);
using (ClientContext cli = new ClientContext(sharePointUrl))
{
cli.AuthenticationMode = ClientAuthenticationMode.Default;
cli.ExecutingWebRequest += (s, e) => e.WebRequestExecutor.WebRequest.Headers.Add("Authorization", "Bearer " + newToken);
cli.AuthenticationMode = ClientAuthenticationMode.Default;
byte[] bytefile = System.IO.File.ReadAllBytes(#"e:\log.txt");
HttpWebRequest endpointRequest = (HttpWebRequest)HttpWebRequest.Create("https://xxx.sharepoint.com/sites/xxx/" + "/_api/web/GetFolderByServerRelativeUrl('Shared%20Documents')/Files/add(url='log.txt',overwrite=true)");
endpointRequest.Method = "POST";
endpointRequest.Headers.Add("binaryStringRequestBody", "true");
endpointRequest.Headers.Add("Authorization", "Bearer " + newToken);
endpointRequest.GetRequestStream().Write(bytefile, 0, bytefile.Length);
HttpWebResponse endpointresponse = (HttpWebResponse)endpointRequest.GetResponse();
}
The code below is ended up being the solution to my question:
/* Beginning CSOM Magic */
using (ClientContext cli = new ClientContext(_spoUrl))
{
/* Adding authorization header */
cli.ExecutingWebRequest += (s, e) => e.WebRequestExecutor.WebRequest.Headers.Add("Authorization", "Bearer " + newToken);
cli.AuthenticationMode = ClientAuthenticationMode.Default;
//Get Document List
List documentsList = cli.Web.Lists.GetByTitle(_libraryName);
var fileCreationInformation = new FileCreationInformation();
//Assign to content byte[] i.e. documentStream
var data = System.IO.File.ReadAllBytes(#"c:\test.txt");
fileCreationInformation.Content = data;
//Allow owerwrite of document
fileCreationInformation.Overwrite = true;
//var siteURL = _spoUrl;
var documentListURL = "shared documents";
//var documentName = "/test.txt";
//Upload URL
fileCreationInformation.Url = string.Concat(_spoUrl,"/",documentListURL,"/",documentName);
Microsoft.SharePoint.Client.File uploadFile = documentsList.RootFolder.Files.Add(
fileCreationInformation);
//Update the metadata for a field having name "DocType"
uploadFile.ListItemAllFields["CRUID"] = cruId;
uploadFile.ListItemAllFields.Update();
cli.ExecuteQuery();
}
I try to GET query from MVC app to OWIN SelfHost. Both have WindowsAuth. MVC is running from Visual Studio in one virtual machine, OWIN SelfHost works as service in other virtual machine. Response is always 401. I tried to set Credentials,PreAuthenticate in WebRequest - nothing changed. I can't let anonymous auth in my system. How I should make request? Thanks.
The solution was found:
WebRequest req = WebRequest.Create(Properties.Settings.Default.SelfHostAdress + "/api/data/GetReportsOrgStructTreeView?currentUserName=" + currentUsername + "&parentNodeId=" + parentNodeId + "&filterId=" + filterId);
req.UseDefaultCredentials = true;
req.PreAuthenticate = true;
WebResponse resp = req.GetResponse();
System.IO.StreamReader sr = new System.IO.StreamReader(resp.GetResponseStream());
content = sr.ReadToEnd();
return content;
I have an ASP.NET app that uses Azure ACS (and indirectly ADFS) for Authentication - which all works fine. Now I've been asked to pass the SessionToken to another backend service where it can be verified and the claims extracted. [Long Story and not my choice]
I'm having fits on the decryption side, and I'm sure I'm missing something basic.
To set the stage, the error upon decryption is:
ID1006: The format of the data is incorrect. The encryption key length is negative: '-724221793'. The cookie may have been truncated.
The ASP.NET website uses the RSA wrapper ala:
void WSFederationAuthenticationModule_OnServiceConfigurationCreated(object sender, ServiceConfigurationCreatedEventArgs e)
{
string thumbprint = "BDE74A3EB573297C7EE79EB980B0727D73987B0D";
X509Certificate2 certificate = GetCertificate(thumbprint);
List<CookieTransform> sessionTransforms = new List<CookieTransform>(new CookieTransform[]
{
new DeflateCookieTransform(),
new RsaEncryptionCookieTransform(certificate),
new RsaSignatureCookieTransform(certificate)
});
SessionSecurityTokenHandler sessionHandler = new SessionSecurityTokenHandler(sessionTransforms.AsReadOnly());
e.ServiceConfiguration.SecurityTokenHandlers.AddOrReplace(sessionHandler);
}
(the thumbprint is the same value as added by FedUtil in web.config.
I write the token with:
if (Microsoft.IdentityModel.Web.FederatedAuthentication.SessionAuthenticationModule.TryReadSessionTokenFromCookie(out token))
{
Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler th = new Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler();
byte[] results = th.WriteToken(token);
...
which gives me:
<?xml version="1.0" encoding="utf-8"?>
<SecurityContextToken p1:Id="_53382b9e-8c4b-490e-bfd5-de2e8c0f25fe-94C8D2D9079647B013081356972DE275"
xmlns:p1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns="http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512">
<Identifier>urn:uuid:54bd1bd7-1110-462b-847e-7f49c1043b32</Identifier>
<Instance>urn:uuid:0462b7d7-717e-4ce2-b942-b0d6a968355b</Instance>
<Cookie xmlns="http://schemas.microsoft.com/ws/2006/05/security">AQAAANCMnd blah blah 1048 bytes total
</Cookie>
</SecurityContextToken>
and, with the same Certificate on the other box (and the token read in as a file just for testing), I have:
public static void Attempt2(FileStream fileIn, X509Certificate2 certificate, out SecurityToken theToken)
{
List<CookieTransform> sessionTransforms = new List<CookieTransform>(new CookieTransform[]
{
new DeflateCookieTransform(),
new RsaSignatureCookieTransform(certificate),
new RsaEncryptionCookieTransform(certificate)
});
SessionSecurityTokenHandler sessionHandler = new SessionSecurityTokenHandler(sessionTransforms.AsReadOnly());
// setup
SecurityTokenResolver resolver;
{
var token = new X509SecurityToken(certificate);
var tokens = new List<SecurityToken>() { token };
resolver = SecurityTokenResolver.CreateDefaultSecurityTokenResolver(tokens.AsReadOnly(), false);
}
sessionHandler.Configuration = new SecurityTokenHandlerConfiguration();
sessionHandler.Configuration.IssuerTokenResolver = resolver;
using (var reader = XmlReader.Create(fileIn))
{
theToken = sessionHandler.ReadToken(reader);
}
}
and then ReadToken throws a FormatException of
ID1006: The format of the data is incorrect. The encryption key length is negative: '-724221793'. The cookie may have been truncated.
At this point, I can't tell if my overall approach is flawed or if I'm just missing the proverbial "one-line" that fixes all of this.
Oh, and I'm using VS2010 SP1 for the website (.NET 4.0) and I've tried both VS2010SP1 .NET 4.0 and VS2012 .NET 4.5 on the decoding side.
Thanks!
Does your app pool account for the backend service have read access to the certificate? If not give your app pool account for the backend service read access to the certificate. I had problems in the past with encryption/decryption because of this.
This might help, this will turn your FedAuth cookies into a readable XML string like:
<?xml version="1.0" encoding="utf-8"?>
<SecurityContextToken p1:Id="_548a372e-1111-4df8-b610-1f9f618a5687-953155F0C35B4862A5BCE4D5D0C5ADF0" xmlns:p1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns="http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512">
<Identifier>urn:uuid:c9f9b733-1111-4b01-8af3-23c8af3e19a6</Identifier>
<Instance>urn:uuid:ee955207-1111-4498-afa3-4b184e97d0be</Instance>
<Cookie xmlns="http://schemas.microsoft.com/ws/2006/05/security">long_string==</Cookie>
</SecurityContextToken>
Code:
private string FedAuthToXmlString(string fedAuthCombinedString)
{
// fedAuthCombinedString is from FedAuth + FedAuth1 cookies: just combine the strings
byte[] authBytes = Convert.FromBase64String(fedAuthCombinedString);
string decodedString = Encoding.UTF8.GetString(authBytes);
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var thumbprint = "CERT_THUMBPRINT"; // from config
var cert = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false)[0];
var sessionTransforms = new List<System.IdentityModel.CookieTransform>(new System.IdentityModel.CookieTransform[]
{
new System.IdentityModel.DeflateCookieTransform(),
new System.IdentityModel.RsaSignatureCookieTransform(cert),
new System.IdentityModel.RsaEncryptionCookieTransform(cert)
});
SessionSecurityTokenHandler sessionHandler = new SessionSecurityTokenHandler(sessionTransforms.AsReadOnly());
SecurityTokenResolver resolver;
{
var token = new X509SecurityToken(cert);
var tokens = new List<SecurityToken>() { token };
resolver = SecurityTokenResolver.CreateDefaultSecurityTokenResolver(tokens.AsReadOnly(), false);
}
sessionHandler.Configuration = new SecurityTokenHandlerConfiguration();
sessionHandler.Configuration.IssuerTokenResolver = resolver;
var i = 0; // clear out invalid leading xml
while ((int)decodedString[i] != 60 && i < decodedString.Length - 1) i++; // while the first character is not <
store.Close();
return decodedString.Substring(i);
}
I've created a project in Big Query and within API Access pane, I've created Service Account so that i can make Big Query API accessible through windows application on behalf of user interaction. Since i am new to using and accessing Google API, I want to know the basic steps so that i can access Big Query via Windows Service.
There are some .NET code samples here using web-based OAuth flows:
Google BigQuery with .NET documentation/ samples
For an example from another dev using service accounts in .NET, see this:
Google OAuth2 Service Account Access Token Request gives 'Invalid Request' Response
These manually implement service accounts on top of the .NET library. The .NET library has recently added native support for service accounts, though I haven't yet found an official example.
Here's an unofficial sample working with the Analytics API. It should be directly analogous to using service accounts with BigQuery:
How do I use a Service Account to Access the Google Analytics API V3 with .NET C#?
Finally here's the working code for Authentication process to Big Query API and retrieving records from Big Query table:-
I've created a class having a method of return type OAuth2Authenticator<AssertionFlowClient>.
internal class clsGetOAuth2Authentication
{
public OAuth2Authenticator<AssertionFlowClient> objGetOAuth2(string strPrivateFilePath, string strPrivateFilePassword, string strServiceAccEmailId,string strScope)
{
AuthorizationServerDescription objAuthServerDesc;
X509Certificate2 objKey;
AssertionFlowClient objClient;
OAuth2Authenticator<AssertionFlowClient> objAuth = null;
string ScopeUrl = "https://www.googleapis.com/auth/" + strScope;
string strSrvAccEmailId = strServiceAccEmailId;
string strKeyFile = strPrivateFilePath; //KeyFile: This is the physical path to the key file you downloaded when you created your Service Account.
string strKeyPassword = (strPrivateFilePassword != "") ? strPrivateFilePassword : "notasecret"; //key_pass: This is probably the password for all key files, but if you're given a different one, use that.
objAuthServerDesc = GoogleAuthenticationServer.Description; //objAuthServerDesc: Description of the server that will grant Authentiation.
objKey = new X509Certificate2(strKeyFile, strKeyPassword, X509KeyStorageFlags.Exportable); //objkey: Load up and decrypt the key.
objClient = new AssertionFlowClient(objAuthServerDesc, objKey) { ServiceAccountId = strSrvAccEmailId, Scope = ScopeUrl }; //objClient: Using the AssertionFlowClient, because we're logging in with our certificate.
objAuth = new OAuth2Authenticator<AssertionFlowClient>(objClient, AssertionFlowClient.GetState); //objAuth: Requesting Authentication.
return objAuth;
}
}
Now, another class calls the above method:-
clsGetOAuth2Authentication objOAuth2 = new clsGetOAuth2Authentication();
try
{
var objAuth = objOAuth2.objGetOAuth2(strKeyFile, strKeyPassword, strSrvAccEmailId, strScope); //Authentication data returned
objBQServ = new BigqueryService(objAuth); //Instantiate BigQueryService with credentials(objAuth) as its parameter
#region Retrieving Records:-
JobsResource j = objBQServ.Jobs;
QueryRequest qr = new QueryRequest();
qr.Query = strQuery;
DateTime dtmBegin = DateTime.UtcNow;
QueryResponse response = j.Query(qr, strProjId).Fetch();
DateTime dtmEnd = DateTime.UtcNow;
string strColHead = "";
foreach (var colHeaders in response.Schema.Fields)
{
strColHead += colHeaders.Name.ToString() + "\t";
}
Console.WriteLine(strColHead);
int intCount = 0;
foreach (TableRow row in response.Rows)
{
intCount += 1;
List<string> list = new List<string>();
foreach (var field in row.F)
{
list.Add(field.V);
}
Console.WriteLine(String.Join("\t", list));
}
TimeSpan tsElapsed = dtmEnd - dtmBegin;
Console.WriteLine("\n" + "Total no. of records:- " + intCount + ". Time taken:- " + tsElapsed);
#endregion
}
catch (Exception ex)
{
Console.WriteLine("Error Occured!" + "\n\n" + "Statement:- " + ex.Message.ToString() + "\n\n" + "Description:- " + ex.ToString() + "\n\n");
Console.WriteLine("\nPress enter to exit");
Console.ReadLine();
}