The Most Comprehensive and Convenient Framework for Integrating OAuth2 Login in .NET Projects

The Most Comprehensive and Convenient Framework for Integrating OAuth2 Login in .NET Projects

MrHuo.OAuth is the most comprehensive and convenient framework for integrating OAuth2 login in .NET projects.

Last updated 4/12/2022 10:48 PM
mrhuo
8 min read
Category
.NET
Topic
C# Open Source Projects
Tags
.NET C# Open Source Authentication Open Source C#

MrHuo.OAuth

The most comprehensive and convenient framework for integrating OAuth2 login in .NET projects

Travis (.org) GitHub stars GitHub license

Works with .NET Core projects or .NET Framework 4.6 projects

Experience site: https://oauthlogin.net/


Supported Platforms

  • Baidu (available)
  • WeChat Official Account (available)
  • Gitlab (available)
  • Gitee (available)
  • GitHub (available)
  • Huawei (available)
  • Coding.net (available)
  • Sina Weibo (available)
  • Alipay (available)
  • OSChina (available)
  • Xunlei (available)
  • DingTalk In-App Login (available)
  • DingTalk Scan Login (available)
  • QQ (available)
  • Microsoft (available)
  • Xiaomi (available)
  • StackOverflow (available)
  • Facebook (available) by Donma Hsu
  • Google (available)
  • LinkedIn (available, nuget pending)
  • Douyin (available, nuget pending) by feng lui
  • Kuaishou (available, nuget pending) by feng lui
  • WeChat Open Platform (pending test)
  • Meituan (pending test)

Planned

  • Feishu
  • Taobao
  • Xigua
  • Toutiao
  • Renren
  • Teambition
  • Pinterest
  • Twitter
  • WeCom QR Login
  • WeCom Web Login
  • Kujiale
  • Ele.me
  • JD.com
  • Alibaba Cloud
  • Ximalaya...

Usage

Create a new web project and install the nuget package to use.

Currently available nuget packages:

https://www.nuget.org/packages?q=MrHuo.OAuth

Gitlab

Install-Package MrHuo.OAuth.Gitlab -Version 1.1.1

WeChat Official Account

Install-Package MrHuo.OAuth.Wechat -Version 1.1.1

oschina.net

Install-Package MrHuo.OAuth.OSChina -Version 1.1.1

coding.net

Install-Package MrHuo.OAuth.Coding -Version 1.1.1

github.com

Install-Package MrHuo.OAuth.GitHub -Version 1.1.1

alipay.com

Install-Package MrHuo.OAuth.Alipay -Version 1.1.1

baidu.com

Install-Package MrHuo.OAuth.Baidu -Version 1.1.1

huawei.com

Install-Package MrHuo.OAuth.Huawei -Version 1.1.1

gitee.com

Install-Package MrHuo.OAuth.Gitee -Version 1.1.1

weibo.com

Install-Package MrHuo.OAuth.SinaWeibo -Version 1.1.1

xunlei.com

Install-Package MrHuo.OAuth.XunLei -Version 1.1.1

qq.com

Install-Package MrHuo.OAuth.QQ -Version 1.1.1

microsoft.com

Install-Package MrHuo.OAuth.Microsoft -Version 1.1.1

mi.com

Install-Package MrHuo.OAuth.Mi -Version 1.1.1

stackoverflow.com

Install-Package MrHuo.OAuth.StackOverflow -Version 1.1.1

facebook.com

Install-Package MrHuo.OAuth.Facebook -Version 1.1.1

google.com

Install-Package MrHuo.OAuth.Google -Version 1.0.0
  1. Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    // Inject third-party login components
    services.AddSingleton(new Baidu.BaiduOAuth(OAuthConfig.LoadFrom(Configuration, "oauth:baidu")));
    services.AddSingleton(new Wechat.WechatOAuth(OAuthConfig.LoadFrom(Configuration, "oauth:wechat")));
    services.AddSingleton(new Gitlab.GitlabOAuth(OAuthConfig.LoadFrom(Configuration, "oauth:gitlab")));
    services.AddSingleton(new Gitee.GiteeOAuth(OAuthConfig.LoadFrom(Configuration, "oauth:gitee")));
    // ... other login methods
}

