1

The following code is throwing returning response from google with the required parameter is missing: response_type. Either the error is misleading with something else or the value might be wrong. Here is the documentation: https://cloud.google.com/docs/authentication/#oauth-2.0-clients enter image description here

var client = new RestClient("https://accounts.google.com/o/oauth2/auth");
var request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddParameter("application/x-www-form-urlencoded", "grant_type=client_credentials&client_id=501819637859-6ng1c949htt0admmpa19vm6tfle04jdc.apps.googleusercontent.com&client_secret=nrrIjxFqugLKd24E8xVesA6f", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
SNew
  • 109
  • 1
  • 2
  • 10
  • where did you get that grant type? Im not sure that show your suposed to send a post body. Any reason you dont want to use the client libraries? – Linda Lawton - DaImTo Feb 14 '21 at 17:49
  • @DaImTo I just took a snippet from this link where it was mentioned grant_type https://stackoverflow.com/questions/38494279/how-do-i-get-an-oauth-2-0-authentication-token-in-c-sharp and we are having issues with client libraries with conflicts with other integration code base so want to use plain vanilla c# rest apis – SNew Feb 14 '21 at 18:39
  • 1
    @SNew I would advice you to fix those "conflicts with other integration code base" rather than trying to work your own way around an officially supported library. This will only lead to bugs and possibly security holes in your application. – Xerillio Feb 14 '21 at 19:56
  • @Xerillio Thank you but some of these integrations are using old .net frameworks and can't upgrade because of other limitations. I assume getting oAuth 2.0 bearer token and making post calls to Google API to get a JSON response should be possible without any libraries. Certain times, if it is just one rest API call then adding google client libraries for .net is overkill becasue it requires atleast 18 dlls referenes that has wrappers for entire google suite. – SNew Feb 14 '21 at 21:27
  • @DaImTo Please see the below comment for some reason I am unable to tag you for comments under your latest reply – SNew Feb 15 '21 at 15:10

2 Answers2

3

Yes it is possible to code your own version of Google Oauth2 flow.

First call

The first call is a HTTP GET and is the link to the consent screen displayed to the user.

If this is an installed application like a desktop app or console app. Then redirect uri should be urn:ietf:wg:oauth:2.0:oob.

Scopes should be separated by space if you want more than one, add offline if you want to get a refresh token back.

GET https://accounts.google.com/o/oauth2/v2/auth?client_id={clientid}&redirect_uri={RedirectURI}&scope={scopes}&response_type=code

second call

The response to the first call is an Authentication Code this code needs to be exchanged with google. This is a HTTP POST

POST https://oauth2.googleapis.com/token
code=4/X9lG6uWd8-MMJPElWggHZRzyFKtp.QubAT_P-GEwePvB8fYmgkJzntDnaiAI&client_id= 

{ClientId}&client_secret={ClientSecret}&redirect_uri={RedirectURI}&grant_type=authorization_code

The post data for this call is one long string do not try to parse it. Just post it in the body of your call. You are correct that the content type is "application/x-www-form-urlencoded";

The response to this call will contain an access token and a refresh token

{
"access_token" : "ya29.1.AADtN_VSBMC2Ga2lhxsTKjVQ_ROco8VbD6h01aj4PcKHLm6qvHbNtn-_BIzXMw",
"token_type" : "Bearer",
"expires_in" : 3600,
"refresh_token" : "1/J-3zPA8XR1o_cXebV9sDKn_f5MTqaFhKFxH-3PUPiJ4"
}

The access token will expire after one hour so you will need to refresh it thats what the refresh token is for.

refresh access token

The following call is also a HTTP POST

https://oauth2.googleapis.com/token
client_id={ClientId}&client_secret={ClientSecret}&refresh_token={Refresh token from previous call}&grant_type=refresh_token

working example

class Program
    {
        private const string Clientid = "Add yours";
        private const string Secret = "Add yours.";
        
        static async Task  Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            
            Console.WriteLine($"open the following link in your browser window: {Helper.BuildConsentURL(Clientid, new []{ "profile" })}");
            
            Console.WriteLine("Please paste the Authorization code here:");
            var authorizationCode = Console.ReadLine();

            var tokenResponse = await Helper.ExchangeAuthorizationCode(authorizationCode, Clientid, Secret);

            var refreshTokenResponse = await Helper.ExchangeRefreshToken(tokenResponse.refresh_token, Clientid, Secret);

        }
    }

