Polhem.OAuth2 1.1.0

dotnet add package Polhem.OAuth2 --version 1.1.0
                    
NuGet\Install-Package Polhem.OAuth2 -Version 1.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Polhem.OAuth2" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Polhem.OAuth2" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Polhem.OAuth2" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Polhem.OAuth2 --version 1.1.0
                    
#r "nuget: Polhem.OAuth2, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Polhem.OAuth2@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Polhem.OAuth2&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Polhem.OAuth2&version=1.1.0
                    
Install as a Cake Tool

Polhem.OAuth2

English | 繁體中文

Build CI

Lightweight OAuth2 sign-in for .NET. Desktop and console applications sign in through the system browser with a loopback redirect and PKCE, on Windows, macOS and Linux. .NET MAUI applications on Android, iOS and Mac Catalyst sign in through WebAuthenticator, directly or through their own back end. ASP.NET Core and ASP.NET (System.Web) applications use the authorization code flow with PKCE, and keep each sign-in in a protected cookie.

Supported providers: Google, Facebook, LINE, Microsoft Entra ID, Auth0 and Okta.

Packages

Package Target frameworks Use it for
Polhem.OAuth2 netstandard2.0, net10.0 The providers, sign-in from desktop, console and .NET MAUI applications, and other server frameworks
Polhem.OAuth2.AspNetCore net10.0 ASP.NET Core applications
Polhem.OAuth2.AspNet net472 ASP.NET Web Forms and MVC applications on System.Web
dotnet add package Polhem.OAuth2

Options

Each provider has its own options type: GoogleOAuth2Options, FacebookOAuth2Options, LineOAuth2Options, AzureOAuth2Options (Microsoft Entra ID), Auth0OAuth2Options and OktaOAuth2Options.

  • ClientId and RedirectUri are required. A client copies and checks the options when it is created, so later changes to them have no effect, and invalid options throw ArgumentException right away.
  • Auth0 and Okta need Domain, such as your-tenant.auth0.com. Okta uses the default authorization server unless AuthorizationServerId names another one; an empty value selects the org authorization server.
  • Microsoft Entra ID uses the common tenant. An application registered for a single tenant sets Tenant to the tenant ID or domain name.
  • Every endpoint must be an absolute https URI.
  • UsePkce is true by default.

Desktop and console applications

LoopbackOAuth2Client listens on the redirect URI, opens the authorization URL in the default browser, waits for the provider to redirect back, and exchanges the authorization code.

using Polhem.OAuth2;

var options = new GoogleOAuth2Options
{
    ClientId = "your-client-id",
    ClientSecret = "your-client-secret",
    RedirectUri = "http://127.0.0.1:0/callback"
};

var client = new LoopbackOAuth2Client(options);
AuthorizationResult result = await client.SignInAsync();

if (result.IsSuccess)
    Console.WriteLine($"{result.UserInfo.UserId} {result.UserInfo.UserName} {result.UserInfo.Email}");
else
    Console.WriteLine($"The sign-in failed: {result.Exception.Message}");
  • The redirect URI must be an http URI on localhost or a loopback address, and it must be registered with the provider. Port 0 picks a free port for each sign-in, which only works with providers that accept any loopback port.
  • The client always uses PKCE. A client secret shipped with a desktop application can be extracted, so it is not sent, except to Google.
  • A timeout (Timeout, 5 minutes by default), cancellation, or an error from the provider becomes a failed result. A port that cannot be listened on throws SocketException, and a missing default browser throws Win32Exception (PlatformNotSupportedException on iOS, which cannot start a process).
  • Set OpenBrowser to open the URL another way, for example uri => launcher.LaunchUriAsync(uri) with the launcher of a UI framework.
  • The snippet uses top-level statements. The OAuthWinForms sample shows the same sign-in in a Windows Forms application on .NET Framework.
  • An application that targets .NET Framework 4.7.2 and runs on a machine with FIPS mode enabled needs the setting described under "Before deploying" in the ASP.NET (System.Web) section.
  • To sign in to a separate back end as well, see the "Apps with a separate back end" section.

The reasons behind this design, and how each provider handled loopback redirects, are recorded in ADR-004.

Registering a loopback redirect URI

These registrations were tested with each provider on 2026-09-14.

