3

I am working on a .NET MAUI Mobile Application and trying to generate an OAuth 2.0 authentication token to call the Google Play Developer API. I have set up everything as follows:

  1. Created a project on Google Cloud Console.
  2. Enabled the Google Play Developer API.
  3. Configured the OAuth consent screen with the external app type and added the "https://www.googleapis.com/auth/androidpublisher" as a scope.
  4. Created an OAuth 2.0 Client ID with the application type set to Android, and added the package name along with the SHA-1 fingerprint.

In my .NET MAUI project, I have installed the following NuGet packages:

  • Microsoft.Identity.Client
  • System.Net.Http.Json

I implemented the OAuth flow for device code authentication. However, when I try to acquire an authentication token, I encounter the following error:

Error requesting device code: { "error": "invalid_client", "error_description": "Invalid client type." } Failed to acquire access token.

I implemented the OAuth flow for device code authentication as below:

public class GoogleAuthService
{
    private static readonly HttpClient _httpClient = new HttpClient();

    // Set your Client ID
    private const string ClientId = "Your_Google_Client_ID";

    // Set Google OAuth endpoints
    private const string DeviceCodeEndpoint = "https://oauth2.googleapis.com/device/code";
    private const string TokenEndpoint = "https://oauth2.googleapis.com/token";

    // Scopes needed for the Google Play Developer API
    private const string Scope = "https://www.googleapis.com/auth/androidpublisher";

    public async Task<string> GetAccessTokenAsync()
    {
        // Step 1: Request the device code
        var deviceCodeResponse = await RequestDeviceCodeAsync();

        if (deviceCodeResponse != null)
        {
            var deviceCode = deviceCodeResponse["device_code"].ToString();
            var userCode = deviceCodeResponse["user_code"].ToString();
            var verificationUrl = deviceCodeResponse["verification_url"].ToString();
            var expiresIn = deviceCodeResponse["expires_in"].ToString();
            var interval = int.Parse(deviceCodeResponse["interval"].ToString());

            // Instruct the user to visit the URL and input the user code
            Console.WriteLine($"Please visit {verificationUrl} and enter the code: {userCode}");

            // Step 2: Poll for the token using the device code
            return await PollForAccessTokenAsync(deviceCode, interval);
        }

        return null;
    }

    private async Task<JObject> RequestDeviceCodeAsync()
    {
        var parameters = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("client_id", ClientId),
            new KeyValuePair<string, string>("scope", Scope)
        });

        var response = await _httpClient.PostAsync(DeviceCodeEndpoint, parameters);
        var responseBody = await response.Content.ReadAsStringAsync();

        if (response.IsSuccessStatusCode)
        {
            return JObject.Parse(responseBody);  // Return the device code response
        }

        Console.WriteLine($"Error requesting device code: {responseBody}");
        return null;
    }

    private async Task<string> PollForAccessTokenAsync(string deviceCode, int interval)
    {
        while (true)
        {
            await Task.Delay(interval * 1000);  // Wait for the polling interval

            var parameters = new FormUrlEncodedContent(new[]
            {
                new KeyValuePair<string, string>("client_id", ClientId),
                new KeyValuePair<string, string>("device_code", deviceCode),
                new KeyValuePair<string, string>("grant_type", "urn:ietf:params:oauth:grant-type:device_code")
            });

            var response = await _httpClient.PostAsync(TokenEndpoint, parameters);
            var responseBody = await response.Content.ReadAsStringAsync();

            if (response.IsSuccessStatusCode)
            {
                var tokenResponse = JObject.Parse(responseBody);
                var accessToken = tokenResponse["access_token"].ToString();
                Console.WriteLine($"Access Token: {accessToken}");
                return accessToken;  // Return the access token
            }
            else if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
            {
                var error = JObject.Parse(responseBody)["error"].ToString();
                if (error == "authorization_pending")
                {
                    Console.WriteLine("Authorization pending, waiting...");
                    continue;  // Keep polling
                }
                else
                {
                    Console.WriteLine($"Error: {error}");
                    return null;
                }
            }
        }
    }
}

To generate the access token from the below function, I call the GoogleAuthService();

public async void LoginButtonClicked(Object sender, EventArgs e)
{
    string accessToken = await _googleAuthService.GetAccessTokenAsync();

    if (!string.IsNullOrEmpty(accessToken))
    {
        // Use the access token to make API calls to Google Play Developer API
        Console.WriteLine($"Access Token: {accessToken}");
    }
    else
    {
        // Handle failure
        Console.WriteLine("Failed to acquire access token.");
    }
}

On AndroidManifest.xml file:

<activity android:name="MainActivity"
  android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="com.googleusercontent.apps.OAuth 2.0 client ID" />
        <data android:host="oauth2redirect" />
    </intent-filter>
</activity>

What could be causing this "invalid_client" error? Is there something I'm missing in configuring the authentication token using OAuth 2.0 for the .NET MAUI Mobile Application to call the Google Play Developer API?

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.