Access TFS RESTful API from Angular web app - rest

I think TFS RESTful api has a bug. I am trying to access it using an Angular web app. Our TFS server is corporate internal. Here is my code:
var path = 'http://tfs.mycompany.com/tfs/mycompany/_apis/wit/queries?$depth=1&$expand=all&api-version=2.2';
var config = { withCredentials: true };
$http.get(path, config)
.then(function (response) {
$scope.resultList = response.data.d.results || [ response.data.d ];
$scope.message = 'Found ' + $scope.resultList.length + ' item' + ($scope.resultList.length == 1 ? '':'s');
}, function (response) {
$scope.resultList = [];
$scope.message = 'Error ' + response.status + ' ' + JSON.stringify(response.data);
});
The request goes to the server, and the server responds with OK 200. However, the browser (Chrome) blocks the data, and tells me:
A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header
when the credentials flag is true. Origin 'http://localhost' is therefore
not allowed access. The credentials mode of an XMLHttpRequest is controlled
by the withCredentials attribute.
The request headers have Origin:http://localhost
The response headers have Access-Control-Allow-Origin:*
Is there any way for me to tell TFS to not return * in the Access-Control-Allow-Origin? This seems like a serious bug in TFS, which renders the RESTful api practically useless for web apps!

You may check Cross-origin resource sharing (CORS) example below to add Authorization in your code:
$( document ).ready(function() {
$.ajax({
url: 'https://fabrikam.visualstudio.com/defaultcollection/_apis/projects?api-version=1.0',
dataType: 'json',
headers: {
'Authorization': 'Basic ' + btoa("" + ":" + myPatToken)
}
}).done(function( results ) {
console.log( results.value[0].id + " " + results.value[0].name );
});
});
Also, check this case to see whether it is helpful:
AJAX cross domain issue with Visual Studio Online REST API

Related

'Access-Control-Allow-Origin' header value not equal to the supplied origin, POST method

