Youtube API V3 authentication. Uploading to shared account - c#

I'm trying to update an existing Asp.Net application using C# to avoid the Youtube V2 API deprecation. The 1st challenge is figuring out how to authenticate using the new API.
This is how the application currently authenticates:
string developerKey = (My Developer Key here);
string username = (My username here);
string password = (My password here);
ytService = new YouTubeService("SampleApplication", developerKey);
ytService.setUserCredentials(username, password);
I've researched, and reviewed the V3 API examples, and they all use OAuth2 to Authenticate like this:
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
);
}
However, that prompts the user to login to their own Google account, and for this application we want all uploaded videos to go into our account. Therefore I would like to continue using the Public API access if possible, and I can't find any examples of this for the V3 APIs.
I've also investigated OAuth2 Service Account, but for the life of me can not figure out how to authenticate using one of those.
Can you help point me in the right direction?

I faced a similar issue recently, except I was working with a shared Google Calendar instead of a Youtube account. What I ended up doing was adding an authorize button that only I could see, which was hooked up to the initial authorize request when clicked. It opened the popup from Google, I gave the permission from the shared account, and I stored the returned access token, refresh token, and expiration date in the database.
Then, whenever users need to view the shared calendar, I just have logic that says if the expiration date has passed, use the stored refresh token to request a new access token and reset the expiration date, then I use the access token to make my Google Calendar API call (getting the events list, in this case).

Unfortunately, V3 of the YouTube API does not support service accounts, and oAuth2 is the only authentication method.
One of the reasons behind this is to do away with having multiple individuals upload into a single account; the legal liability (for both YouTube and app developers) is too great. The only current alternative is the YouTube direct lite program (which has multiple clients ... see YouTube's Github page to find them), which gives you a simple management/admin pane for autogenerating a playlist based on users uploading videos into their own accounts (in other words, they upload the video through your app, to their account, but your account gets a record of it and manages what can/can't appear on the list).

Related

Can you use refresh tokens in Blazor Server app with cookie authentication and no Api's and no local storage?

Just looking into Blazor and my company decided to explore Server Side App (no WASM).
I got the basic project setup and because we want the app to work with out current user db, I modified our User Server (Microservice) to authenticate a user and return a JWT token. In the Login page once we have the token, we authenticate and the cookie is stored.
var auth = await HttpContext.AuthenticateAsync();
auth.Properties.RedirectUri = "/";
var accessToken = new AuthenticationToken()
{
Name = OpenIdConnectParameterNames.AccessToken,
Value = Token
};
AuthenticationToken[] tokens = { accessToken };
auth.Properties.StoreTokens(tokens);
auth.Properties.IsPersistent = true;
await HttpContext.SignInAsync("Cookies", auth.Principal, auth.Properties);
So I want to say at this point everything is working fine.
Now back to the question. I wish to increase security by using Refresh Tokens, and this is where I hit the problem. Every tutorial or guide I google/bing that talks about Refresh tokens goes by the basses that I am using WASM or an API that I call. There are a few articles that say to use Local Storage but then that can bring up a whole new conversation of Local Storage vs Cookies vs Session, and I was hoping to stick with most of the built in stuff.
I understand that Server-Side issues with the HTTP context, hence why the login page is done on a .cshtml page and the fact that the cookies are set as HTTPOnly so JavaScript cannot access them.
So, is there away to do it?
Just to clarify, this is a Server-Side app only, no client apps or API's or anything else and I wish to stick with most of the built in stuff using cookies.

Create Microsoft Graph GraphServiceClient with user/password unattended