Note: When using the appsettings.json approach, a convenience method is provided to load from configuration.

OAuthConfig.LoadFrom(Configuration, "oauth:baidu")

The "oauth:baidu" part is a configuration prefix. The configuration format is as follows:

{
  "oauth": {
    "qq": {
      "app_id": "qq_app_id",
      "app_key": "qq_app_key",
      "redirect_uri": "https://oauthlogin.net/oauth/qqcallback",
      "scope": "get_user_info"
    },
    "github": {
      "app_id": "github_app_id",
      "app_key": "github_app_key",
      "redirect_uri": "https://oauthlogin.net/oauth/githubcallback",
      "scope": "repo"
    },
    "wechat": {
      "app_id": "wechat_app_id",
      "app_key": "wechat_app_key",
      "redirect_uri": "https://oauthlogin.net/oauth/wechatcallback",
      "scope": "snsapi_userinfo"
    },
    "huawei": {
      "app_id": "huawei_app_id",
      "app_key": "huawei_app_key",
      "redirect_uri": "https://oauthlogin.net/oauth/huaweicallback",
      "scope": "https://www.huawei.com/auth/account"
    },
    "gitee": {
      "app_id": "gitee_app_id",
      "app_key": "gitee_app_key",
      "redirect_uri": "http://oauthlogin.net/oauth/giteecallback",
      "scope": "user_info"
    },
    "baidu": {
      "app_id": "baidu_app_id",
      "app_key": "baidu_app_key",
      "redirect_uri": "http://oauthlogin.net/oauth/baiducallback",
      "scope": "basic"
    },
    "alipay": {
      "app_id": "alipay_app_id",
      "app_key": "alipay_app_key",
      "redirect_uri": "https://oauthlogin.net/oauth/alipaycallback",
      "scope": "auth_user",
      "private_key": "private_key",
      "public_key": "public_key"
    },
    "gitlab": {
      "app_id": "gitlab_app_id",
      "app_key": "gitlab_app_key",
      "redirect_uri": "http://oauthlogin.net/oauth/gitlabcallback",
      "scope": "read_user"
    }
  }
}
  1. OAuthController.cs (name it as needed)
public class OAuthController : Controller
{
    [HttpGet("oauth/{type}")]
    public IActionResult Index(
        string type,
        [FromServices] BaiduOAuth baiduOAuth,
        [FromServices] WechatOAuth wechatOAuth
    )
    {
        var redirectUrl = "";
        switch (type.ToLower())
        {
            case "baidu":
                {
                    redirectUrl = baiduOAuth.GetAuthorizeUrl();
                    break;
                }
            case "wechat":
                {
                    redirectUrl = wechatOAuth.GetAuthorizeUrl();
                    break;
                }
            default:
                return ReturnToError($"Login method [{type}] not implemented!");
        }
        return Redirect(redirectUrl);
    }

    [HttpGet("oauth/{type}callback")]
    public async Task<IActionResult> LoginCallback(
        string type,
        [FromServices] BaiduOAuth baiduOAuth,
        [FromServices] WechatOAuth wechatOAuth,
        [FromQuery] string code,
        [FromQuery] string state)
    {
        try
        {
            switch (type.ToLower())
            {
                case "baidu":
                    {
                        var authorizeResult = await baiduOAuth.AuthorizeCallback(code, state);
                        if (!authorizeResult.IsSccess)
                        {
                            throw new Exception(authorizeResult.ErrorMessage);
                        }
                        return Json(authorizeResult);
                    }
                case "wechat":
                    {
                        var authorizeResult = await wechatOAuth.AuthorizeCallback(code, state);
                        if (!authorizeResult.IsSccess)
                        {
                            throw new Exception(authorizeResult.ErrorMessage);
                        }
                        return Json(authorizeResult);
                    }
                default:
                    throw new Exception($"Login callback [{type}] not implemented!");
            }
        }
        catch (Exception ex)
        {
            return Content(ex.Message);
        }
    }
}
  1. Views
<!-- Place authorization buttons in code -->
<a href="/oauth/baidu">Baidu Login</a>
<a href="/oauth/wechat">WeChat Scan Login</a>
<!-- Other login methods follow the same pattern -->

Extension

