Google Admin Audit api - how do I get the CustomerID in C# - c#

I am new to the Google API and web based programming so excuse my general ignorance. I am trying to use the Google Admin Audit APIs and have not found an example of how to make the call to get the ClientID to be able to make one of the Google API calls that require the CustomerID - like Admin Auditing. I have run other simple examples like retrieving Tasks list etc but the issue is that these types of calls do not require you to use a Customer ID.
I have all the right includes as per other Google API examples plus Google.Apis.Audit.v1 required for auditing. I have enabled the provisioning API in my admin console and I have created a new client based project in the API console and also enabled the Audit API service for the project.
Here is generally what I am doing:
public IAuthenticator GetServiceInterface()
{
ClientProvider = new NativeApplicationClient(GoogleAuthenticationServer.Description);
ClientProvider.ClientIdentifier = "MY_CLIENT_ID";
ClientProvider.ClientSecret = "MY_CLIENT_SECRET";
IAuthenticator IAuth = new OAuth2Authenticator<NativeApplicationClient>(ClientProvider, GetAuthorization);
return IAuth;
}
private IAuthorizationState GetAuthorization(NativeApplicationClient client)
{
string[] ScopeList = new string[2] { "https://apps-apis.google.com/a/feeds/policies/", "https://www.googleapis.com/auth/apps/reporting/audit.readonly" };
IAuthorizationState IAuthState = new AuthorizationState(ScopeList);
IAuthState = AuthorizationMgr.RequestNativeAuthorization(client, ScopeList);
return IAuthState;
}
public void GoogleAuditTest()
{
//Get a Audit Service Interface
AuditService AuService = new AuditService( GetServiceInterface() );
????????????????????????????
...How do I get the CustomerID required by the audit service calls to list activities, etc
}
Am I missing something?

There is an easy way to get the customerId through the API's explorer (no coding needed).
Log onto the domain
Go to:
https://developers.google.com/apis-explorer/#s/reports/v1/reports.customerUsageReports.get?date=2013-05-18&_h=1&
Authorize OAuth
When you run the report, you'll see the customerId in the response.

Related

PayPalAPIInterfaceServiceService and SetExpressCheckout config / credential information

I created an app in my business account on this page:
https://developer.paypal.com/developer/applications/
When I click on the app I created I see the following:
Sandbox account, Client ID, and Secret.
I am trying to call SetExpressCheckout… but the documentation is unclear and examples are all over the map.
Basically I’m seeing things like:
var request = new SetExpressCheckoutReq() { … };
var config = new Dictionary<string, string>()
{
{ "mode", "sandbox" }, // some variations of these values
{ "clientId", "fromAbovePage" },
{ "clientSecret", "fromAbovePage" },
{ "sandboxAccount", "fromAbovePage" },
{ "apiUsername", "IDontKnow" },
{ "apiPassword", "IDontKnow" },
{ "apiSignature", "IDontKnow" }
};
var service = new PayPalAPIInterfaceServiceService(config);
var response = service.SetExpressCheckout(request, new SignatureCredential(config["apiUsername"], config["apiPassword"], config["apiSignature"]));
Also, kind of weird that credentials go into both the PayPalAPIInterfaceServiceService and the actual SetExpressCheckout call.
What are (and where do I get) the correct values for the above config? (the request itself I have pretty much figured out)
Note: PayPal support told me that I need to use Reference Transactons in order to charge varying amounts over potentially varying times without subsequent user interaction, if that is relevant.
I would love to see examples of this with the most recent API's if anyone has that information as well.
Thank you.
SetExpressCheckout is a legacy NVP API
For sandbox, it uses credentials from the "Profile" of a sandbox account in https://www.paypal.com/signin?intent=developer&returnUri=https%3A%2F%2Fdeveloper.paypal.com%2Fdeveloper%2Faccounts%2F
For live, https://www.paypal.com/api
ClientID/Secret credentials are used for the current v2/checkout/orders REST API, which at the moment does not have any public documentation for vaulting or reference transactions; it is for one time payments. You can find information for a server-side integration at https://developer.paypal.com/docs/checkout/reference/server-integration/
If you are using this REST API integration, create two routes --one for 'Set Up Transaction' and one for 'Create Transaction'. Then pair them with this approval flow: https://developer.paypal.com/demo/checkout/#/pattern/server