I am creating a console application that connects to Microsoft Graph using the Microsoft Graph API (as shown in https://github.com/microsoftgraph/console-csharp-connect-sample).
Everything is working fine, but I wonder if there is a way where I can authenticate a user (when I already know their user/password) without them needing to manually enter their credentials on the "Sing in to your account" window rendered on the desktop.
The idea is basically to run the application unattended, so there is no need for the user to be entering their credentials when the application starts. I can´t find any relevant information on the subject.
Is that even possible?
EDIT
After following the link #DanSilver posted about geting access without a user, I tried the sample suggested in that link (https://github.com/Azure-Samples/active-directory-dotnet-daemon-v2). Although that is an MVC application that forces users to authenticate (precisely what I wanted to avoid) I have managed to use part of the authentication code in that sample with my console application. After giving authorization to the application manually through a request to https://login.microsoftonline.com/myTenantId/adminconsent I can create a GraphServiceClient in my console app that connects to Graph without user interaction. So I mark the answer as valid.
Just in case someone is in the same situation, the GraphServiceclient is created as:
GraphServiceClient graphServiceClientApplication = new GraphServiceClient("https://graph.microsoft.com/v1.0", new DelegateAuthenticationProvider(
async (requestMessage) =>
{
string clientId = "yourClientApplicationId";
string authorityFormat = "https://login.microsoftonline.com/{0}/v2.0";
string tenantId = "yourTenantId";
string msGraphScope = "https://graph.microsoft.com/.default";
string redirectUri = "msalXXXXXX://auth"; // Custom Redirect URI asigned in the Application Registration Portal in the native Application Platform
string clientSecret = "passwordGenerated";
ConfidentialClientApplication daemonClient = new ConfidentialClientApplication(clientId, String.Format(authorityFormat, tenantId), redirectUri, new ClientCredential(clientSecret), null, new TokenCache());
AuthenticationResult authResult = await daemonClient.AcquireTokenForClientAsync(new string[] { msGraphScope });
string token = authResult.AccessToken;
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
}
));
One idea is using the "app only" authorization flow. The idea is that you can have long running apps access the Microsoft Graph without user authentication. The main difference is instead of the access token granting access to a particular user, it grants your app access to resources that you've consented to in advance. There will be no user login dialog and you can programmatically fetch access tokens to call the Graph API.
To reiterate that these tokens aren't for a particular user, consider making a GET request to 'https://graph.microsoft.com/v1.0/me'. This will return an error since the access token isn't for a particular user and "me" doesn't mean anything. Requests should be sent with full user ids "like graph.microsoft.com/users/someuser#contosos.com".
More information on this can be found at the Get access without a user documentation page.
Another idea is to let the user authenticate the first time they use your app and then store a refresh token. These tokens live longer (a few months IIRC) and then you won't need to prompt for user consent each time the app runs. Refresh tokens can be exchanged for access tokens that live 60 minutes and those can be used to call Graph API on behalf of users.
More info on refresh tokens: https://developer.microsoft.com/en-us/graph/docs/concepts/auth_v2_user#5-use-the-refresh-token-to-get-a-new-access-token
I did want to come back out here and share, since I ran into this problem yesterday, and the idea of granting read/write mailbox access for my application... to EVERYONE'S EMAIL BOX IN THE ENTIRE ORGANIZATION... was way over the top for my needs. (And that is exactly what happens when you start talking about granting Application level permissions instead of delegated permissions to your registered app).
It's a simple use case: I had a nightly process that needed to automate sending of emails from a shared mailbox using a traditional AD service account.
Thankfully... even though they are on the march to eliminate passwords (lol)... someone at Microsoft still recognizes my use case, and it's lack of apples-to-apples alternatives in Azure AD. There is still an extension method we can lean on to get the job done:
private AuthenticationContext authContext = null;
authContext = new AuthenticationContext("https://login.microsoftonline.com/ourmail.onmicrosoft.com",
new TokenCache());
result = authContext.AcquireTokenAsync("https://graph.microsoft.com/",
"12345678-1234-1234-1234-1234567890",
new UserPasswordCredential(
Environment.GetEnvironmentVariable("UID", EnvironmentVariableTarget.User),
Environment.GetEnvironmentVariable("UPD", EnvironmentVariableTarget.User)
)).Result;
You can replace those GetEnvironmentVariable calls with your Username (UID) and Password (UPD). I just stuff them in the environment variables of the service account so I didn't have to check anything into source control.
AcquireTokenAsync is an extension method made available from the Microsoft.IdentityModel.Clients.ActiveDirectory namespace. From there, it's a simple business to fire up a GraphClient.
string sToken = result.AccessToken;
Microsoft.Graph.GraphServiceClient oGraphClient = new GraphServiceClient(
new DelegateAuthenticationProvider((requestMessage) => {
requestMessage
.Headers
.Authorization = new AuthenticationHeaderValue("bearer", sToken);
return Task.FromResult(0);
}));
The last bit of magic was to add these permissions to Application registration I created in Azure AD (where that GUID came from). The application has be defined as a Public client (there's a radio button for that towards the bottom of the authentication tab). I added the following 5 DELEGATED permissions (NOT application permissions):
Microsoft Graph
1. Mail.ReadWrite.Shared
2. Mail.Send.Shared
3. User.Read
4. email
5. openid
Since user consents are actually blocked in our organization, another permissions admin had to review my application definition and then do an admin level grant of those rights, but once he did, everything lit up and worked like I needed: limited access by a service account to a single shared mailbox, with the actual security of that access being managed in Office 365 and not Azure AD.

Authorized failed when run Google.Apis.YouTube.Samples.Upload project from Github

I am new from youtube api. I would like to have a console program schedule upload videos to youtube without authorization everytime.
I see many posts and videos but not the complete story, I download the sample from Github and try to run it. It failed.
i follow the guide that
。create OAuth client ID and application type choose Other
。download code from github
。click allow in browser, lets youtube api manage youtube account.
img1 - client secret download from console developer and code from github
img2 - error message told me unauthorized
which part is wrong, how can i fix it? Please help!
The error your getting means that you have not authenticated the YouTubeService there must be something wrong with your credentials which i cant see because you haven't posted all your code.
The YouTube API does not support service accounts. You will need to authenticate your code at least once. This is done via a web browser and the client library you are using will store the credentials for "user" in %appData% directory.
The tutorial you are following doesn't say anything about not needing authentication. This is the code for the auth.
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 for full read/write access to the
// authenticated user's account.
new[] { YouTubeService.Scope.Youtube },
"user",
CancellationToken.None,
new FileDataStore(this.GetType().ToString())
);
}

