Can't believe I'm stuck with a LOGIN :( hate when this happens.
Can somebody enlight me how to connect TF.EXE by using PAT password or in the best case an OAuth token?
I might add that I already have a Pat token and an OAuth token, not a problem while trying to get those, but every time I try this example:
TF.exe workspaces /collection:xxxx.visualstudio.com/xxxx /loginType:OAuth /login:.,MyPatTokenOrMyOauthToken /noprompt
I get the following response:
TF30063: You are not authorized to access xxxx.visualstudio.com\xxxx.
So, I Know command it's ok, because if I don't specify a login, a modal window prompts for credentials, and I tested already with that approach and works fine.
For the end, I might change everything to change tf.exe for the TFS api, but I'm unable to find same methods in the api (see reference: https://learn.microsoft.com/es-es/rest/api/vsts/?view=vsts )
If API has same methods than TF.exe, that will be useful, but so far I don't see same methods in the API.
Hope somebody has the solution for my problem.
Thanks in advance.
From my test, PAT token doesn't work in the following command, you have to get a OAuth token:
tf workspaces /collection:https://xxxx.visualstudio.com /loginType:OAuth /login:.,[OAuth token]
For the api that authenticate with Visual Studio Team Services (VSTS), you could refer to the examples in this link:
Here is an example getting a list of projects for your account:
REST API
using System.Net.Http;
using System.Net.Http.Headers;
...
//encode your personal access token
string credentials = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", "", personalAccessToken)));
ListofProjectsResponse.Projects viewModel = null;
//use the httpclient
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://{accountname}.visualstudio.com"); //url of our account
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials);
//connect to the REST endpoint
HttpResponseMessage response = client.GetAsync("_apis/projects?stateFilter=All&api-version=1.0").Result;
//check to see if we have a succesfull respond
if (response.IsSuccessStatusCode)
{
//set the viewmodel from the content in the response
viewModel = response.Content.ReadAsAsync<ListofProjectsResponse.Projects>().Result;
//var value = response.Content.ReadAsStringAsync().Result;
}
}
.Net Client Libraries
using Microsoft.TeamFoundation.Core.WebApi;
using Microsoft.VisualStudio.Services.Common;
...
//create uri and VssBasicCredential variables
Uri uri = new Uri(url);
VssBasicCredential credentials = new VssBasicCredential("", personalAccessToken);
using (ProjectHttpClient projectHttpClient = new ProjectHttpClient(uri, credentials))
{
IEnumerable<TeamProjectReference> projects = projectHttpClient.GetProjects().Result;
}
Add a screenshot:
Update:
I've tested with a new account, and the result is as below. If I remove /loginType and /login parameters, a window will pop up to ask me logon.
The screenshot without /loginType and /login parameters:
The screenshot with /loginType and /login parameters:
Related
I have a project setup like this:
React frontend
-> authenticates against...
Identity Server
-> which redirects to...
A Microsoft login
I'm using a Clients Credential Provider and it works great - the IS4 redirects to MS login, and then gets redirected with the access token back, which is then passed on to the React app.
Now, I've been tasked with creating a feature to change the user's password. I'm trying to do this by sending the old+new password to IS4, and then calling the MSGraphClient, but I couldn't make it work.
I've tried the Username/Password provider, because I have all the info needed, but I need to change stuff on the ActiveDirectory settings to make my app public. But even then, I don't like that solution.
I've also tried with the On-behalf-of provider, this is the code:
var scopes = new[] { "User.Read",
"Directory.AccessAsUser.All" };
// Multi-tenant apps can use "common",
// single-tenant apps must use the tenant ID from the Azure portal
var tenantId = "~~";
// Value from app registration
var clientId = "~~";
var clientSecret = "~~";
var options = new TokenCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
// This is the incoming token to exchange using on-behalf-of flow
var oboToken = HttpContext.Request.Headers.First(h => h.Key == "Authorization").Value.ToString().Replace("Bearer ", "");
var cca = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantId)
.WithClientSecret(clientSecret)
.Build();
// DelegateAuthenticationProvider is a simple auth provider implementation
// that allows you to define an async function to retrieve a token
// Alternatively, you can create a class that implements IAuthenticationProvider
// for more complex scenarios
var authProvider = new DelegateAuthenticationProvider(async (request) => {
// Use Microsoft.Identity.Client to retrieve token
var assertion = new UserAssertion(oboToken);
var result = await cca.AcquireTokenOnBehalfOf(scopes, assertion).ExecuteAsync();
request.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", result.AccessToken);
});
var graphClient = new GraphServiceClient(authProvider);
And it kinds of work, because the request is made, but the server throws an error:
AADSTS5002727: Invalid JWT header type specified. Allowed types: 'JWT','http://openid.net/specs/jwt/1.0'.
I checked my token on JWT.io, and the typ is at+jwt... Why? Why is MS sending me a type of token that it doesn't support? How can I change it from my side so it's a plain JWT?
Thanks for any advice, and any other possible solution for this.
To resolve the error "AADSTS5002727: Invalid JWT header type specified. Allowed types: JWT,http ://openid.net/specs/jwt/1.0" , please try the below if helpful:
Please check the version of .Net core you are currently using to generate the token. Try using .Net core 2.2 with IS4.
Try setting IdentityServerOptions.AccessTokenJwtType to empty string or JWT on IdentityServerOptions.
In the mentioned code, replace var oboToken variable directly with the value of token.
var oboToken = "JWT_TOKEN_TO_EXCHANGE";
Please note the below point from MsDoc :
Don't attempt to validate or read tokens for any API you don't own,
including the tokens in this example, in your code. Tokens for Microsoft services can use a special format that will not validate as
a JWT, and may also be encrypted for consumer (Microsoft account)
users
If still the error persists, try upgrading clients to a new token validation library that works with the new style tokens.
Please check whether the below links give you any pointer to resolve the issue:
JWT Token always Invalid · Issue #905 · openiddict/openiddict-core · GitHub
IdentityServer .Net Core 3.0 & Owin/Katana Token validation · Issue #3705 · IdentityServer/IdentityServer4 · GitHub
We have mobile app developed in react native in which we have to implement Google and Facebook login. We have RN libraries using which we will get Facebook and Google user's profile details. But our requirement is like we need to just pass the access token to web api which is developed in asp.net core, and using the access token we have to verify the access token in asp.net core web api and fetch the user's profile details using Facebook or Google Apis.
It is working fine for Facebook api, below is the code for the same
var httpClient = new HttpClient { BaseAddress = new Uri("https://graph.facebook.com/v2.9/") };
var response = await httpClient.GetAsync($"me?access_token={token}&fields=id,name,email,first_name,last_name,age_range,birthday,gender,locale,picture");
Similarly, when we pass access token(id_token) for google, it is not working, and below is code for the same,
var token ="eyJhb.eyJpc....";
var httpClient1 = new HttpClient { BaseAddress = new Uri("https://www.googleapis.com/oauth2/v3/") };
var response1 = await httpClient1.GetAsync($"userinfo?access_token={token}");
Can anyone please assist me, how can we verify the access token and fetch the user's profile details?
Thanks In Advance.
You can verify your "id_token" and get some user profile details at the same time by making GET request to the next endpoint:
"https://oauth2.googleapis.com/tokeninfo?id_token=XYZ123".
var token ="eyJhb.eyJpc....";
var httpClient1 = new HttpClient { BaseAddress = new Uri("https://oauth2.googleapis.com/") };
var response1 = await httpClient1.GetAsync($"tokeninfo?id_token={token}");
as described in google documentation "https://developers.google.com/identity/sign-in/web/backend-auth" (Calling the tokeninfo endpoint) section.
However in case you want to access google api services:
"id_token" is not meant to be used to access google api services, then you will need to have an "access_token" not an "id_token", you can follow the next documentation for that :
"https://developers.google.com/identity/protocols/oauth2"
I tried using YouTube Data API.
I really took a good look at everything I found on the internet. The code itself isn't the problem, but I did not find out, where to use this code. Do I simply create a python file (in Visual Studio Code for example) and run it there? Because it didn't work when I tried this...
I also saw many people using the API with the commander only, others used something in chrome (localhost:8888...). So I don`t really know what's the way to go or what I should do.
Thanks for any help :)
Best regards!
I'm not a python developer but as a guess you could start here:
https://developers.google.com/youtube/v3/quickstart/python
using pip to install the dependencies you need.
You should be able to create a simple python file that authenticates with the API and then calls a method on the on the google api client and then output it. There are some examples here:
https://github.com/youtube/api-samples/blob/master/python/
using System;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Upload;
using Google.Apis.Util.Store;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
namespace Google.Apis.YouTube.Samples
{
/// <summary>
/// YouTube Data API v3 sample: upload a video.
/// Relies on the Google APIs Client Library for .NET, v1.7.0 or higher.
/// See https://code.google.com/p/google-api-dotnet-client/wiki/GettingStarted
/// </summary>
internal class UploadVideo
{
[STAThread]
static void Main(string[] args)
{
Console.WriteLine("YouTube Data API: Upload Video");
Console.WriteLine("==============================");
try
{
new UploadVideo().Run().Wait();
}
catch (AggregateException ex)
{
foreach (var e in ex.InnerExceptions)
{
Console.WriteLine("Error: " + e.Message);
}
}
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
private async Task Run()
{
UserCredential credential;
using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
// This OAuth 2.0 access scope allows an application to upload files to the
// authenticated user's YouTube channel, but doesn't allow other types of access.
new[] { YouTubeService.Scope.YoutubeUpload },
"user",
CancellationToken.None
);
}
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
});
var video = new Video();
video.Snippet = new VideoSnippet();
video.Snippet.Title = "Default Video Title";
video.Snippet.Description = "Default Video Description";
video.Snippet.Tags = new string[] { "tag1", "tag2" };
video.Snippet.CategoryId = "22"; // See https://developers.google.com/youtube/v3/docs/videoCategories/list
video.Status = new VideoStatus();
video.Status.PrivacyStatus = "unlisted"; // or "private" or "public"
var filePath = #"REPLACE_ME.mp4"; // Replace with path to actual movie file.
using (var fileStream = new FileStream(filePath, FileMode.Open))
{
var videosInsertRequest = youtubeService.Videos.Insert(video, "snippet,status", fileStream, "video/*");
videosInsertRequest.ProgressChanged += videosInsertRequest_ProgressChanged;
videosInsertRequest.ResponseReceived += videosInsertRequest_ResponseReceived;
await videosInsertRequest.UploadAsync();
}
}
void videosInsertRequest_ProgressChanged(Google.Apis.Upload.IUploadProgress progress)
{
switch (progress.Status)
{
case UploadStatus.Uploading:
Console.WriteLine("{0} bytes sent.", progress.BytesSent);
break;
case UploadStatus.Failed:
Console.WriteLine("An error prevented the upload from completing.\n{0}", progress.Exception);
break;
}
}
void videosInsertRequest_ResponseReceived(Video video)
{
Console.WriteLine("Video id '{0}' was successfully uploaded.", video.Id);
}
}
}
Make sure you have python installed on your PC
Create a project: Google’s APIs and Services dashboard
Enable the Youtube v3 API: API Library
Create credentials: Credentials wizard
Now you need to get an access token and a refresh token using the credentials you created
Find an authentication example in one of the following libraries:
https://github.com/googleapis/google-api-python-client
https://github.com/omarryhan/aiogoogle (for the async version)
Copy and paste the client ID and client secret you got from step 4 and paste them in the authentication example you found in step 6 (Should search for an OAuth2 example), this step should provide with an access token and a refresh token
Copy and paste a Youtube example from either:
https://github.com/googleapis/google-api-python-client
https://github.com/omarryhan/aiogoogle (for the async version)
Replace the access token and refresh token fields with the ones you got.
Now you should be able to run the file from any terminal by typing:
python3 yourfile.py
[EDIT]
The API key is not the same as the access token. There are 2 main ways to authenticate with Google APIs:
Access and refresh token
API_KEY.
API key won't work with personal info. You need to get an access and refresh token for that (method 1).
Once you get an access token, it acts in a similar fashion to the API_KEY you got. Getting an access token is a bit more complicated than only working with an API_KEY.
A refresh token is a token you get with the access token upon authentication. Access tokens expire after 3600 seconds. When they expire, your authentication library asks Google's servers for a new access token with the refresh token. The refresh token has a very long lifetime (often indefinite), so make sure you store it securely.
To get an access token and a refresh token (user credentials), you must first create client credentials. Which should consists of 1. a client ID and 2. a client secret. These are just normal strings.
You should also, set a redirect URL in your Google app console in order to properly perform the OAuth2 flow. The OAuth2 flow is the authentication protocol that many APIs rely on to allow them to act on a user's account with the consent of the user. (e.g. when an app asks you to post on your behalf or control your account on your behalf, it typically will use this protocol.)
Aiogoogle's docs does a decent job in explaining the authentication flow(s) available by Google.
https://aiogoogle.readthedocs.io/en/latest/
But this is an async Python library. If you're not familiar with the async syntax, you can read the docs just to get a general idea of how the authentication system works and then apply it to Google's sync Python library.
About point no.6. The links I posted with Aiogoogle being one of them, are just client libraries that help you access Google's API quicker and with less boilerplate. Both libraries have documentation, where they have links to examples on how to use them. So, open the documentation, read it, search for the examples posted, try to understand how the code in the example(s) work. Then maybe download it and run it on your own machine.
I recommend that your read the docs. Hope that helps.
I'm testing out IdentityServer4, going through the documentation in order to learn more about OAuth2, OpenId Connect and Claim-based authentication, all of which I'm new at. However, some of the example code behaves weirdly and I can't figure out why...
So from my understanding, when given permission to access user data, the client can reach out to the UserInfo endpoint, which contains data such as claims, etc.
In IdentityServer4 there's even a setting
GetClaimsFromUserInfoEndpoint
that the documentation recommends we set to true.
So I'm following the IdentityServer4 startup guides and everything works perfectly until a point. This Quickstart contains the example code provided, although I'm assuming that I'm missing something obvious and seeing the code is not required.
Based on the openId Configuration page of the running server, the userinfo endpoint is located at
http://localhost:5000/connect/userinfo and when I try to access it via the browser I'm seeing a navbar which claims I'm logged in, but the body of the page is a signin prompt. Looks weird but I'm assuming that this is because I'm logged in at localhost:5000 (IdentityServer4), but I'm not sending the userId token which I got for the client on localhost:5002.
So I wrote the following code on my client app:
public async Task<IActionResult> GetData()
{
var accessToken = HttpContext.Authentication.GetTokenAsync("access_token").Result;
HttpClient client = new HttpClient();
client.SetBearerToken(accessToken);
var userInfo = await client.GetStringAsync("http://localhost:5000/connect/userinfo");
return Content(userInfo);
}
Here I know that GetTokenAsync("access_token") should work as it's used in other places in the example project by the client app that connect to an API. However, the responce I'm getting is again the layout page of IdentityServer and a log in prompt.
Any idea what my mistake is and how to access the UserInfo endpoint?
Edit: removed thread-blocking so that I don't show strangers shameful test code
Ok, so it turns out that this code should have a simplified version, namely:
UserInfoClient uic = new UserInfoClient("http://localhost:5000", idToken);
var result = await uic.GetAsync();
return Content(JsonConvert.SerializeObject(result.Claims));
Yet, the problem persists, even the encapsulated code inside UserInfoClient hits the brick wall of "no user endpoint data, just the layout for the example website".
It's probably little late to answer, but for anyone who is still stumbling upon this, try this ---
var accessToken = await HttpContext.Authentication.GetTokenAsync("access_token");
var client = new HttpClient();
client.SetBearerToken(accessToken);
var userInfoClient = new UserInfoClient("http://localhost:5000/connect/userinfo");
var response = await userInfoClient.GetAsync(accessToken);
var claims = response.Claims;
You can also get the list of claims on the client app like -
var claims = HttpContext.User.Claims.ToList();
without calling the endpoint.
I'm trying to initiate a session with the Shoeboxed API via Google Apps Script. I hoped I could use Apps Script internal library to access it but I'm having issues. Here is my attempt:
function testAPI() {
var consumerKey = '';
var consumerSecret = '';
var oauthConfig = UrlFetchApp.addOAuthService('shoeboxed');
oauthConfig.setAccessTokenUrl(
'https://id.shoeboxed.com/oauth/token');
oauthConfig.setRequestTokenUrl(
'https://id.shoeboxed.com/oauth/token');
oauthConfig.setAuthorizationUrl(
'https://id.shoeboxed.com/oauth/authorize');
oauthConfig.setConsumerKey(consumerKey);
oauthConfig.setConsumerSecret(consumerSecret);
var options = {
'oAuthServiceName' : 'shoeboxed',
'oAuthUseToken' : 'always'
};
var url = 'https://api.shoeboxed.com/v2/user';
var response = UrlFetchApp.fetch(url, options);
Logger.log("Response: " + response.getContentText());
}
It's failing at the point where it attempts to fetch user data via the API url with an authorization failed message. I'm not sure what I'm doing wrong. Information about the API and OAuth can be found here: https://github.com/Shoeboxed/api/blob/master/sections/authentication.md
New method:
It looks like that API requires OAuth2, but the UrlFetchApp.addOAuthService method only works with the older version of OAuth.
There's a new method ScriptApp.newStateToken() which can be used in combination with OAuth2, but it requires more manual/explicit control over the OAuth2 steps. It generates a state token.
A minor detail on that method:
Note that when you construct URLs, the state token should passed as a URL parameter on the .../authorize URL, not embedded as a URL parameter within the .../usercallback URL.
For example:
You would want to redirect the user to:
https://id.shoeboxed.com/oauth/authorize?client_id=<your client id>&response_type=code&scope=all&redirect_uri=<your site>&state=<CSRF token>
where redirect_uri is:
https://script.google.com/macros/d/1234567890abcdefghijklmonpqrstuvwxyz/usercallback
When the user clicked authorize, Shoeboxed should redirect them to:
https://script.google.com/macros/d/1234567890abcdefghijklmonpqrstuvwxyz/usercallback?state=<CSRF token>
oauth2 support for the shoeboxd API has just been added to the cEzyOauth2 Google Apps Script library.
You can copy the pattern to your app and include the library as described here
It uses the statetoken as described by Steve Lieberman, and takes care of the oauth2 conversation, token handling and refreshing automatically.