Service Account - Must be a G Suite domain user

This is my team's first foray into implementing functionality with Google Cloud and GSuite. After searching issues and the community I have not yet found what seems to be the proper path forward, or at least have not managed to get the desired functionality.
Background
We have a device/display that shows calendar and event information for a given/specific GSuite Room resource.
As part of displaying information regarding a specific event, we want to display attendee/invitee names.
Implementation
We are successfully calling the Calendar API using a service account. But, when the event information comes back, the attendee information only includes the attendee e-mail address.
The implementation is using the .NET Client libraries for Google.
We found a post directing that we then need to make follow up calls to get more attendee information to the People API.
When querying the People API utilizing the same service account we receive the error Must be a G Suite domain user.
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.6.1",
"title": "An error occured while processing your request.",
"status": 500,
"detail": "Google.Apis.Requests.RequestError\nMust be a G Suite domain user. [400]\nErrors [\n\tMessage[Must be a G Suite domain user.] Location[ - ] Reason[failedPrecondition] Domain[global]\n]\n",
"traceId": "|6007b977-42e9ca34c40a6cb0."
}
Below is the current hacked together code simply trying to make a successful query against the People Service API
public async Task<IList<Person>> GetAttendees(string tenant, string spaceEmail)
{
var serviceAccount = _redisCache.GoogleTenantCredentials.StringGet(tenant).ToString();
var svcDto = JsonConvert.DeserializeObject<ServiceAccountDto>(serviceAccount);
if (!string.IsNullOrEmpty(serviceAccount))
{
var credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(svcDto.ClientEmail)
{
Scopes = new[] { PeopleServiceService.Scope.DirectoryReadonly }
}.FromPrivateKey(svcDto.PrivateKey));
var svc = new PeopleServiceService(new BaseClientService.Initializer { HttpClientInitializer = credential });
var request = svc.People.ListDirectoryPeople();
request.ReadMask = "names,emailAddresses";
request.Sources = PeopleResource.ListDirectoryPeopleRequest.SourcesEnum
.DIRECTORYSOURCETYPEDOMAINPROFILE;
var result = await request.ExecuteAsync();
return result.People;
}
return null;
}
Researching the error, we found references to allowing a service account domain-wide delegation. Attempting to follow the documentation we have the setup below.
We spent some time with Google Support today and they directed us to Stack Overflow with the tag below.
Not sure where we are going wrong. Since this is a test/sandbox Google environment, one thing that has been our minds is if the GSuite domain is properly linked to the Cloud side, but we have been novices in attempting to verify that is correct as well.
You need to set up domain wide delegation to the service account this is the best documentation i am aware of. Perform G Suite Domain-Wide Delegation of Authority
Beyond that make sure you have delegated to a user.
var gsuiteUser = "user#YourDomain.com";
var credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(svcDto.ClientEmail)
{
User = gsuiteUser,
Scopes = new[] { PeopleServiceService.Scope.DirectoryReadonly }
}.FromPrivateKey(svcDto.PrivateKey));
To read from the people api you need a person whos data you are reading or you are just going to be reading the service accounts data of which it doesnt have any.

Google Data API Authorization Redirect URI Mismatch