I get the following message in the Chrome dev tools console when submitting a contact form (making a POST request) on the /about.html section my portfolio web site:
Access to XMLHttpRequest at 'https://123abc.execute-api.us-east-1.amazonaws.com/prod/contact' from origin 'https://example.net' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header has a value 'https://example.net/' that is not equal to the supplied origin.
I don't know how to troubleshoot this properly, any help is appreciated.Essentially, this is happening (https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSAllowOriginNotMatchingOrigin) and I don't know where within my AWS assets to fix it. This person had same problem, but i'm unsure of how to apply their fix (CORS header 'Access-Control-Allow-Origin' does not match... but it does‼)
Here is a description of the AWS stack:
Context, I am using an S3 bucket as static website using CloudFront and Route 53, this stuff works fine, has for years. When I added the form, I did the following to allow the HTTP POST request:
Cloudfront, On the site's distribution I added a behavior with all settings default except:
Path pattern: /contact (I am using this bc this is the API Gateway resource path ending)
Origin and origin groups: S3-Website-example.net.s3-website... (Selected correct origin)
Viewer protocol policy: HTTP and HTTPS
Allowed HTTP methods: GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE
Cache HTTP methods GET and HEAD methods are cached by default: Checked OPTIONS box
Origin request policy - optional: CORS-S3Origin
Response headers policy - optional: CORS-With-Preflight
API Gateway, Created a REST API with all default settings except:
Created a resource: /contact
Created a method: POST
For /contact, Resource Actions > Enable CORS:
Methods: OPTIONS and POST both checked
Access-Control-Allow-Origin: 'https://example.net' (no ending slash)
Clicked "Enable CORS and Replace existing headers"
Results are all checked green:
✔ Add Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin Method Response Headers to OPTIONS method
✔ Add Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin Integration Response Header Mappings to OPTIONS method
✔ Add Access-Control-Allow-Origin Method Response Header to POST method
✔ Add Access-Control-Allow-Origin Integration Response Header Mapping to POST method
Created a stage called "prod", ensured it had the /contact resource, and deployed.
At the /contact - POST - Method Execution, The test works as expected (triggers Lambda func that uses SES to send email, which I do actually receive).
The only thing I feel unsure about with API Gateway is after I enable the CORS, I can't seem to find a place where that setting has been saved, and if I click again on enable CORS, it is back to the default form ( with Access-Control-Allow-Origin: '')*
Amazon SES, set up 2 verified identities for sending/receiving emails via lamda.
Lamda, set up a basic javascript function with default settings, the REST API is listed as a trigger, and does actually work as previously mentioned. The function code is:
var AWS = require('aws-sdk');
var ses = new AWS.SES({ region: "us-east-1" });
var RECEIVER = 'myemail#email.com';
var SENDER = 'me#example.net';
var response = {
"statusCode": 200,
"headers": {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*"
},
"isBase64Encoded": false,
"body": "{ \"result\": \"Success\"\n}"
}
exports.handler = async function (event, context) {
console.log('Received event:', event);
var params = {
Destination: {
ToAddresses: [
RECEIVER
]
},
Message: {
Body: {
Text: {
Data: 'first name: ' + event.fname + 'last name: ' + event.lname + '\nemail: ' + event.email + '\nmessage: ' + event.message,
Charset: 'UTF-8'
}
},
Subject: {
Data: 'Website Query Form: ' + event.name,
Charset: 'UTF-8'
}
},
Source: SENDER
};
return ses.sendEmail(params).promise();
};
The only thing i can think of here is to maybe update the response to have "headers": {"Access-Control-Allow-Origin": "https://example.net"}
S3 bucket that holds the site contents, in permissions > CORS, I have the following JSON to allow a post of the contact form (notice no slash):
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"POST"
],
"AllowedOrigins": [
"https://example.net"
],
"ExposeHeaders": []
}
]
Permissions/Roles, Established Roles and permissions per
AWS guide: create dynamic contact forms for s3 static websites using aws lambda amazon api gateway and amazon ses
video titled: "Webinar: Dynamic Contact Forms for S3 Static Websites Using AWS Lambda, API Gateway & Amazon SES"
Client code, this is a very milk toast function being called to post the form on click.
function submitToAPI(event) {
event.preventDefault();
URL = "https://123abc.execute-api.us-east-1.amazonaws.com/prod/contact";
const namere = /[A-Za-z]{1}[A-Za-z]/;
const emailre = /^([\w-\.]+#([\w-]+\.)+[\w-]{2,6})?$/;
let fname = document.getElementById('first-name-input').value;
let lname = document.getElementById('last-name-input').value;
let email = document.getElementById('email-input').value;
let message = document.getElementById('message-input').value;
console.log(`first name: ${fname}, last name: ${lname}, email: ${email}\nmessage: ${message}`);
if (!namere.test(fname) || !namere.test(lname)) {
alert ("Name can not be less than 2 characters");
return;
}
if (email == "" || !emailre.test(email)) {
alert ("Please enter valid email address");
return;
}
if (message == "") {
alert ("Please enter a message");
return;
}
let data = {
fname : fname,
lname: lname,
email : email,
message : message
};
$.ajax(
{
type: "POST",
url : URL,
dataType: "json",
crossDomain: "true",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(data),
success: function () {
alert("Successful");
document.getElementById("contact-form").reset();
location.reload();
},
error: function () {
alert("Unsuccessful");
}
});
}
The problem was that the response in the lambda function had "Access-Control-Allow-Origin" set to "*".
This should have been set to the exact origin (no trailing slash), so if the origin is 'https://example.net', then the response in the lamda function should have "Access-Control-Allow-Origin" set to 'https://example.net' as shown below:
var response = {
"statusCode": 200,
"headers": {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "https://example.net"
},
"isBase64Encoded": false,
"body": "{ \"result\": \"Success\"\n}"
}```

How to manage contacts using Contacts API in our website correctly?

I was trying to integrate Google Contacts API to manage the contacts in my website.
I've done the following things:
I've created an application in google developer console and added http://localhost:4200 as URIs & Authorized redirect URIs.
Enabled 'Contacts API'.
I've added the following in my index.html (I've replaced {clientID} with my original client ID (of course):
<script>
function loadAuthClient() {
gapi.load('auth2', function() {
gapi.auth2.init({
client_id: '{clientID}'
}).then(() => {
console.log("success");
}).catch(err => {
console.log(err);
});
});
}
</script>
<script src="https://apis.google.com/js/platform.js?onload=loadAuthClient" async defer></script>
<meta name="google-signin-client_id" content="{clientID}">
Signed in successfully using:
gapi.auth2.getAuthInstance().signIn().then(() => {
console.log("Logged in")
}).catch(err => {
console.log(err);
});
Tried fetching the contacts using the following:
var user = gapi.auth2.getAuthInstance().currentUser.get();
var idToken = user.getAuthResponse().id_token;
var endpoint = `https://www.google.com/m8/feeds/contacts/`;
var xhr = new XMLHttpRequest();
xhr.open('GET', endpoint + '?access_token=' + encodeURIComponent(idToken));
xhr.setRequestHeader("Gdata-Version", "3.0");
xhr.onreadystatechange = function () {
if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
window.alert(xhr.responseText);
}
};
xhr.send();
But I'm getting the error:
Access to XMLHttpRequest at 'https://www.google.com/m8/feeds/contacts/?access_token={I removed the access token}' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Can someone please guide me where I'm going wrong?
My original response was off the mark. The actual answer is much simpler.
In step 4, try changing your endpoint:
var endpoint = `https://www.google.com/m8/feeds/contacts/default/full`;
In my local tests, this resulted in the expected response.
Another suggestion is to add alt=json to your query, so that you get easy to parse JSON payload. Otherwise you'll get a nasty XML payload in the response.
Here's the updated step 4 with these changes:
var endpoint = `https://www.google.com/m8/feeds/contacts/default/full`;
var xhr = new XMLHttpRequest();
xhr.open('GET', endpoint + '?access_token=' + encodeURIComponent(idToken) + '&alt=json');
xhr.setRequestHeader("Gdata-Version", "3.0");
xhr.onreadystatechange = function () {
if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
window.alert(xhr.responseText);
}
};
xhr.send();
Here's my original response, just in case it helps someone else.
I suspect that you'll need to add http://localhost:4200 to your list of "Authorized JavaScript origins" for the OAuth Client that you are using.
Edit your OAuth 2.0 Client ID and add the URI to the Javascript origins as below:
The other section on that page, Authorized Redirect URIs, only permits the OAuth flow to be redirected back to your web app. Often your web app server will actually consume the APIs so Google doesn't automatically permit CORS access to these APIs to keep things secure.