Extending other platforms is very easy. Take the Gitee platform code as an example: https://github.com/mrhuo/MrHuo.OAuth/tree/main/MrHuo.OAuth.Gitee

Step 1: Find the OAuth documentation for the platform, find the JSON returned by the user info API, and convert it to a C# entity class. For example:

Extend user properties as needed according to your requirements and API standards.

public class GiteeUserModel : IUserInfoModel
{
    [JsonPropertyName("name")]
    public string Name { get; set; }

    [JsonPropertyName("avatar_url")]
    public string Avatar { get; set; }

    [JsonPropertyName("message")]
    public string ErrorMessage { get; set; }

    [JsonPropertyName("email")]
    public string Email { get; set; }

    [JsonPropertyName("blog")]
    public string Blog { get; set; }

    //... other properties similar to above
}
Step 2: Write the authorization interface for the corresponding platform
/// <summary>
/// https://gitee.com/api/v5/oauth_doc#/
/// </summary>
public class GiteeOAuth : OAuthLoginBase<GiteeUserModel>
{
    public GiteeOAuth(OAuthConfig oauthConfig) : base(oauthConfig) { }
    protected override string AuthorizeUrl => "https://gitee.com/oauth/authorize";
    protected override string AccessTokenUrl => "https://gitee.com/oauth/token";
    protected override string UserInfoUrl => "https://gitee.com/api/v5/user";
}

With comments, that's ten lines total. As you can see, it's very convenient. If the platform follows OAuth2 standard protocols, these few lines are all you need.

Even a more complex implementation like WeChat login (with modified fields) is straightforward; you just define the basic parameters. Code example:

/// <summary>
/// Wechat OAuth documentation reference:
/// <para>https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html</para>
/// </summary>
public class WechatOAuth : OAuthLoginBase<WechatAccessTokenModel, WechatUserInfoModel>
{
    public WechatOAuth(OAuthConfig oauthConfig) : base(oauthConfig) { }
    protected override string AuthorizeUrl => "https://open.weixin.qq.com/connect/oauth2/authorize";
    protected override string AccessTokenUrl => "https://api.weixin.qq.com/sns/oauth2/access_token";
    protected override string UserInfoUrl => "https://api.weixin.qq.com/sns/userinfo";
    protected override Dictionary<string, string> BuildAuthorizeParams(string state)
    {
        return new Dictionary<string, string>()
        {
            ["response_type"] = "code",
            ["appid"] = oauthConfig.AppId,
            ["redirect_uri"] = System.Web.HttpUtility.UrlEncode(oauthConfig.RedirectUri),
            ["scope"] = oauthConfig.Scope,
            ["state"] = state
        };
    }
    public override string GetAuthorizeUrl(string state = "")
    {
        return $"{base.GetAuthorizeUrl(state)}#wechat_redirect";
    }
    protected override Dictionary<string, string> BuildGetAccessTokenParams(Dictionary<string, string> authorizeCallbackParams)
    {
        return new Dictionary<string, string>()
        {
            ["grant_type"] = "authorization_code",
            ["appid"] = $"{oauthConfig.AppId}",
            ["secret"] = $"{oauthConfig.AppKey}",
            ["code"] = $"{authorizeCallbackParams["code"]}"
        };
    }
    protected override Dictionary<string, string> BuildGetUserInfoParams(WechatAccessTokenModel accessTokenModel)
    {
        return new Dictionary<string, string>()
        {
            ["access_token"] = accessTokenModel.AccessToken,
            ["openid"] = accessTokenModel.OpenId,
            ["lang"] = "zh_CN",
        };
    }
}

Special Contributions

  1. Carl
  2. Donma Hsu
  3. feng lui

To help everyone successfully use various platform login components, we are urgently seeking APPIDs from various platforms for testing. If you have resources, please contact me. Contributors who provide test app IDs will be permanently listed in the Special Contributions section on the project homepage, with a possible link.

Email: admin@mrhuo.com Subject: OAuth Appid

Contribution

  1. Welcome to participate in development and contribute code for other unimplemented platforms.
  2. Welcome to submit platform requests in issues, including the platform link; we will add them to the plan.
  3. Feel free to submit any suggestions. Communicate civilly.

License

Apache-2.0 License

Project Address

MrHuo.OAuth

Keep Exploring

Related Reading

More Articles