Background
I am wanting to write a small, personal web app in .NET Core 1.1 to interact with YouTube and make some things easier for me to do and I am following the tutorials/samples in Google's YouTube documentation. Sounds simple enough, right? ;)
Authenticating with Google's APIs seems impossible! I have done the following:
Created an account in the Google Developer Console
Created a new project in the Google Developer Console
Created a Web Application OAuth Client ID and added my Web App debug URI to the list of approved redirect URIs
Saved the json file provided after generating the OAuth Client ID to my system
In my application, my debug server url is set (and when my application launches in debug, it's using the url I set which is http://127.0.0.1:60077).
However, when I attempt to authenticate with Google's APIs, I recieve the following error:
That’s an error.
Error: redirect_uri_mismatch
The redirect URI in the request, http://127.0.0.1:63354/authorize/,
does not match the ones authorized for the OAuth client.
Problem
So now, for the problem. The only thing I can find when searching for a solution for this is people that say
just put the redirect URI in your approved redirect URIs
Unfortunately, the issue is that every single time my code attempts to authenticate with Google's APIs, the redirect URI it is using changes (the port changes even though I set a static port in the project's properties). I cannot seem to find a way to get it to use a static port. Any help or information would be awesome!
NOTE: Please don't say things like "why don't you just do it this other way that doesn't answer your question at all".
The code
client_id.json
{
"web": {
"client_id": "[MY_CLIENT_ID]",
"project_id": "[MY_PROJECT_ID]",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_secret": "[MY_CLIENT_SECRET]",
"redirect_uris": [
"http://127.0.0.1:60077/authorize/"
]
}
}
Method That Is Attempting to Use API
public async Task<IActionResult> Test()
{
string ClientIdPath = #"C:\Path\To\My\client_id.json";
UserCredential credential;
using (var stream = new FileStream(ClientIdPath, FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { YouTubeService.Scope.YoutubeReadonly },
"user",
CancellationToken.None,
new FileDataStore(this.GetType().ToString())
);
}
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = this.GetType().ToString()
});
var channelsListRequest = youtubeService.Channels.List("contentDetails");
channelsListRequest.Mine = true;
// Retrieve the contentDetails part of the channel resource for the authenticated user's channel.
var channelsListResponse = await channelsListRequest.ExecuteAsync();
return Ok(channelsListResponse);
}
Project Properties
The Original Answer works, but it is NOT the best way to do this for an ASP.NET Web Application. See the update below for a better way to handle the flow for an ASP.NET Web Application.
Original Answer
So, I figured this out. The issue is that Google thinks of a web app as a JavaScript based web application and NOT a web app with server side processing. Thus, you CANNOT create a Web Application OAuth Client ID in the Google Developer Console for a server based web application.
The solution is to select the type Other when creating an OAuth Client ID in the Google Developer Console. This will have Google treat it as an installed application and NOT a JavaScript application, thus not requiring a redirect URI to handle the callback.
It's somewhat confusing as Google's documentation for .NET tells you to create a Web App OAuth Client ID.
Feb 16, 2018 Updated Better Answer:
I wanted to provide an update to this answer. Though, what I said above works, this is NOT the best way to implement the OAuth workflow for a ASP.NET solution. There is a better way which actually uses a proper OAuth 2.0 flow. Google's documentation is terrible in regards to this (especially for .NET), so I'll provide a simple implementation example here. The sample is using ASP.NET core, but it's easily adapted to the full .NET framework :)
Note: Google does have a Google.Apis.Auth.MVC package to help simplifiy this OAuth 2.0 flow, but unfortunately it's coupled to a specific MVC implementation and does not work for ASP.NET Core or Web API. So, I wouldn't use it. The example I'll be giving will work for ALL ASP.NET applications. This same code flow can be used for any of the Google APIs you've enabled as it's dependent on the scopes you are requesting.
Also, I am assuming you have your application set up in your Google Developer dashboard. That is to say that you have created an application, enabled the necessary YouTube APIs, created a Web Application Client, and set your allowed redirect urls properly.
The flow will work like this:
The user clicks a button (e.g. Add YouTube)
The View calls a method on the Controller to obtain an Authorization URL
On the controller method, we ask Google to give us an Authorization URL based on our client credentials (the ones created in the Google Developer Dashboard) and provide Google with a Redirect URL for our application (this Redirect URL must be in your list of accepted Redirect URLs for your Google Application)
Google gives us back an Authorization URL
We redirect the user to that Authorization URL
User grants our application access
Google gives our application back a special access code using the Redirect URL we provided Google on the request
We use that access code to get the Oauth tokens for the user
We save the Oauth tokens for the user
You need the following NuGet Packages
Google.Apis
Google.Apis.Auth
Google.Apis.Core
Google.apis.YouTube.v3
The Model
public class ExampleModel
{
public bool UserHasYoutubeToken { get; set; }
}
The Controller
public class ExampleController : Controller
{
// I'm assuming you have some sort of service that can read users from and update users to your database
private IUserService userService;
public ExampleController(IUserService userService)
{
this.userService = userService;
}
public async Task<IActionResult> Index()
{
var userId = // Get your user's ID however you get it
// I'm assuming you have some way of knowing if a user has an access token for YouTube or not
var userHasToken = this.userService.UserHasYoutubeToken(userId);
var model = new ExampleModel { UserHasYoutubeToken = userHasToken }
return View(model);
}
// This is a method we'll use to obtain the authorization code flow
private AuthorizationCodeFlow GetGoogleAuthorizationCodeFlow(params string[] scopes)
{
var clientIdPath = #"C:\Path\To\My\client_id.json";
using (var fileStream = new FileStream(clientIdPath, FileMode.Open, FileAccess.Read))
{
var clientSecrets = GoogleClientSecrets.Load(stream).Secrets;
var initializer = new GoogleAuthorizationCodeFlow.Initializer { ClientSecrets = clientSecrets, Scopes = scopes };
var googleAuthorizationCodeFlow = new GoogleAuthorizationCodeFlow(initializer);
return googleAuthorizationCodeFlow;
}
}
// This is a route that your View will call (we'll call it using JQuery)
[HttpPost]
public async Task<string> GetAuthorizationUrl()
{
// First, we need to build a redirect url that Google will use to redirect back to the application after the user grants access
var protocol = Request.IsHttps ? "https" : "http";
var redirectUrl = $"{protocol}://{Request.Host}/{Url.Action(nameof(this.GetYoutubeAuthenticationToken)).TrimStart('/')}";
// Next, let's define the scopes we'll be accessing. We are requesting YouTubeForceSsl so we can manage a user's YouTube account.
var scopes = new[] { YouTubeService.Scope.YoutubeForceSsl };
// Now, let's grab the AuthorizationCodeFlow that will generate a unique authorization URL to redirect our user to
var googleAuthorizationCodeFlow = this.GetGoogleAuthorizationCodeFlow(scopes);
var codeRequestUrl = googleAuthorizationCodeFlow.CreateAuthorizationCodeRequest(redirectUrl);
codeRequestUrl.ResponseType = "code";
// Build the url
var authorizationUrl = codeRequestUrl.Build();
// Give it back to our caller for the redirect
return authorizationUrl;
}
public async Task<IActionResult> GetYoutubeAuthenticationToken([FromQuery] string code)
{
if(string.IsNullOrEmpty(code))
{
/*
This means the user canceled and did not grant us access. In this case, there will be a query parameter
on the request URL called 'error' that will have the error message. You can handle this case however.
Here, we'll just not do anything, but you should write code to handle this case however your application
needs to.
*/
}
// The userId is the ID of the user as it relates to YOUR application (NOT their Youtube Id).
// This is the User ID that you assigned them whenever they signed up or however you uniquely identify people using your application
var userId = // Get your user's ID however you do (whether it's on a claim or you have it stored in session or somewhere else)
// We need to build the same redirect url again. Google uses this for validaiton I think...? Not sure what it's used for
// at this stage, I just know we need it :)
var protocol = Request.IsHttps ? "https" : "http";
var redirectUrl = $"{protocol}://{Request.Host}/{Url.Action(nameof(this.GetYoutubeAuthenticationToken)).TrimStart('/')}";
// Now, let's ask Youtube for our OAuth token that will let us do awesome things for the user
var scopes = new[] { YouTubeService.Scope.YoutubeForceSsl };
var googleAuthorizationCodeFlow = this.GetYoutubeAuthorizationCodeFlow(scopes);
var token = await googleAuthorizationCodeFlow.ExchangeCodeForTokenAsync(userId, code, redirectUrl, CancellationToken.None);
// Now, you need to store this token in rlation to your user. So, however you save your user data, just make sure you
// save the token for your user. This is the token you'll use to build up the UserCredentials needed to act on behalf
// of the user.
var tokenJson = JsonConvert.SerializeObject(token);
await this.userService.SaveUserToken(userId, tokenJson);
// Now that we've got access to the user's YouTube account, let's get back
// to our application :)
return RedirectToAction(nameof(this.Index));
}
}
The View
#using YourApplication.Controllers
#model YourApplication.Models.ExampleModel
<div>
#if(Model.UserHasYoutubeToken)
{
<p>YAY! We have access to your YouTube account!</p>
}
else
{
<button id="addYoutube">Add YouTube</button>
}
</div>
<script>
$(document).ready(function () {
var addYoutubeUrl = '#Url.Action(nameof(ExampleController.GetAuthorizationUrl))';
// When the user clicks the 'Add YouTube' button, we'll call the server
// to get the Authorization URL Google built for us, then redirect the
// user to it.
$('#addYoutube').click(function () {
$.post(addYoutubeUrl, function (result) {
if (result) {
window.location.href = result;
}
});
});
});
</script>
As referred here, you need to specify a fix port for the ASP.NET development server like How to fix a port number in asp.NET development server and add this url with the fix port to the allowed urls. Also as stated in this thread, when your browser redirects the user to Google's oAuth page, you should be passing as a parameter the redirect URI you want Google's server to return to with the token response.
I noticed that there is easy non-programmatic way around.
If you have typical monotlith application built in typical MS convention(so not compatible with 12factor and typical DDD) there is an option to tell your Proxy WWW server to rewrite all requests from HTTP to HTTPS so even if you have set up Web App on http://localhost:5000 and then added in Google API url like: http://your.domain.net/sigin-google, it will work perfectly and it is not that bas because it is much safer to set up main WWW to rewrite all to HTTPS.
It is not very good practice I guess however it makes sense and does the job.
I've struggled with this issue for hours in a .net Core application. What finally fixed it for me was, in the Google developers console, to create and use a credential for "Desktop app" instead of a "Web application".
Yeah!! Using credentials of desktop app instead of web app worked for me fine. It took me more than 2 days to figure out this problem. The main problem is that google auth library dose not adding or supporting http://localhost:8000 as redirect uri for web app creds but credentials of desktop app fixed that issue. Cause its supporting http://___ connection instead of https: connection for redirect uri