Provider Application type Redirect URI Port
Google Desktop app http://127.0.0.1:0/callback Any port was accepted
Microsoft Entra ID Mobile and desktop applications, registered as http://localhost http://localhost:0 Ignored
Auth0 Native http://127.0.0.1:53682/callback Must match
Okta Native, with client authentication None and PKCE required http://localhost:53682/callback Must match
LINE LINE Login channel, Callback URL http://localhost:53682/callback Must match
Facebook Facebook Login, Valid OAuth Redirect URIs http://localhost:53682/callback Register the port you use
  • Google: the tested client had its redirect URIs registered. Google's documentation differs on whether a desktop app needs them, and lists the client secret as optional for installed applications.
  • Okta: the authorization server needs an access policy with a rule that allows the authorization code grant. Without one, the sign-in fails with a policy evaluation error.
  • Facebook refused 127.0.0.1; use localhost. Whether the tested app was in development or live mode was not recorded.
  • A redirect URI without a port means port 80, which usually needs administrator rights to listen on. Name the port.

.NET MAUI applications

An application on Android, iOS or Mac Catalyst signs in in one of two ways (ADR-006):

  • Directly, for an application without a back end of its own. AppOAuth2Client opens the sign-in with WebAuthenticator, the provider redirects to a URI scheme of the application, and the client exchanges the code with PKCE and without a client secret.
  • Through the back end, for an application that signs in to its own ASP.NET Core back end. The back end signs in as a web client and gives the application a single-use code, which the application redeems for the user information. Google and LINE accept no direct redirect to an Android application, so on Android they need this way.

WebAuthenticator does not work on Windows, so the Windows platform of a MAUI application signs in with LoopbackOAuth2Client, as a desktop application does.

Signing in directly

using Polhem.OAuth2;

var options = new Auth0OAuth2Options
{
    Domain = "your-tenant.auth0.com",
    ClientId = "your-native-client-id",
    RedirectUri = "com.example.app:/oauth2redirect"
};

var client = new AppOAuth2Client(options, async (url, redirectUri, cancellationToken) =>
    (await WebAuthenticator.Default.AuthenticateAsync(url, redirectUri)).CallbackUri);

AuthorizationResult result = await client.SignInAsync();
  • The redirect URI is a custom scheme or an https URI. http, javascript, data and file URIs, relative URIs, and URIs with a fragment are rejected.
  • Each provider needs its own redirect form and registration, such as fb<app id>://authorize for Facebook and line3rdp.<bundle id>://auth for LINE on iOS. The table in ADR-006 lists them with the platforms tested.
  • Set no client secret: anything shipped with an application can be extracted.
  • Closing the sign-in becomes a failed result with OperationCanceledException, and an error from the provider, such as access_denied, one with OAuth2Exception.
  • The user information stays in the application. It is no proof of identity for a server, so an application that signs in to its own back end signs in through the back end.

Platform settings

The application must receive the redirect to its scheme:

  • iOS and Mac Catalyst: list the scheme under CFBundleURLTypes in Info.plist. A Mac Catalyst application in the App Sandbox also needs the com.apple.security.network.client entitlement.
  • Android: add an activity that derives from WebAuthenticatorCallbackActivity, with an intent filter for the scheme, and declare the Custom Tabs service under <queries> in AndroidManifest.xml, which Android 11 and later require to open it.
[Activity(NoHistory = true, LaunchMode = LaunchMode.SingleTop, Exported = true)]
[IntentFilter([Intent.ActionView], Categories = [Intent.CategoryDefault, Intent.CategoryBrowsable], DataScheme = "com.example.app")]
public class CallbackActivity : WebAuthenticatorCallbackActivity
{
}
<queries>
  <intent>
    <action android:name="android.support.customtabs.action.CustomTabsService" />
  </intent>
</queries>

Signing in through the back end

The back end registers its web clients as in the ASP.NET Core section, and the redirect URIs of its applications:

builder.Services.AddOAuth2AppRelay(options => options.AppRedirectUris.Add("com.example.app:/relay"));
  • The application creates a PKCE code verifier and opens a URL of the back end with its redirect URI and the S256 code challenge. That endpoint calls oauth2Manager.RedirectToAppAuthorization(HttpContext, "Google", redirectUri, codeChallenge).
  • The provider returns to the back end's usual callback. After CompleteAuthorizationAsync, oauth2Manager.RedirectToAppAsync(HttpContext, result, cancellationToken) sends a sign-in that an application started back to the application with a single-use code and returns true. For a sign-in in the browser it returns false.
  • The application posts the code and its verifier to the back end, where oauth2Manager.RedeemAppCodeAsync returns the user information, or null. The back end then issues the application's own session; the provider's tokens stay on the back end.
  • The codes are kept in IDistributedCache. With several servers, use a distributed cache and share the data protection keys, as for web sign-ins.
  • The OAuthAspNetCore and OAuthMaui samples show both sides.