Service Account Google Analytics OAuth AccessType = Offline C#

I've got credentials of an account with access to Google Analytics,
I'm looking to utilise the Analytics Core Reporting API http://code.google.com/apis/analytics/docs/gdata/home.html
I've found examples which use username/password calling setUserCredentials, but have seen comments this is less secure/has a low request limit (And doesn't exist in the lastest client).
Plus I've seem examples which use oauth, but require user interaction and grant access to the users google account.
However I'm looking to run a service which doesn't require any user interaction, and connects to a predefined google account (un-related to the user viewing it).
I can then store the results in a database, and end users can query the results from the database.
I've seen information about using AccessType = Offline when you first login, which then returns an access token and a refreshtoken.
http://code.google.com/apis/accounts/docs/OAuth2WebServer.html#offline
In my example though, the end user will never login to the application.
Could I have a seperate admin application which gets a refresh token, and stores the refresh token in the config/lookup table?
Then the main application can use the refresh token pulling from the config/lookup table, and get an access token to be able to query the Google Analytics account.
I'm looking for a C# example which uses AccessType = Offline, and seperates out the fetching of the refresh token and using the refresh token to get an access token to query the google analytics account.
Create your app https://code.google.com/apis/console/
For you App, turn on access to Google Analytics, and create an OAuth 2.0 client ID for your website.
Browse to:
https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=YOUR_APP_ID.apps.googleusercontent.com&access_type=offline&scope=https://www.googleapis.com/auth/analytics.readonly&redirect_uri=HTTP://YOUR_CALL_BACK_URL
Having changed YOUR_APP_ID, YOUR_CALL_BACK_URL to the relevant values.
Important to include access_type=offline.
Press Grant Access, this will redirect to HTTP://YOUR_CALL_BACK_URL?code=THIS_IS_YOUR_CODE. Copy the code in the URL.
With the code, request the Refresh Token using CMD prompt.
curl -d "code=THIS_IS_YOUR_CODE&client_id=YOUR_APP_ID.apps.googleusercontent.com&client_secret=YOUR_APPS_SECRET_CODE&redirect_uri=HTTP://YOUR_CALL_BACK_URL&grant_type=authorization_code" https://accounts.google.com/o/oauth2/token
Having changed THIS_IS_YOUR_CODE, YOUR_APP_ID, YOUR_APPS_SECRET_CODE, YOUR_CALL_BACK_URL to the relevant values.
Record the refresh_token returned.
Download the latest version of the Core Reporting V3.0 .net libraries
http://code.google.com/p/google-api-dotnet-client/wiki/Downloads
There is a bug in the current version of Google.Apis.Analytics.v3.cs, to fix this copy the code in this file to your local solution (And don’t reference Google.Apis.Analytics.v3.bin)
http://code.google.com/p/google-api-dotnet-client/source/browse/Services/Google.Apis.Analytics.v3.cs?repo=samples&name=20111123-1.1.4344-beta
And change the property Dimensions from a List<system.string> to a string.
Or you'll get an error like me and this guy did http://www.evolutiadesign.co.uk/blog/using-the-google-analytics-api-with-c-shar/
You can then use your Refresh Token, to generate you an Access Token without user interaction, and use the Access Token to run a report against Google Analytics.
using System;
using DotNetOpenAuth.OAuth2;
using Google.Apis.Authentication.OAuth2;
using AnalyticsService = Google.Apis.Analytics.v3.AnalyticsService;
class Program
{
public static void Main()
{
var client = new WebServerClient(GoogleAuthenticationServer.Description, "YOUR_APP_ID.apps.googleusercontent.com", "YOUR_APPS_SECRET_CODE");
var auth = new OAuth2Authenticator<WebServerClient>(client, Authenticate);
var asv = new AnalyticsService(auth);
var request = asv.Report.Get("2012-02-20", "2012-01-01", "ga:visitors", "ga:YOUR_GOOGLE_ANALYTICS_ACCOUNT_ID");
request.Dimensions = "ga:pagePath";
request.Sort = "-ga:visitors";
request.MaxResults = 5;
var report = request.Fetch();
Console.ReadLine();
}
private static IAuthorizationState Authenticate(WebServerClient client)
{
IAuthorizationState state = new AuthorizationState(new string[]{}) { RefreshToken = "REFRESH_TOKEN" };
client.RefreshToken(state);
return state;
}
}
Great Answer Ian and it helped me to get going in the correct Direction more than any other answer I could find online. Something must have changed in the AnalyticsService object because the line:
var request = asv.Report.Get("2012-02-20", "2012-01-01", "ga:visitors", "ga:YOUR_GOOGLE_ANALYTICS_ACCOUNT_ID");
did not work for me and I had to use the following:
var request = asv.Data.Ga.Get("ga:YOUR_GOOGLE_ANALYTICS_ACCOUNT_ID", "2012-01-01", "2012-02-20", "ga:visitors");
Hopefully this will help others like your answer helped me. Thanks!
Ian's answer helped me a lot but I kept getting an error running the curl command. Did some research and found that the steps to get the access code and refresh token can be made easier by going to https://code.google.com/oauthplayground/ and checking your oAuth configuration settings. Top right of the page there is a settings button. selected "Use your own OAuth credentials". You can get your access code and request a refresh token all from here.
Hope this helps.
You can manually get a refresh token from the OAuth Playground.
If you are needing a refresh token for a Service Account as I was, make sure you
Click on the settings on the right.
Check Use your own OAuth credentials
Fill in your Client ID and Secret
Close the settings
Click the Refresh button on step 2
Then save the refresh token for use in your app

