Search code examples
c#asp.netentity-frameworkgoogle-oauthdotnetnuke

An asp.net OAuth2 google login example


Grettings,

I'm really struggling with the google authentication. I got to the point where I get an authentication code generated for the user, by redirecting him to a page (will provide code below) and that was pretty easy. But I can't find a way to convert that code to the access token.

I tried using ExchangeCodeForTokenAsync but that either led me to a thread lock or other thread problems because the method has to be executed on page load.

I found a solution with HttpWebRequest to make a call to the api, but that gets me a Bad request response (will provide a code for this also)

I'm using DotNetNuke, if I am able to get the user info I can authenticate them (I have a custom login, I am not using the DNN one), but for that I need the access token.

Redirect to google login page:

string redirectUri = "https://url.com/google";
string[] Scopes = { "openid", "email" };
var flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
    ClientSecrets = new ClientSecrets
    {
        ClientId = "client_id",
        ClientSecret = "client_secret "
    },
    Scopes = Scopes,
});
var authorizationUrl = flow.CreateAuthorizationCodeRequest(redirectUri).Build();
Response.Redirect(authorizationUrl.AbsoluteUri);

Current code of the redirection page which gets the code after google auth:

    public class GoogleToken
    {
        public string access_token { get; set; }
        public string token_type { get; set; }
        public int expires_in { get; set; }
        public string id_token { get; set; }
        public string refresh_token { get; set; }
    }
    public class GoogleInfo
    {
        public string id { get; set; }
        public string email { get; set; }
        public bool verified_email { get; set; }
        public string name { get; set; }
        public string given_name { get; set; }
        public string family_name { get; set; }
        public string picture { get; set; }
        public string locale { get; set; }
        public string gender { get; set; }
    }
    protected void Page_Load(object sender, EventArgs e)
    {
        string ClientId = "client_id";
        string ClientSecret = "client_secret";
        string redirecturl = "https://url.com/google";
        try
        {
            if (!IsPostBack)
            {
                string code = Request.QueryString["code"];
                if (!string.IsNullOrEmpty(code))
                {
                    
                    string parameters = string.Format("code={0}&client_id={1}&client_secret={2}&redirect_uri={3}&grant_type=authorization_code", code,
                        ClientId,
                        ClientSecret,
                       redirecturl);
                    string response = MakeWebRequest("https://oauth2.googleapis.com/token", "POST", "application/x-www-form-urlencoded", parameters);
                    GoogleToken tokenInfo = new JavaScriptSerializer().Deserialize<GoogleToken>(response);

                    if (tokenInfo != null)
                    {
                        if (!string.IsNullOrEmpty(tokenInfo.access_token))
                        {
                            var googleInfo = MakeWebRequest("https://www.googleapis.com/oauth2/v1/userinfo?access_token=" + tokenInfo.access_token, "GET");
                            GoogleInfo profile = new JavaScriptSerializer().Deserialize<GoogleInfo>(googleInfo);
                            ltrMSG.Text = googleInfo;
                        }
                    }
                }
            }
        }
        catch (Exception ex)
        {
            ltrMSG.Text += "<br/>" + ex.ToString();
        }
    }
    public string MakeWebRequest(string destinationUrl, string methodName, string contentType = "", string requestJSON = "")
    {
        try
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(destinationUrl);
            request.Method = methodName;
            if (methodName == "POST")
            {
                byte[] bytes = System.Text.Encoding.ASCII.GetBytes(requestJSON);
                request.ContentType = contentType;
                request.ContentLength = bytes.Length;
                using (Stream requestStream = request.GetRequestStream())
                {
                    requestStream.Write(bytes, 0, bytes.Length);
                }
            }
            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        return reader.ReadToEnd();
                    }
                }
            }

            return null;
        }
        catch (WebException webEx)
        {
            return webEx.ToString();
        }
    }

}

I will be grateful if anyone can provide me with snippets of what I should be doing, tell me what I am doing wrong or point me in the right direction. Thank you!


Solution

  • That's Oauth2 authorization code, not login. Not to mention that GoogleAuthorizationCodeFlow is designed for installed applications and wont work if you try to host it on a web server.

    You should check out Google external login setup in ASP.NET Core

    var builder = WebApplication.CreateBuilder(args);
    var services = builder.Services;
    var configuration = builder.Configuration;
    
    services.AddAuthentication().AddGoogle(googleOptions =>
        {
            googleOptions.ClientId = configuration["Authentication:Google:ClientId"];
            googleOptions.ClientSecret = configuration["Authentication:Google:ClientSecret"];
        });
    

    Authentication vs authorization

    • Sign-in OpenID Connect (OIDC): Focuses on verifying the identity of users and provides a way for applications to securely obtain information about the authenticated user.
    • OAuth 2.0: Focuses on granting limited access rights to resources on behalf of the user, without exposing the user's credentials, and does not necessarily address user authentication directly.