Google PlayGamesAPI: How to validate ServerAuthCode in C# - c#

I have developed an Android game which successfully gets a ServerAuthCode from the Google Play API. I want to send this ServerAuthCode to my custom game server, which I have wrote in C# and validate it to authenticate the player.
There is a documentation by Google for Java available (part "Exchange the server auth code for an access token on the server"): https://developers.google.com/games/services/android/offline-access
Unfortunately I can not adapt this for C#.
I have the client_secret.json which seems to include all API authentication data and I have the ServerAuthCode (which seems to be a token).
There is also a NuGet package available for C#, but it does not contain all the classes from the above documentation: https://www.nuget.org/packages/Google.Apis.AndroidPublisher.v3/
How can I validate the token? I would also welcome a simple Postman example.

I figured it out by trial and error. One important thing to note is that the Server Auth Code expires fast. In case you are debugging and copy & pasting by hand, it may happen that until you run the code, the Server Auth Code is already expired. In this case, Google API returns "invalid_grant" as error, which for me was misleading.
In my example solution you need to have a file "client_secret.json" in your project, which is copied on build to the output directory (file properties -> "Build Action" = "Content", "Copy to Output Directory" = "Copy always").
You get your client_secret.json file from the Google API console (https://console.developers.google.com/apis/credentials?project=, click on the download icon on the right side of your project, under "OAuth 2.0-Client-IDs").
Important: The redirect url must match the redirect url configured in your project. For me, it was just empty, so just use an empty string.
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Requests;
using System;
using System.IO;
using System.Reflection;
using System.Text;
namespace GoogleApiTest
{
// Source: https://developers.google.com/identity/sign-in/android/offline-access
class Program
{
static void Main(string[] args)
{
var authCode = "YOUR_FRESH_SERVER_AUTH_CODE";
var path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), #"client_secret.json");
var config = File.ReadAllText(path, Encoding.UTF8);
GoogleClientSecrets clientSecrets = GoogleClientSecrets.Load(new FileStream(path, FileMode.Open));
var request = new AuthorizationCodeTokenRequest()
{
ClientId = clientSecrets.Secrets.ClientId,
ClientSecret = clientSecrets.Secrets.ClientSecret,
RedirectUri = "",
Code = authCode,
GrantType = "authorization_code"
};
var tokenResponse = request.ExecuteAsync(new System.Net.Http.HttpClient(), "https://www.googleapis.com/oauth2/v4/token", new System.Threading.CancellationToken(), Google.Apis.Util.SystemClock.Default).GetAwaiter().GetResult();
Console.ReadLine();
}
}
}

Related

How to get a MSAL access token for SharePoint Online in a federated environment the non-interactive way in a non-interactive .Net console app?