Magento Change Order Status from REST API

I am 'communicating' with a Magento web app(version 1.9.2.2) via the REST API in a C# ASP.NET MVC application.
The application essentially acts as a backend order flow dashboard for pizzas. I need to display the latest orders and allow the user to check the items off as they are processed (among other things).
I am able to retrieve orders, products, customers etc; but need to be able to update the order status. From my research it seems that this can be achieved by adding an order comment.
That said, my questions are as follows:
Is adding an order comment (thus updating the order status) only possible through the SOAP Service in Magento 1.9?
If the above is true, how can I update the order status of a particular order using another secure approach?
Docs on REST API: http://devdocs.magento.com/guides/m1x/api/rest/Resources/Orders/order_comments.html
To anyone that may be facing the same issue, I discovered that it is not possible to update the order status (AKA add a sales order comment) via the REST API. You have to use the SOAP API and version 2 makes it easiest.
Setup:
In magento, create a SOAP Role and User
Add the SOAP v2 API as a web reference to your Visual Studio project
Code:
public void UpdateOrderStatus(string orderIncrementId, string newStatus, string comment = "")
{
// Init service with uri
var service = new MagentoSoap.MagentoService();
// Login - username and password (soap api key) of the soap user
string sessionId = service.login(Username, Password);
// Update order status
service.salesOrderAddComment(sessionId, orderIncrementId, newStatus, comment, 1, true);
}
You can do this by using the addComment method, which also lets you specify the new order status as one of it's parameters.
$sku='100000003';
$orderStatus = 'Downloaded';
$comment = 'The order was successfully downloaded';
$sendEmailToCustomer = false;
$proxy->call($sessionId, 'sales_order.addComment', array($sku, $orderStatus, $comment, $sendEmailToCustomer));