Google Calendar V3 2 Legged authentication fails

I'm trying to create web page that access the (business) private calendar of the company and insert events if the time slot is available. Still I'm facing an authentication problem.
The API manual states that I should use an API key and Oauth2LeggedAuthenticator, so I did all this and the request that is fired is quite okey (it has a oauth token and such) But still the response is an exception with Invalid Credentials; Easy to say is that my credentials are wrong, still clientID, clientSecret and API Key are valid; I doubt the 2 last params of the 2legged authenticater, is this correct?
var provider = new NativeApplicationClient(GoogleAuthenticationServer.Description);
provider.ClientIdentifier = ClientCredentials.ClientID;
provider.ClientSecret = ClientCredentials.ClientSecret;
var authenticator =
new OAuth2LeggedAuthenticator(ClientCredentials.ClientID, ClientCredentials.ClientSecret, "myworkusername", "workdomain.com");
Google.Apis.Calendar.v3.CalendarService service = new Google.Apis.Calendar.v3.CalendarService(authenticator);
service.Key = ClientCredentials.ApiKey;
var result = service.CalendarList.List().Fetch();
Assert.IsTrue(result.Items.Count > 0);
NB: At the time of writing you can only used 2-legged authentication with Google Apps for Business/Eduction, this won't work on personal accounts as there's no way to get an OAuth 1.0 key/secret pair, you will have to use online authentication at least once (but you can use the out-of-browser option so you don't have to create a dedicated page).
Your code is correct apart from you don't need the first three lines relating to the NativeApplicationClient. This is most likely failing because you haven't properly set the OAuth keys, this causes 401s.
The other thing that causes 401s is using "matt#example.com" instead of "matt" as the username, the username is without including your domain.
To setup OAuth follow the instructions in this article from Google.
The most important parts to note are "Allow access to all APIs" must be unchecked and you have to individually grant access to all the APIs. If this hasn't been done you will get a 401 Invalid Credentials error. You then also need to turn those services on in the api console. If the api console step hasn't been done you will get a different error of 403 Daily Limit Exceeded.
This will cause you problems if you were previously relying on the "Allow access to all APIs" to use various services, you will have to grant them all individually as far as I understand it to use the v3 APIs. This seems to have been confirmed by google (4th reply by Nicolas Garnier) and is supposedly a bug, but that is an old post so it looks as if it's here to stay.
For reference once this has been done, this code will work, which in essence is the same as yours:
var auth = new OAuth2LeggedAuthenticator(domainName, consumerSecret, usernameWithoutDomain, domainName); //domainName is presently used as the OAuth ConsumerKey for Google's 2legged OAuth
var service = new CalendarService(auth);
service.Key = serviceKey;
var results = service.CalendarList.List().Fetch();
Console.WriteLine(results.Items.Count);
So in summary:
In Google Apps "Manage this Domain" > "Advanced Tools"
Using "Manage OAuth domain key" enable key, generate secret, uncheck "Allow access to all APIs".
Using "Manage third party OAuth Client access" enable the APIs you want access to using your domain as "Client Name" and the APIs you want to access e.g. "http://www.google.com/calendar/feeds/" for the calendar.
Then finally create a project in the API console, use the APIKey as the serviceKey in the above example and turn on the APIs you need to access.
I am answering this as I kept hitting this question when I was trying to find out why my code was constantly returning 401s. Hope this helps someone as the Google instructions are awful and scattered all over the place at the moment.

Categories