The task as simple as to have a scheduled .NET console app which will download a file from SharePoint Online on a regular basis using AD domain user account.
If I use recommended way
var token = publicApplication.AcquireTokenByIntegratedWindowsAuth(scopes).ExecuteAsync().Result;
I'm getting
UriFormatException: Invalid URI: The hostname could not be parsed.
What does it mean? Which URI, hostname? Should I override something somewhere or add some special parameter?
I've googled thru this stuff a lot, and I have no idea where to look further, any advice will be appreciated.
P.S. I have no permissions to do anything on SharePoint side, I'm not a SP admin. I just have access to specific folder on the site from which I'm downloading the file. And also I have a code which works interactively:
WebRequest.DefaultWebProxy = WebRequest.GetSystemWebProxy();
WebRequest.DefaultWebProxy.Credentials = CredentialCache.DefaultNetworkCredentials;
var scopes = new string[] { "https://tenant.sharepoint.com/.default" };
var options = new PublicClientApplicationOptions()
{
TenantId = "tenant.com",
ClientId = "{872cd9fa-d31f-45e0-9eab-6e460a02d1f1}",//known Visual Studio Id
};
var publicApplication = PublicClientApplicationBuilder.CreateWithApplicationOptions(options).Build();
var token = publicApplication.AcquireTokenInteractive(scopes).WithLoginHint("name.surname#tenant.com").ExecuteAsync().Result;
But it shows a browser window
No questions asked, pop-up disappear, and I get the token which is used further to download a file from SPOnline using /_api/web/GetFileByServerRelativeUrl(' stuff.
So just run the app, see the popup, get the file downloaded. No interaction needed.
But this approach doesn't work if I put this routine really non-interactive:
Showing a modal dialog box or form when the application is not running in UserInteractive mode is not a valid operation. Specify the ServiceNotification or DefaultDesktopOnly style to display a notification from a service application.
Turns out the non-interactive way is only possible using tenant-side registered application. Implemented using certificate authentication.
But surprisingly the token obtained by ConfidentialClientApplicationBuilder doesn't work the way I wanted/expected (scopes/user impersonation issues). So now we use Graph client approach.
This is the only way which works for me (.NetFramework 4.7.2):
using Azure.Identity;
using Microsoft.Graph;
//...
static async Task GetFile(GraphServiceClient graphClient, string fileName2get)
{
var fileitem = graphClient
.Sites["SiteGuidYouMayGetBy /sites/[your site name]/_api/site/id"]
.Drives["CrazyLongDriveIdYouMayGetByEnumeratingDrivesHere"]
.Root
.ItemWithPath($"/Path To The File starting from Drive Root/{fileName2get}")
.Content
.Request().GetResponseAsync();
var stream = fileitem.GetAwaiter().GetResult();
using (var fileStream = System.IO.File.Create($"C:/Temp/{fileName2get}"))
{
await stream.Content.CopyToAsync(fileStream);
}
}

Google drive API Error 400: redirect_uri_mismatch with Unity

Hi I'm creating a desktop application with unity to take a photo and upload it to a public google drive account.
I have followed the instructions to create the project in google cloud of this page:
https://developers.google.com/drive/api/v3/quickstart/dotnet
And I've used the unity package from this repository, also following the instructions:
https://github.com/Elringus/UnityGoogleDrive
The problem is that when I try to access from my program to unity for upload the photo to the repository I get this error:
Authorization Error
Error 400: redirect_uri_mismatch
You can't log in to this app because it doesn't comply with Google's OAuth 2.0 policy.
If you are the developer of the application, register the redirect URI in the Google Cloud console.
Learn more
Request Details
The content in this section has been provided by the app developer. This content has not been reviewed or verified by Google.
If you're the app developer, make sure that these request details comply with Google policies.
redirect_uri: http: // localhost: 54201
But I have already set the URLs to Authorized redirect URIs
from Google Cloud, however in the error shows me the url of my localhost changes in each request (example: redirect_uri: http://localhost:59708 and redirect_uri: http://localhost:55683), so I would appreciate it if you would help me to know which url I should set on Google Cloud or how I can configure that my url doesn't change in every request.
In add I can't change the project to webgl or mobile as android, so this is a solution only for a desktop app in unity.That means than I've .json credentials from google cloud.
This my C # code that I'm using in unity.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityGoogleDrive;
using System.IO;
public class UploadGoogle : MonoBehaviour
{
public string UploadFilePath;
private GoogleDriveFiles.CreateRequest request;
private string result;
public void OnButtonClick()
{
UploadFilePath = string.Format("{0}/Snapshots/snap.jpg", Application.dataPath);
var content = File.ReadAllBytes(UploadFilePath);
var file = new UnityGoogleDrive.Data.File { Name = "Image.jpg", Content = content };
GoogleDriveFiles.Create(file).Send();
request = GoogleDriveFiles.Create(file);
request.Fields = new List<string> { "id", "name", "size", "createdTime" };
request.Send().OnDone += PrintResult;
}
private void PrintResult(UnityGoogleDrive.Data.File file)
{
result = string.Format("Name: {0} Size: {1:0.00}MB Created: {2:dd.MM.yyyy HH:MM:ss}\nID: {3}",
file.Name,
file.Size * .000001f,
file.CreatedTime,
file.Id);
}
}
Thank you

C#: Download Release Asset from Github

I want to download release asset zipball´s in a C# application for further use.
I´m using Octokit to get all release informations from the repo, including the respective browserdownload_url.
After some research it seemed to me, that you cant download this release asset zip´s via octokit, so trying with httpclient as suggested by some SO posts, that were asking these questions.
The release zip´s are on a Github Enterprise Repository, so they require Authentication.
And that is probably my issue, i cant make the authentication work with the httpClient...
The request always responds with Code 404
(which is the regular behaviour if you try by putting the url into the regular browser without logging in)
My actual implementation looks like this
public void DownloadRelease(string dlUrl, string targetPath)
{
var githubToken = "aaaaaaaaaaabbbbbbbbbcccccccccdddddddddd"; //Token created in the github developer settings with all available rights
//dlUrl = https://github.server.de/organization/project/releases/download/v1.2.34/release.zip
using (var client = new System.Net.Http.HttpClient())
{
var credentials = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}:", githubToken);
credentials = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(credentials));
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", credentials);
//client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", githubToken);
var contents = client.GetByteArrayAsync(dlUrl).Result;
System.IO.File.WriteAllBytes(targetPath, contents);
}
}
Update:
At the End we followed the way of using the curl way:
https://docs.github.com/en/enterprise-server#3.0/rest/reference/repos#download-a-repository-archive-zip
And one more mistake on my end: There were releases without downloadable Asset IDs which i didnt catch in the code.
Based on the documentation (https://docs.github.com/en/enterprise-server#2.22/rest/overview/other-authentication-methods#via-oauth-and-personal-access-tokens) my best guess is, that your crendentials are wrong.
The docs say, the format should be username:token, yet you are only using token followed by a colon : - that doesn't look right to me, either.
So essentially you need to refactor your credentials variable a bit:
var credentials = $"{username}:{githubToken}";

Using AWS SDK on .net with localstack (TransferUtility/S3 - setting endpoint)

I have localstack (https://github.com/localstack/localstack) running and am able to use the aws s3 cli to upload files to it.
What I want to be able to do is use the .NET AWS ADK with localstack. I'd like the following code to upload a file into localstack:
using (var tfu = new TransferUtility())
{
await tfu.UploadAsync(new TransferUtilityUploadRequest
{
Key = key,
BucketName = bucketName,
ContentType = document.ContentType,
Headers = { ["Content-Disposition"] = "attachment; filename=\"test.txt\"" },
InputStream = stream
});
}
My problem is I don't know how to set the endpoints so that localstack is used by the SDK rather than aws. Apparently you can set the AWSEndpointDefinition in appSettings.config as mentioned in the AWS SDK documentation, e.g:
<add key="AWSEndpointDefinition" value="C:\Dev\localstack\endpoints.json"/>
However I have no idea what this endpoints.json config should look like. I tried using this file:
https://raw.githubusercontent.com/aws/aws-sdk-net/master/sdk/src/Core/endpoints.json
When I do this, as soon as I new up a TransferUtility class I get a null reference exception - this is before I point anything to my localstack setup.
The version of AWS ASK is 3.3.0.
Another thing to note is that in some places in the documentation it is implied that the config should be an xml file rather than a json, however, when I try to use an xml file instead I get a different exception when newing up TransferUtility: 'Invalid character '<' in input string'
You can easily override it by creating an S3 client and passing it to TransferUtility constructor.
var config = new AmazonS3Config { ServiceURL = "http://localhost:4572" };
var s3client = new AmazonS3Client(config);
Do not forget to replace URL if your localstack is using different port for S3.
Hope this helps.

How to remove a users manager in AzureAD using Microsoft.Azure.ActiveDirectory.GraphClient

I'm using the Microsoft.Azure.ActiveDirectory.GraphClient (Version 2.1.0) to write an app for Azure AD user management. I'm able to set the Manager of a user but have no idea how to clear the field.
Unfortunately the sample project provided on GitHub do not contain this function either.
I managed to clear the "manager" field using the code below. It is not using the Microsoft.Azure.ActiveDirectory.GraphClient library but gets the job done.
var token = <get your adal token here>
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
var url = "https://graph.windows.net/<tenant domain>/users/<userid>/$links/manager?api-version=1.6"
var resp = httpClient.DeleteAsync(url).Result;
if (!resp.IsSuccessStatusCode)
{
// log / throw exception etc.
}
You need to perform a DELETE HTTP request to https://graph.microsoft.com/v1.0/users/<user_email>/manager/$ref (make sure to replace the <user_email> in the URL.
A successful call will receive 204 response code and empty string as the response body.
This method is currently missing from the Microsoft Graph API docs but should be added in the future. (see here)
Also you should start using Microsoft Graph (graph.microsoft.com) instead of Azure AD Graph (graph.windows.net) as the latter is becoming obsolete. (See here)
//Assign and remove user's manager
// User.Manager = newUser as DirectoryObject;
User.Manager = null;

Categories