ASP.NET Core

using Polhem.OAuth2;

builder.Services.AddControllers();
builder.Services.AddOAuth2Client("Google", new GoogleOAuth2Options
{
    ClientId = "your-client-id",
    ClientSecret = "your-client-secret",
    RedirectUri = "https://localhost:7032/auth/callback"
});

var app = builder.Build();
app.MapControllers();
using Microsoft.AspNetCore.Mvc;
using Polhem.OAuth2;
using Polhem.OAuth2.AspNetCore;

public class AuthController(OAuth2Manager oauth2Manager) : ControllerBase
{
    [HttpGet("/auth/login")]
    public IActionResult Login()
    {
        return Redirect(oauth2Manager.CreateAuthorizationUrl(HttpContext, "Google"));
    }

    [HttpGet("/auth/callback")]
    public async Task<IActionResult> Callback()
    {
        AuthorizationResult result = await oauth2Manager.CompleteAuthorizationAsync(HttpContext, HttpContext.RequestAborted);
        return result.IsSuccess
            ? Content($"{result.UserInfo.UserId} {result.UserInfo.UserName} {result.UserInfo.Email}")
            : Content($"The sign-in failed: {result.Exception.Message}");
    }
}
  • AddOAuth2Client registers the client, OAuth2Manager and ASP.NET Core data protection. The client is created by the call, so invalid options stop the application at startup. It takes an optional HttpClient.
  • oauth2Manager.GetClient("Google") returns the client, for example to call RefreshTokenAsync.

ASP.NET (System.Web)

There is no runnable sample for System.Web, because its projects cannot be built with the dotnet CLI. The package is used the same way, through the static OAuth2Manager:

using Polhem.OAuth2;
using Polhem.OAuth2.AspNet;

// Global.asax.cs
protected void Application_Start()
{
    OAuth2Manager.RegisterClient("Google", new GoogleOAuth2Options
    {
        ClientId = "your-client-id",
        ClientSecret = "your-client-secret",
        RedirectUri = "https://localhost:44300/auth/callback"
    });
}
// An MVC controller. In Web Forms, call OAuth2Manager.RedirectToAuthorization("Google") from the sign-in page and return,
// and await OAuth2Manager.CompleteAuthorizationAsync() on the callback page, which needs Async="true".
public class AuthController : Controller
{
    public ActionResult Login()
    {
        return Redirect(OAuth2Manager.CreateAuthorizationUrl(HttpContext, "Google"));
    }

    public async Task<ActionResult> Callback()
    {
        AuthorizationResult result = await OAuth2Manager.CompleteAuthorizationAsync(HttpContext);
        if (result.IsSuccess)
            return Content(result.UserInfo.UserId + " " + result.UserInfo.UserName + " " + result.UserInfo.Email);

        return Content("The sign-in failed: " + result.Exception.Message);
    }
}

Before deploying:

  • Target .NET Framework 4.7.2 or later, and set <httpRuntime targetFramework="4.7.2" />, or your later version, in web.config. Asynchronous pages and the operating system's TLS defaults depend on it.
  • On .NET Framework the core package depends on System.Text.Json. Keep the binding redirects that NuGet adds for it and its dependencies in web.config.
  • When more than one server can receive the callback, set the same <machineKey> in web.config on each of them.
  • On a machine with FIPS mode enabled, an application that targets .NET Framework 4.7.2 can get a CryptographicException when a sign-in starts: for such applications .NET Framework blocks the managed SHA-256 implementation that PKCE uses. Target .NET Framework 4.8 or later, or set the Switch.System.Security.Cryptography.UseLegacyFipsThrow switch to false. See Managed cryptography classes do not throw a CryptographyException in FIPS mode.

Web applications: how a sign-in is kept

  • Each sign-in keeps its state, PKCE code verifier, redirect URI and client name in a cookie of its own, encrypted and authenticated with ASP.NET Core data protection or MachineKey. No session state is needed, and sign-ins started in several tabs do not replace each other. See ADR-005.
  • The cookie name starts with __Host-, and the cookie is Secure, HTTP-only and SameSite=Lax, so the sign-in must start and end on HTTPS pages.
  • A sign-in must complete within 10 minutes. The callback removes the cookie before it exchanges the code.
  • Every server that can receive the callback must be able to decrypt the cookie: share the data protection key ring in ASP.NET Core, or use the same machine key on System.Web.

Other server frameworks

OAuth2Client in the core package runs the same flow without an HTTP framework. Keep the pending values where only the browser that started the sign-in can present them, such as an encrypted, HTTP-only cookie, and remove them when the callback is handled.