Facebook OAuth 2.0 authentication without using ASP.NET

I'm developing an app for iOS and Android that uses Facebook to login. The users will login on their phones using their Facebook credentials and I use Facebook's GraphAPI to authenticate them. I do not want to store their email/password unless I absolutely have to.
After authentication, I can get a myriad of information from Facebook but the one that's of most interest to my question is the access token's authorization token.
Since my app has a server side component, I also need to validate that this access token is valid on the server side (so given the access token and the Facebook user id, i should be able to validate this client), otherwise the entirety of using Facebook to authenticate users is pointless as I would need to also store username/password of the users myself.
My thought was to send the userId and the access token via SSL to my server and then use a library to validate that these tokens are valid and the user is indeed who it says it is in order to proceed with DB access and everything else server related.
I am however having a hard time finding a library in .NET that does not use ASP.NET.
Is there any library out there that can do this simple validation (given an authorization token and a user id, tell me if the user is logged in to Facebook and if so, how long the token is valid for) that does not need to inject 20 different DLLs and does not rely on ASP.NET?
I've had a look at DotNetOpenAuth but (1) it seems to need quite a few DLLs to operate which is kind of fine on its own although not ideal and (2) it seems to rely on ASP.NET and microsoft libraries that I would strongly like to avoid.
I'm running my server on Mono and would ideally like to avoid doing anything with ASP.NET since they have proven to be very unstable in the past.
Many thanks,
You might want to try a service like https://oauth.io/home which handles that oAUth stuff for you. According to the docs once you set it up you can simply use rest to make authenticated calls. http://docs.oauth.io/#simple-server-side-authorization
Ok I found an easy way to do it.
First, I downloaded their .Net library from NuGet:
<package id="Facebook" version="7.0.6" targetFramework="net40" />
Then here's the process in order to authenticate users.
Step 1
Get the server's Access Token (this has to be done once at the startup of the service)
var client = new FacebookClient
{
AppId = appId, // get this from developer.facebook
AppSecret = appSecret, // get this from developer.facebook
};
dynamic appTokenQueryResponse = client.Get("oauth/access_token"
, new
{
client_id = appId,
client_secret = appSecret,
grant_type = "client_credentials"
});
_appAccessToken = appTokenQueryResponse.access_token;
Step 2
With the server access token, We're able to make the appropriate calls into the API in order to make sure the token is valid.
private FacebookAuthorizationResponse AuthorizeUser(FacebookClient client, string userId, string accessToken)
{
dynamic expirationToken = client.Get("debug_token", new
{
input_token = accessToken,
access_token = _appAccessToken
});
DateTime expiresAt = DateTimeConvertor.FromUnixTime(expirationToken.data.expires_at);
bool isValid = expirationToken.data.is_valid;
if (!isValid)
{
return new FacebookAuthorizationResponse
{
IsAuthorized = false,
};
}
dynamic response = client.Get(userId, new
{
access_token = accessToken,
fields = "id,name"
});
return new FacebookAuthorizationResponse
{
IsAuthorized = isValid,
ExpiresAt = expiresAt,
Name = response.name
};
}
Where
public class FacebookAuthorizationResponse
{
public bool IsAuthorized { get; set; }
public DateTime ExpiresAt { get; set; }
public string Name { get; set; }
}

Categories