JoeDevSharp.Net.Extensions.OAuth2
1.0.5
dotnet add package JoeDevSharp.Net.Extensions.OAuth2 --version 1.0.5
NuGet\Install-Package JoeDevSharp.Net.Extensions.OAuth2 -Version 1.0.5
<PackageReference Include="JoeDevSharp.Net.Extensions.OAuth2" Version="1.0.5" />
<PackageVersion Include="JoeDevSharp.Net.Extensions.OAuth2" Version="1.0.5" />
<PackageReference Include="JoeDevSharp.Net.Extensions.OAuth2" />
paket add JoeDevSharp.Net.Extensions.OAuth2 --version 1.0.5
#r "nuget: JoeDevSharp.Net.Extensions.OAuth2, 1.0.5"
#:package JoeDevSharp.Net.Extensions.OAuth2@1.0.5
#addin nuget:?package=JoeDevSharp.Net.Extensions.OAuth2&version=1.0.5
#tool nuget:?package=JoeDevSharp.Net.Extensions.OAuth2&version=1.0.5
Net.Extensions.OAuth2
Lightweight and extensible OAuth2 authentication framework for .NET 6+, designed to simplify integration with multiple providers and enable easy custom extensions.
Why use this framework?
- Simplicity & Control: No complex external servers or heavy libraries. Just pure .NET with HttpClient and your own rules.
- Extensible: Supports the most popular providers (Google, GitHub, Microsoft, Apple, Facebook, LinkedIn, Twitter, Keycloak) and allows you to easily create any custom provider.
- Full OAuth2 Authorization Code Flow: Includes PKCE, automatic authorization code capture via embedded local server, token management, and refresh handling.
- Unified User Model: Maps user info into a standard
AuthUser
model for straightforward consumption. - Compatible with Desktop, Web, and Service apps: Designed for multi-scenario use with no external dependencies.
How it works — Typical Authentication Flow
- You create the provider with your client credentials and endpoint URLs.
- Call
LoginAsync()
, which opens the browser to the provider login page. - The framework spins up a local HTTP server to catch the OAuth2 authorization code redirected back.
- The code is automatically exchanged for an access token.
- User information is retrieved and mapped into an
AuthUser
instance. - You can use the access token for API calls and the framework handles refreshing automatically if needed.
- Call
LogoutAsync()
to clear session data.
Supported Providers & Configuration
You can customize any provider by extending CustomOAuth2Provider
.
Basic Usage Example (Google)
var provider = new GoogleProvider(
clientId: "your-client-id",
clientSecret: "your-client-secret",
redirectUri: "http://localhost:60000/",
scopes: new[] { "openid", "email", "profile" }
);
try
{
var user = await provider.LoginAsync();
if (user != null)
{
Console.WriteLine($"Authenticated user: {user.Username} - {user.Email}");
// Here you can save tokens, call APIs with AccessToken, etc.
}
}
catch (Exception ex)
{
Console.WriteLine($"Login failed: {ex.Message}");
}
Note:
LoginAsync()
opens the browser and starts a local HTTP server to automatically capture the OAuth2 code.
Creating a Custom Provider
For unsupported providers, extend CustomOAuth2Provider
and override GetUserInfoAsync
to adapt user data retrieval.
public class MyCustomProvider : CustomOAuth2Provider
{
public MyCustomProvider(OAuth2Options options) : base(options) { }
public override async Task<AuthUser> GetUserInfoAsync(string accessToken)
{
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var json = await client.GetStringAsync(Options.UserInfoEndpoint);
var data = JsonDocument.Parse(json).RootElement;
return new AuthUser
{
Id = data.GetProperty("id").GetString() ?? "",
Username = data.TryGetProperty("username", out var u) ? u.GetString() ?? "" : "",
Email = data.TryGetProperty("email", out var e) ? e.GetString() ?? "" : "",
Picture = data.TryGetProperty("avatar", out var a) ? a.GetString() ?? "" : "",
Roles = new List<string>(),
Claims = data.EnumerateObject().ToDictionary(x => x.Name, x => x.Value.ToString())
};
}
}
Important Interfaces and Models
IAuthProvider
public interface IAuthProvider
{
bool IsAuthenticated { get; }
AuthUser? CurrentUser { get; }
Task<AuthUser?> LoginAsync();
Task LogoutAsync();
}
Core Models
- OAuth2Options: Client configuration, endpoints, and scopes.
- OAuth2Token: Access and refresh tokens, expiration, type, and scope.
- AuthUser: Authenticated user with Id, Username, Email, Picture, Roles, and Claims.
Implementing the IAuthProvider
Interface
Here is a simple full implementation example for a custom OAuth2 provider using IAuthProvider
:
public class SimpleCustomProvider : IAuthProvider
{
private readonly OAuth2Options _options;
private OAuth2Token? _token;
public AuthUser? CurrentUser { get; private set; }
public bool IsAuthenticated => CurrentUser != null;
public SimpleCustomProvider(OAuth2Options options)
{
_options = options;
}
public async Task<AuthUser?> LoginAsync()
{
// Build authorization URL with PKCE, scopes, clientId, redirectUri
var authUrl = OAuth2Helper.BuildAuthorizationUrl(_options);
// Open browser and start local server to capture code
var code = await OAuth2Helper.GetCodeViaLocalServerAsync(authUrl, _options.RedirectUri);
if (string.IsNullOrEmpty(code))
throw new Exception("Authorization code not received.");
// Exchange code for token
_token = await OAuth2Helper.ExchangeCodeForTokenAsync(code, _options);
if (_token == null || string.IsNullOrEmpty(_token.AccessToken))
throw new Exception("Failed to obtain access token.");
// Get user info with token
CurrentUser = await GetUserInfoAsync(_token.AccessToken);
return CurrentUser;
}
public Task LogoutAsync()
{
_token = null;
CurrentUser = null;
return Task.CompletedTask;
}
private async Task<AuthUser> GetUserInfoAsync(string accessToken)
{
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", accessToken);
var response = await client.GetAsync(_options.UserInfoEndpoint);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
var data = JsonDocument.Parse(json).RootElement;
return new AuthUser
{
Id = data.GetProperty("id").GetString() ?? "",
Username = data.TryGetProperty("username", out var u) ? u.GetString() ?? "" : "",
Email = data.TryGetProperty("email", out var e) ? e.GetString() ?? "" : "",
Picture = data.TryGetProperty("picture", out var p) ? p.GetString() ?? "" : "",
Roles = new List<string>(),
Claims = data.EnumerateObject().ToDictionary(x => x.Name, x => x.Value.ToString())
};
}
}
Usage example
var options = new OAuth2Options
{
ClientId = "your-client-id",
ClientSecret = "your-client-secret",
RedirectUri = "http://localhost:60000/",
AuthorizationEndpoint = "https://provider.com/oauth2/authorize",
TokenEndpoint = "https://provider.com/oauth2/token",
UserInfoEndpoint = "https://provider.com/api/userinfo",
Scopes = new[] { "openid", "profile", "email" }
};
var provider = new SimpleCustomProvider(options);
try
{
var user = await provider.LoginAsync();
Console.WriteLine($"User logged in: {user?.Username} ({user?.Email})");
}
catch (Exception ex)
{
Console.WriteLine($"Login failed: {ex.Message}");
}
Security & Best Practices
- PKCE support: protects OAuth2 flow in public and desktop apps.
- Local server for redirect URI: no external redirect URI setup needed.
- Token refresh: automatic handling to maintain session without frequent logins.
- Error handling: clear exceptions for easier debugging.
- No automatic storage: you control token persistence for your scenario.
- No external dependencies: pure .NET standard for maximum control and lightweight use.
Requirements
- .NET 6 or higher
- System.Text.Json
- HttpClient
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. 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 was computed. 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. |
-
net8.0
- JoeDevSharp.Net.Extensions.Auth.Abstractions (>= 1.0.4)
- Microsoft.AspNetCore (>= 2.3.0)
- Microsoft.IdentityModel.Tokens (>= 8.12.1)
- Microsoft.Web.WebView2 (>= 1.0.3296.44)
- System.IdentityModel.Tokens.Jwt (>= 8.12.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.