OAuth2 1.1.0
dotnet add package OAuth2 --version 1.1.0
NuGet\Install-Package OAuth2 -Version 1.1.0
<PackageReference Include="OAuth2" Version="1.1.0" />
<PackageVersion Include="OAuth2" Version="1.1.0" />
<PackageReference Include="OAuth2" />
paket add OAuth2 --version 1.1.0
#r "nuget: OAuth2, 1.1.0"
#:package OAuth2@1.1.0
#addin nuget:?package=OAuth2&version=1.1.0
#tool nuget:?package=OAuth2&version=1.1.0
OAuth2
OAuth2 is a library for user authentication using third-party services (OAuth/OAuth2 protocol) such as Google, Facebook and so on.
Standard Flow
- Generate a login URL and render a page with it
- Define a callback endpoint that the third-party service redirects to after successful authentication
- Retrieve user info on callback from the third-party service
Installation
Install the OAuth2 NuGet package:
dotnet add package OAuth2
Usage (ASP.NET Core Minimal API)
Install OAuth2 in an ASP.NET Core web project. Configure Google:ClientId and Google:ClientSecret using user secrets or a secret store, and register https://localhost:5001/auth/google/callback with Google. Run the app on https://localhost:5001 with a trusted development certificate.
This example shows the basic flow only. Before using it for authentication, generate an unpredictable, single-use state, bind it to the initiating browser session, pass it to GetLoginLinkUriAsync(state), and validate it before exchanging the code. The library does not validate state, manage sessions, or validate ID tokens; use an established authentication handler or correlation store for those responsibilities. See Google's authorization-code flow.
using System.Collections.Specialized;
using OAuth2.Client.Impl;
using OAuth2.Configuration;
using OAuth2.Infrastructure;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
GoogleClient CreateGoogleClient() => new(new RequestFactory(), new ClientConfiguration
{
ClientId = app.Configuration["Google:ClientId"]!,
ClientSecret = app.Configuration["Google:ClientSecret"]!,
RedirectUri = "https://localhost:5001/auth/google/callback",
Scope = "openid profile email"
});
app.MapGet("/auth/google", async () =>
{
var loginUri = await CreateGoogleClient().GetLoginLinkUriAsync();
return Results.Redirect(loginUri);
});
app.MapGet("/auth/google/callback", async (HttpContext context) =>
{
var code = context.Request.Query["code"].ToString();
if (string.IsNullOrEmpty(code))
return Results.BadRequest("Missing authorization code.");
var userInfo = await CreateGoogleClient().GetUserInfoAsync(
new NameValueCollection { { "code", code } });
return Results.Ok(userInfo);
});
app.Run();
Open /auth/google to start. Create a client per authentication flow; do not share an instance concurrently. Use ProviderName and Id to look up a linked account, and follow the email-verification guidance before trusting an email address.
HTTP transport and timeouts
RequestFactory shares its HTTP transport across authentication flows. Each call to CreateClient returns a separate RestClient; headers, authenticators, and timeouts stay on that client. Disposing it leaves the shared transport available. On Windows, Linux, and macOS with .NET 10 or .NET 11, pooled connections expire after two minutes so new connections can refresh DNS. Mobile, browser, and netstandard2.0 builds retain their platform handler. Outside the browser, handler cookies are disabled so they do not cross authentication flows. RestSharp still handles explicit request cookies and same-origin redirects.
The default transport rejects redirects to another scheme, host, or port before RestSharp can forward an OAuth form body or token-bearing query string. A provider that requires a redirect to another origin must use its documented endpoint directly or an application-owned IRequestFactory with an explicit redirect policy. Browser redirect behavior also depends on the browser's fetch implementation and CORS policy.
Custom IRequestFactory implementations retain ownership of the clients and handlers they supply. Use an application-managed transport for custom DNS, proxy, certificate, or connection-lifetime requirements.
Set RequestOptions.Timeout when constructing RequestFactory to change the response timeout. A request's RestRequest.Timeout takes precedence; otherwise the factory value applies, or RestSharp's 100-second default when neither is set. The shared HttpClient has no separate timeout because RestSharp enforces these limits and caller cancellation. An infinite HTTP transport timeout does not make requests unlimited by default.
Email verification
UserInfo.EmailVerified is nullable: true and false preserve the provider's assertion; null means unknown or not mapped. Verification methods differ between providers. The following clients populate it:
| Client | Source |
|---|---|
Boolean email_verified in userinfo. |
|
Optional boolean email_verified in /v2/userinfo. |
|
| Salesforce | Boolean email_verified in the existing identity URL response. |
| Uber | Optional boolean email_verified in /v3/me. |
| Yahoo | Boolean email_verified in UserInfo. |
| GitHub | verified on the selected /user/emails record. A public profile email alone leaves verification unknown without adding a request. |
Google, LinkedIn, Salesforce, Uber, and Yahoo accept only JSON booleans for verification; missing, null, or other types become null. GitHub retains its existing typed email parser and primary/verified/first-email selection order. Verification belongs to the selected address, not another address in the response.
Google also maps a string hd to UserInfo.ProviderData["hd"]. Interpret this key only for ProviderName == "Google". It is not a domain inferred from the email address or a universal tenant identifier. Other clients leave ProviderData null; raw responses and tokens are not copied into it.
Look up a previously linked account by ProviderName and UserInfo.Id, not its email address. Email is mutable contact data and can be reassigned; successful OAuth authentication does not guarantee that the provider has verified the mailbox. For Google, UserInfo.Id contains sub, which stays the same when an email address changes. Update the contact email without changing that external-account key.
Use EmailVerified is true when checking the provider's assertion; neither false nor null establishes verification. Even true alone does not authorize linking to an existing local account. Google can retain a verification assertion for an external email address after mailbox ownership changes. Apply an explicit linking policy using Google's email-authority guidance. To restrict Google Workspace access, compare the returned hd claim with your allowed domain; an email suffix or the mere presence of hd is insufficient.
Older UserInfo JSON deserializes with both new properties set to null. Default System.Text.Json serialization adds EmailVerified and ProviderData, including null values. Check consumers that require an exact JSON schema; naming and null-value settings remain under your application's control.
Supported Services
| Provider | Client Class | Status | API Version | Auth Endpoint | Last Verified | Docs |
|---|---|---|---|---|---|---|
| GitHub | GitHubClient |
Active | REST (default: 2022-11-28) | github.com/login/oauth/authorize |
2026-04-23 | Docs |
GoogleClient |
Active | OAuth2 v2 / UserInfo v3 | accounts.google.com/o/oauth2/v2/auth |
2026-04-23 | Docs | |
FacebookClient |
Active | Graph API v25.0 | www.facebook.com/v25.0/dialog/oauth |
2026-04-23 | Docs | |
| Microsoft | MicrosoftClient |
Active | Identity Platform v2.0 / Graph v1.0 | login.microsoftonline.com/common/oauth2/v2.0/authorize |
2026-04-23 | Docs |
| Asana | AsanaClient |
Maps current gid; retains legacy id |
API v1 | app.asana.com/-/oauth_authorize |
2026-10-25 (docs) | Docs |
| DigitalOcean | DigitalOceanClient |
OAuth supported; maps user UUID from token response | OAuth 2.0 | cloud.digitalocean.com/v1/oauth/authorize |
2026-10-25 (docs) | OAuth API |
| ExactOnline | ExactOnlineClient |
Maps OData Current/Me response |
REST API v1 | start.exactonline.nl/api/oauth2/auth |
2026-10-25 (docs) | Docs |
| Fitbit | FitbitClient |
Active | Web API v1 (user profile) | www.fitbit.com/oauth2/authorize |
2026-04-23 | Docs |
| Foursquare | FoursquareClient |
Deprecated | v2 (OAuth deprecated) | foursquare.com/oauth2/authorize |
2026-04-23 | Docs |
LinkedInClient |
Active | OAuth v2 (OpenID Connect) | www.linkedin.com/oauth/v2/authorization |
2026-04-23 | Docs | |
| LoginCidadao | LoginCidadaoClient |
Unknown | OpenID Connect v2 | logincidadao.rs.gov.br/openid/connect/authorize |
2026-04-23 | |
| MailRu | MailRuClient |
Active | OAuth 2.0 | connect.mail.ru/oauth/authorize |
2026-04-23 | Docs |
| Odnoklassniki | OdnoklassnikiClient |
Active | OAuth 2.0 | www.odnoklassniki.ru/oauth/authorize |
2026-04-23 | Docs |
| Salesforce | SalesforceClient |
Active | OAuth 2.0 (Web Server Flow) | login.salesforce.com/services/oauth2/authorize |
2026-04-23 | Docs |
| Spotify | SpotifyClient |
Active | Web API v1 | accounts.spotify.com/authorize |
2026-04-23 | Docs |
| Todoist | TodoistClient |
Active | REST API v1 | app.todoist.com/oauth/authorize |
2026-04-23 | Docs |
| X (Twitter) | XClient |
Active | OAuth 1.0a / API v1.1 | api.twitter.com/oauth/authenticate |
2026-04-23 | Docs |
| Uber | UberClient |
Requires approved application | OAuth 2.0 / userinfo v3 | auth.uber.com/oauth/v2/authorize |
2026-10-25 (docs) | Current userinfo API |
| VK (Vkontakte) | VkClient |
Active | API v5.131 | oauth.vk.com/authorize |
2026-04-23 | Docs |
| VSTS (Azure DevOps) | VSTSClient |
Deprecated (2026) | Azure DevOps OAuth (deprecated Apr 2025) | app.vssps.visualstudio.com/oauth2/authorize |
2026-04-23 | Docs |
| Windows Live | WindowsLiveClient |
Legacy (Working) | Live SDK v5.0 | login.live.com/oauth20_authorize.srf |
2026-04-23 | Migration Guide |
| Yahoo | YahooClient |
OAuth supported; uses current UserInfo API | OAuth 2.0 / OpenID Connect UserInfo | api.login.yahoo.com/oauth2/request_auth |
2026-10-25 (docs) | Sign In With Yahoo |
| Yandex | YandexClient |
Active | OAuth 2.0 (Yandex ID) | oauth.yandex.ru/authorize |
2026-04-23 | Docs |
Provider status and documentation checks do not establish that an authenticated login works. Tests cover requests and documented response shapes with synthetic values. Applications still need registered credentials, granted scopes, and any provider approval.
Provider compatibility and upgrades
These clients accept current responses and retain their existing legacy parser cases. Configured scopes are passed through unchanged; the library does not add permissions.
- Asana: requests and reads
gid, with a fallback to legacyid. Asana guarantees existing ID values do not change.UserInfo.Idalready stores strings, so existing account keys remain the same. The user endpoint requiresusers:readfor scoped applications. - ExactOnline: authorization uses
/api/oauth2/auth; profile requests still use/api/v1/current/Me, now with JSON requested explicitly. The parser readsUserID,FirstName,LastName,Email, andPictureUrlfrom the OData response. The old parser expected unrelated root fields and did not map this response. Applications that supplied custom profile responses retain that parser path. The built-in client still targets the Netherlands host. - DigitalOcean:
/v1/oauthremains the current OAuth 2.0 route. The client usesinfo.uuidfor the authenticating user, as DigitalOcean's own client does. It does not useteam_uuidas the user ID. A legacy rootuidstill takes precedence when supplied.ProviderNameis now correctly set toDigitalOcean. Applications with stored numeric IDs or a null provider name need an explicit account-link check; this library cannot infer a numeric-ID-to-UUID mapping. Do not link those accounts by email alone. - Uber: profile requests now use
/v3/me, withsubmapped toUserInfo.Idand the current name and verification claims mapped. The old parser leftIdnull. Uber requires application approval and theprofilescope for profile fields. Existing applications that linked accounts using another key must establish the new subject through a trusted account-link flow. - Yahoo: profile requests now use
/openid/v1/userinfo; the OAuth 2.0 authorization and token endpoints remain unchanged. ConfigureScope = "openid profile email"for names and email. Yahoo's migration guide maps the oldguidtosub. The client uses that subject for bothUserInfo.IdandUserProfileGUID, including when the token omits deprecatedxoauth_yahoo_guid. If both values are supplied, they must match. Missing claims remain unknown, and profile image sizes are preserved.
These provider requirements apply only to applications that use the affected client. They do not block deployments that use other providers. A real sign-in and account-link check is still required before enabling a newly configured provider in production.
Google retains its existing /oauth2/v3/userinfo route. Google's current OIDC discovery advertises https://openidconnect.googleapis.com/v1/userinfo; API path version numbers are distinct from OAuth protocol versions. Exposing the existing email_verified and hd claims does not require an endpoint migration.
Removed providers (Instagram, Xing): These providers' APIs have been retired or shut down. The client classes have been removed.
- Instagram: Basic Display API shut down Dec 4, 2024. There is no consumer OAuth replacement — the remaining Instagram APIs are business/creator-only. Announcement
- Xing: OAuth 1.0a REST API discontinued. dev.xing.com only hosts plugins.
Renamed providers:
TwitterClient→XClient(Twitter rebranded to X). The old class name is preserved as an obsolete derived class that produces a compiler error directing users to the new name.Legacy (Working):
WindowsLiveClient— Microsoft officially retired the Live SDK in Nov 2018 but the endpoints continue to function in production. Confirmed working in Exceptionless. For new integrations, useMicrosoftClient(Microsoft Identity Platform v2.0 + Graph). User IDs differ between the two platforms.Deprecated providers:
- Foursquare: v2 consumer OAuth API deprecated. The replacement Places API v3 uses API keys, not OAuth.
- VSTS (Azure DevOps): Azure DevOps OAuth stopped accepting new app registrations in April 2025 and is scheduled for full retirement in 2026. Use
MicrosoftClientwith Microsoft Entra ID OAuth instead
Goals
- Simplicity in usage — even a newcomer can call a couple of methods and receive the expected result
- Well-documented, testable, and tested code
- Flexible, transparent, and easily understandable design
- Support for both fine-grained control and simple plug-and-play usage
Dependencies
Contributors
- Constantin Titarenko (started development, defined library structure, released initial version)
- Blake Niemyjski (helped a lot to maintain the project, currently (since 2015) — top maintainer)
- Andriy Somak (helped with improvements on configuration and extending the list of supported services)
- Sascha Kiefer (simplified extending the library with own provider implementations, added GitHub client)
- Krisztián Pócza (added LinkedIn (OAuth 2) client)
- Jamie Houston (added a Todoist client)
- Sasidhar Kasturi (added Uber, Spotify, Yahoo)
- Jamie Dalton (added Visual Studio Team Services)
Acknowledgements
Many thanks to JetBrains company for providing free OSS licenses for ReSharper and dotCover - these tools allow us to work on this project with pleasure!
Also we glad to have opportunity to use free Teamcity CI server provided by Codebetter.com and JetBrains - many thanks for supporting OSS!
OAuth2 optimization would never be so simple without YourKit .NET profiler! We appreciate kind support of open source projects by YourKit LLC - the creator of innovative and intelligent tools for profiling .NET YourKit .NET Profiler and Java applications YourKit Java Profiler.
License
The MIT License (MIT) Copyright (c) 2012-2013 Constantin Titarenko, Andrew Semack and others
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. net11.0 is compatible. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- RestSharp (>= 114.1.0)
- System.Text.Json (>= 11.0.0)
-
net10.0
- RestSharp (>= 114.1.0)
-
net11.0
- RestSharp (>= 114.1.0)
NuGet packages (6)
Showing the top 5 NuGet packages that depend on OAuth2:
| Package | Downloads |
|---|---|
|
BotAuth.GenericOAuth2
This package is a generic OAuth2 authentication provider for using with the BotAuth package in a Bot Framework/BotBuilder Project. |
|
|
TQLApp.Plugins.GitHub
Techie's Quick Launcher plugin for GitHub. |
|
|
DashAPI
A client library for the Dash API's |
|
|
Vern.Social
Vern Social |
|
|
Vern.Social.Umbraco7
Social |
GitHub repositories (1)
Showing the top 1 popular GitHub repositories that depend on OAuth2:
| Repository | Stars |
|---|---|
|
exceptionless/Exceptionless
Exceptionless application
|
| Version | Downloads | Last Updated |
|---|---|---|
| 1.1.0 | 57 | 11/13/2026 |
| 1.0.0 | 8,940 | 5/11/2026 |
| 1.0.0-beta1 | 470 | 4/24/2026 |
| 0.10.3 | 66,985 | 2/25/2023 |
| 0.10.2 | 73,644 | 10/18/2021 |
| 0.10.1 | 64,092 | 10/12/2020 |
| 0.10.0 | 3,293 | 9/2/2020 |
| 0.9.13-pre | 33,404 | 11/8/2018 |
| 0.9.12-pre | 45,809 | 1/9/2018 |
| 0.9.11-pre | 2,840 | 11/10/2017 |
| 0.9.10-pre | 2,379 | 11/7/2017 |
| 0.9.8-pre | 2,346 | 10/6/2017 |
| 0.8.40 | 99,572 | 9/3/2016 |
| 0.8.38 | 2,376 | 8/31/2016 |
| 0.8.37 | 17,876 | 6/10/2016 |
| 0.8.36 | 15,739 | 10/22/2015 |
| 0.8.35 | 3,164 | 9/27/2015 |
| 0.8.34 | 27,805 | 7/31/2014 |
| 0.8.33 | 2,366 | 7/28/2014 |
| 0.8.32 | 2,655 | 6/30/2014 |