var client = new OAuth2Client(options);

// Start the sign-in. Keep and Redirect stand for code of your framework.
AuthorizationRequest request = client.CreateAuthorizationRequest();
Keep(request.Pending.State, request.Pending.CodeVerifier, request.Pending.RedirectUri);
Redirect(request.Url);

// Complete it in the callback.
var pending = new PendingAuthorization(keptState, keptCodeVerifier, keptRedirectUri);
var callback = new AuthorizationCallback(query["code"], query["state"], query["error"], query["error_description"]);
AuthorizationResult result = await client.CompleteAuthorizationAsync(callback, pending, cancellationToken);

Results, tokens and errors

  • A successful result has ProviderName, UserInfo and Token; a failed result has Exception.
  • Token holds the access token, and the refresh token, ID token, lifetime and scopes when the provider returns them. RefreshTokenAsync on a client obtains new tokens. Keep the refresh token of the latest response, because some providers issue a new one each time. Facebook does not issue refresh tokens.
  • Exception holds only the failures a sign-in is expected to produce, such as a failed HTTP request, a state that does not match, or an error from the provider. Configuration and programming errors, such as an unregistered client name, are thrown. See ADR-003.
  • An error from the provider is an OAuth2Exception: Error holds the error code, such as access_denied, and ErrorDescription the provider's text. Anyone who sends the user a link can set the values of an error in a redirect, so encode them before showing them.

Identifying users

  • Identify a user by the provider name together with UserInfo.UserId, not by Email: an address can change, and providers differ in whether they verify it.
  • The provider name is AuthorizationResult.ProviderName: Google, Facebook, LINE, Azure (Microsoft Entra ID), Auth0 or Okta. It does not depend on the name a client is registered under.
  • For Microsoft Entra ID, UserId is the sub claim, which is different for each application the user signs in to.
  • LINE returns the email address only in the ID token, and only when the channel may read it and the user agreed. The library reads it from the ID token that the token endpoint returned, and checks that the token was issued to the client, but does not check its signature.
  • The library does not validate ID tokens. Token.IdToken is returned as the provider sent it; validate it before relying on its claims.

Apps with a separate back end

For brevity, the desktop example signs in and reads the user information in one call, which suits an application that uses the result itself. When the front end signs in and then signs in to a separate back end, do not send the user information from the front end to the back end as proof of identity: the back end cannot tell whether it was forged.

  • After signing in, the front end passes the token to the back end over HTTPS, and the back end obtains the user information from the provider with that token.
  • Before the back end trusts the token, it confirms that the token was issued to its own client ID, for example through the provider's token verification endpoint or by validating the signature and audience of the ID token. A request to the user information endpoint alone does not confirm this: a token that another application obtained for the same user returns the same user.
  • The library does not provide these back-end steps. A .NET MAUI application can sign in through its ASP.NET Core back end instead; see the .NET MAUI section.

Migrating from Bee.OAuth2

Bee.OAuth2 package Replacement
Bee.OAuth2 Polhem.OAuth2
Bee.OAuth2.AspNet Polhem.OAuth2.AspNet
Bee.OAuth2.AspNetCore Polhem.OAuth2.AspNetCore
Bee.OAuth2.WinForms, Bee.OAuth2.Desktop LoopbackOAuth2Client in Polhem.OAuth2
  • Namespaces: Bee.OAuth2 becomes Polhem.OAuth2, and so on for each package.
  • Web registration: ASP.NET Core registers each client with AddOAuth2Client, and System.Web with OAuth2Manager.RegisterClient(name, options). Session state and OAUTH2_STATE_KEY are no longer used.
  • Web methods: GetAuthorizationUrl becomes CreateAuthorizationUrl, and ValidateAuthorization becomes CompleteAuthorizationAsync. In ASP.NET Core they take the HttpContext. A sign-in started before the upgrade does not complete after it; the user signs in again.
  • Desktop sign-in moves from an embedded WebView2 window to the system browser. The synchronous Authorization(), Caption, Width, Height, AuthorizationForm, and the desktop OAuth2Client, OAuth2Manager and StateStorage are gone; call LoopbackOAuth2Client.SignInAsync instead.
  • Redirect URIs for desktop applications must be registered again as loopback URIs (see the table above). URIs that only worked inside an embedded browser, such as https://login.microsoftonline.com/common/oauth2/nativeclient, no longer work.
  • Results and tokens: AuthorizationResult is read-only. AccessToken becomes Token.AccessToken, and refreshing moves to RefreshTokenAsync on the client, which returns a TokenResponse.
  • Types that are no longer public: the provider classes, IOAuth2Provider, BaseOAuth2Client, IStateStorage, PkceHelper and OAuth2StateCryptor. Applications use an options type with a client or a manager.
  • PKCE and the client secret: UsePkce is true by default. A web client sends the client secret whenever it is set, also with PKCE.
  • Endpoints must be https. Domain of Auth0 and Okta takes a host name, with or without https://.
  • Exceptions: AuthorizationResult.Exception no longer collects unexpected exceptions; they are thrown.
  • JSON null: a user information field whose value is JSON null is now null; Bee.OAuth2 returned an empty string. Fallback fields therefore take effect, such as nickname when Auth0 returns name as JSON null.
  • Target frameworks: Polhem.OAuth2.AspNet targets .NET Framework 4.7.2, and Polhem.OAuth2.AspNetCore targets net10.0.
  • Dependencies: no Bee.Base or Newtonsoft.Json. JSON is parsed with System.Text.Json, which the netstandard2.0 build references as a package.