TFS Extension - TF400813: The user ... is not authorized to access this resource

I'm developing a TFS extension where I'm trying to access the dashboards Rest API.
The extension has almost all the scopes assigned:
"scopes": [
"vso.build_execute",
"vso.code_write",
"vso.code_manage",
"vso.dashboards_manage",
"vso.dashboards",
"vso.extension_manage",
"vso.extension.data_write",
"vso.gallery_manage",
"vso.identity",
"vso.notification_manage",
"vso.packaging_manage",
"vso.profile",
"vso.project",
"vso.project_manage",
"vso.release_manage"
],
The below code is making the API call:
var webContext = VSS.getWebContext();
console.log(`Collection URI: ${webContext.collection.uri}`);
console.log(`Project Name: ${webContext.project.name}`);
console.log(`User uniquename: ${webContext.user.uniqueName} id: ${webContext.user.id}`);
var baseUri = webContext.collection.uri + "/" + webContext.project.name;
var endpointUri = baseUri + "/_apis/dashboard/dashboards";
console.log(endpointUri);
var authToken = vssAuthentificationService.authTokenManager.getAuthorizationHeader(token);
console.log(authToken);
$.ajax({
type: "GET",
url: endpointUri,
contentType: 'application/json',
dataType: 'json',
headers: { 'Authorization': authToken }
})
.done(function (data) {
console.log(data);
});
which returns:
401 (Unauthorized); TF400813: The user ... is not authorized to access this resource
If I change the API uri to:
var endpointUri = baseUri + "/_apis/build/builds";
the response is OK.
The user that's using the extension is a TFS collection administrator.
What kind of permissions/scopes do I need to set in order to have access to the dashboards API?
TFS Version 16.131.27701.1
Please check whether you have {team} in your baseUri, this is required. The rest api should look like below:
GET https://{accountName}.visualstudio.com/{project}/{team}/_apis/dashboard/dashboards?api-version=4.1-preview.2