Helper.cs

 public class Helper
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="scope">string array of scopes</param>
        /// <param name="redirectUri">leave empty for installed application</param>
        /// <returns></returns>
        public static string BuildConsentURL(string clientId, string[] scope, string redirectUri = null)
        {
            if (string.IsNullOrEmpty(redirectUri))
            {
                redirectUri = "urn:ietf:wg:oauth:2.0:oob"; // for installed application
            }

            return
                $"https://accounts.google.com/o/oauth2/auth?client_id={clientId}&redirect_uri={redirectUri}&scope={string.Join(" ", scope)}&response_type=code";
        }

        private static string BuildAuthorizationCodeRequest(string code, string clientId, string secret,
            string redirectUri)
        {
            return
                $"code={code}&client_id={clientId}&client_secret={secret}&redirect_uri={redirectUri}&grant_type=authorization_code";
        }
        
        private static string BuildRefreshAccessTokenRequest(string refreshToken, string clientId, string secret)
        {
            return
                $"client_id={clientId}&client_secret={secret}&refresh_token={refreshToken}&grant_type=refresh_token";
        }


        private static async Task<AuthResponse> PostMessage(string postData)
        {
            AuthResponse result;

            var client = new HttpClient();
            client.BaseAddress = new Uri("https://accounts.google.com/");
            var request = new HttpRequestMessage(HttpMethod.Post, "o/oauth2/token");
            request.Content = new StringContent(postData, Encoding.UTF8, "application/x-www-form-urlencoded");
            var response = await client.SendAsync(request);
            using (var content = response.Content)
            {
                var json = content.ReadAsStringAsync().Result;
                result = JsonSerializer.Deserialize<AuthResponse>(json);
            }
            return result;
        }


        public static async Task<AuthResponse> ExchangeAuthorizationCode(string code, string clientId, string secret,
            string redirectUri = null)
        {
            var result = new AuthResponse();

            if (string.IsNullOrEmpty(redirectUri))
            {
                redirectUri = "urn:ietf:wg:oauth:2.0:oob"; // for installed application
            }

            var postData = BuildAuthorizationCodeRequest(code, clientId, secret, redirectUri);

            return await PostMessage(postData);
        }
        
        public static async Task<AuthResponse> ExchangeRefreshToken(string refreshToken, string clientId, string secret)
        {
            var postData = BuildRefreshAccessTokenRequest(refreshToken, clientId, secret);

            return await PostMessage(postData);
        }
        
    }

authresonse.cs

  public class AuthResponse
    {
        public string access_token { get; set; }
        public string token_type { get; set; }
        public int expires_in { get; set; }
        public string refresh_token { get; set; }
    }
Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449
  • Awesome reply and your articles are showing exactly what I am looking for but the second call is throwing error saying invalid url. Please see this document for calls and real test values. https://docs.google.com/document/d/1qFg8WIuF5csHcXm2A6w-4-B3fr_Yoi7y_g0f_gHF7C0/edit?usp=sharing – SNew Feb 15 '21 at 15:05
  • I tried with your ancient sample code, same error. Looks like something changed in google... System.Net.WebException: 'The remote server returned an error: (400) Bad Request.' – SNew Feb 15 '21 at 15:53
  • The endpoints have changed https://accounts.google.com/.well-known/openid-configuration think that happend about three years ago to be openid compliant. If fixing the endpoints doesn't work let me know and i will give it a try in the morning. – Linda Lawton - DaImTo Feb 15 '21 at 16:05
  • 1
    @DalmTo I tried all the endpoints https://oauth2.googleapis.com/token, https://accounts.google.com/o/oauth2/v2/auth, and neither of them worked. – SNew Feb 15 '21 at 16:25
  • 1
    Try the new example. i am wondering if your issue was the Encoding.UTF8 – Linda Lawton - DaImTo Feb 16 '21 at 14:54
  • @DalmTo Thank you this code has returned the bearer token but I have to regenerate the authentication code again then how will it work since the authentication code was generated via browser and unable to generate via code, hence in real time application where there shouldn't any consent etc. How can I do all three 3 steps via code and with no consent if I have to generate the authentication code every time – SNew Feb 17 '21 at 18:08
  • You cant Oauth2 requires a user consent to it. There's no way around the Oauth2 dance these are the steps needed. You get an authorization code exchange it for an access token and refresh token, use the refresh token to get a new access token when ever you need one. – Linda Lawton - DaImTo Feb 17 '21 at 19:11
  • @DalmTo Just trying to understand how anyone would call google APIs programmatically in real-world web applications if a human person need to update the authentication code every day etc and are we forced to use Client Libraries? – SNew Feb 17 '21 at 21:33
  • 1
    Your not understanding. No one needs to update the authorization code that's what the refresh token is for. You only need one authorization code after that you have a refresh token, the refresh token gets you an access token, you make all the requests with the access token. Save the refresh token for a user and you don't need to ask them to authorize again. try reading this https://www.daimto.com/google-developer-console-oauth2/ – Linda Lawton - DaImTo Feb 18 '21 at 08:33
0

Try This.

   String client_id = "<OAUTH_client_id >";
            String client_secret = "<OAUTH_client_secret>";

            var BaseAddress = "https://gateway-stage-core.optum.com";
            var httpClient = new HttpClient() { BaseAddress = new Uri(BaseAddress) };


            var creds = $"client_id={client_id}&client_secret={client_secret}&grant_type=client_credentials";
            httpClient.DefaultRequestHeaders.Accept.Clear();
            httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
            var content = new StringContent(creds, Encoding.UTF8, "application/x-www-form-urlencoded");
            var response = httpClient.PostAsync("/auth/oauth2/cached/token", content).Result;
            var jsonContent = response.Content.ReadAsStringAsync().Result;

            var tokenObj = JsonConvert.DeserializeObject<BearerViewModel>(jsonContent);
            var token = tokenObj.access_token;
Abhishek Tomar
  • 827
  • 1
  • 10
  • 20