Samples

The samples and the loopback redirect probe read their provider settings from one OAuthConfig.json in the repository root. Copy OAuthConfig.example.json to OAuthConfig.json and fill it in; the build copies the file to the output folder of each project. OAuthConfig.json is ignored by git; keep credentials out of OAuthConfig.example.json.

Every provider holds one client per client type, because a provider registers desktop, web and mobile clients separately:

{
  "Providers": {
    "Okta": {
      "Domain": "",
      "Desktop": { "ClientId": "", "RedirectUri": "http://localhost:53682/callback" },
      "Web": { "ClientId": "", "ClientSecret": "", "RedirectUri": "https://localhost:7032/auth/callback" }
    }
  }
}
  • The console and Windows Forms samples and the probe read Desktop, and the ASP.NET Core sample reads Web. A client section takes the properties of that provider's options type, such as Scopes and UsePkce, and what it leaves out keeps the default of the type.
  • The fields both clients share, none of which are credentials, sit in the provider section: Domain for Auth0 and Okta, AuthorizationServerId for Okta, and Tenant for Azure. Anything else there is an error, so credentials cannot end up shared by accident.
  • A provider whose back end registers one client for both, such as a LINE channel with two callback URLs, gets the same ClientId and ClientSecret in both sections.
  • A section whose ClientId is still empty is skipped, so the samples offer only the providers you filled in.
  • The OAuthMaui sample reads iOS on iOS and Mac Catalyst, Android on Android, and Desktop on Windows. An iOS or Android section cannot hold a ClientSecret, because an application cannot keep one: loading such a file fails. The build of OAuthMaui packages only ClientId, RedirectUri, Scopes, UsePkce and the shared fields, never a secret. Google and LINE refuse a direct redirect to an Android application (ADR-006), so they have no Android section, and the sample signs in to them through the back end there.
  • The top-level AppRelay section configures the back-end relay: BackendUrl is where the ASP.NET Core sample runs, and RedirectUri is the relay callback of the application, which the ASP.NET Core sample registers with AddOAuth2AppRelay. The relay runs over HTTPS, so the simulator or emulator must trust the ASP.NET Core development certificate. On the Android emulator, run adb reverse tcp:7032 tcp:7032 so that localhost reaches the host.
Sample Shows
OAuthConsole Desktop sign-in from a console application, on any operating system
OAuthDesktop Desktop sign-in from Windows Forms on .NET
OAuthWinForms Desktop sign-in from Windows Forms on .NET Framework 4.8
OAuthAspNetCore ASP.NET Core, including the relay endpoints for OAuthMaui
OAuthMaui .NET MAUI: direct and back-end relay sign-in on Android, iOS and Mac Catalyst, loopback sign-in on Windows. It needs the MAUI workload and is not part of the solution.

LoopbackRedirectProbe checks whether a provider accepts a loopback redirect URI before you build on it.

Design decisions

The reasons behind the design are recorded in the architecture decision records.

License

MIT. Copyright (c) Polhem contributors.

Polhem.OAuth2 continues Bee.OAuth2.

Product 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. 
.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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Polhem.OAuth2:

Package Downloads
Polhem.OAuth2.AspNetCore

OAuth2 sign-in for ASP.NET Core applications, with Google, Facebook, LINE, Microsoft Entra ID, Auth0 and Okta.

Polhem.OAuth2.AspNet

OAuth2 sign-in for ASP.NET Web Forms and MVC applications on System.Web, with Google, Facebook, LINE, Microsoft Entra ID, Auth0 and Okta.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 0 9/19/2026
1.0.0 58 9/14/2026