Create DocumentSet with REST api

How would one format the rest uri to create a document set in SP?
The JSOM code below works fine, but I would prefer to use the REST in order to be able to call it from a workflow.
var dsresult = SP.DocumentSet.DocumentSet.create(context, parentFolder, "docsetfromjsom", docsetCtId);
I tried this format based on this MSDN article
var restQueryUrl = spAppWebUrl + "/_api/SP.AppContextSite(#target)/SP.DocumentSet.DocumentSet.create('serverrelativeurl','docsetname','ctid')?#target='spHostUrl'";
Tried other formats as well but none successful. In jsom you also need to include the context, but I am assuming that for the rest call you don't need to use it (i think). Anyone tried this before?
Thanks!
I've already answered a similar question at SharePoint StackExchange.
To summarize, it does not seem possible to create Document Set using SharePoint 2013 REST API since SP.DocumentSet.DocumentSet.create function is not accessible via REST. But you could utilize SharePoint 2010 REST API instead for that purpose.
The following example demonstrates how to create a Document Set using SharePoint 2010 REST Interface:
function getListUrl(webUrl,listName)
{
return $.ajax({
url: webUrl + "/_api/lists/getbytitle('" + listName + "')/rootFolder?$select=ServerRelativeUrl",
type: "GET",
contentType: "application/json;odata=verbose",
headers: {
"Accept": "application/json;odata=verbose"
}
});
}
function createFolder(webUrl,listName,folderUrl,folderContentTypeId)
{
return $.ajax({
url: webUrl + "/_vti_bin/listdata.svc/" + listName,
type: "POST",
contentType: "application/json;odata=verbose",
headers: {
"Accept": "application/json;odata=verbose",
"Slug": folderUrl + "|" + folderContentTypeId
}
});
}
function createDocumentSet(webUrl,listName,docSetName)
{
return getListUrl(webUrl,listName)
.then(function(data){
var folderUrl = data.d.ServerRelativeUrl + '/' + docSetName;
return createFolder(webUrl,listName,folderUrl,'0x0120D520');
});
}
Usage
Create Document Set named Orders in Documents library:
createDocumentSet(webUrl,'Documents','Orders')
.done(function(data)
{
console.log('Document Set ' + data.d.Name + ' has been created succesfully');
})
.fail(
function(error){
console.log(JSON.stringify(error));
});

Getting Message "Sorry, this site hasn't been shared with you" while access url through REST in sharepoint 2013 app model

I am accessing a list through SharePoint 2013 App using REST, below is code snippet:
var executor = new SP.RequestExecutor(appweburl);
executor.executeAsync(
{
url: appweburl +
"/_api/SP.AppContextSite(#hostweburl)/Web/lists/getbytitle('SomeListName')/items?" +
"#hostweburl='" +
hostweburl + "'",
method: "GET",
headers: { "Accept": "application/json; odata=verbose" },
success: successHandler,
error: errorHandler
}
);
when i am debugging it, getting the complete URL from URL tag, but when i am going to access it through browser, it shows me message like "Sorry, this site hasn't been shared with you".
Why is this so happen while i have the full control over site collection.
Is there any configuration which we have to be made in APP?
Thanks.