Skip to content

.NET Integration

This guide covers everything you need to integrate AuthStack into your .NET application using the official SDK.

Terminal window
dotnet add package AuthStack.Client
Terminal window
Install-Package AuthStack.Client
<PackageReference Include="AuthStack.Client" Version="1.3.1" />
using AuthStack.Client;
var client = new AuthStackClient(new AuthStackConfig
{
BaseUrl = "https://api.authstack.voostack.com"
});
// Initialize (restores any stored session)
await client.InitializeAsync();
// Register
var result = await client.RegisterAsync(
email: "user@example.com",
password: "securePassword123",
firstName: "John",
lastName: "Doe"
);
if (result.IsSuccess)
{
Console.WriteLine($"Welcome, {result.User!.FullName}!");
}
// Login
var loginResult = await client.LoginAsync("user@example.com", "password123");
if (loginResult.IsSuccess)
{
Console.WriteLine($"Logged in as {loginResult.User!.Email}");
Console.WriteLine($"Access token: {loginResult.Tokens!.AccessToken}");
}
else
{
Console.WriteLine($"Login failed: {loginResult.Error}");
}

AuthStack handles OAuth app configuration centrally. You don’t need your own OAuth credentials:

// Get the OAuth authorization URL
var authUrl = await client.GetProviderAuthUrlAsync(
OAuthProvider.GitHub,
"https://yourapp.com/oauth/callback"
);
// Redirect user to authUrl.AuthorizationUrl
// After callback, link the provider
var linked = await client.LinkProviderWithCodeAsync(
OAuthProvider.GitHub,
codeFromCallback,
"https://yourapp.com/oauth/callback"
);

If you have your own OAuth app configured:

// Using an ID token (e.g., from Google Sign-In)
var result = await client.LoginWithProviderTokenAsync(
OAuthProvider.Google,
googleIdToken
);
// Using an authorization code (redirect flow)
var result = await client.LoginWithProviderCodeAsync(
OAuthProvider.GitHub,
authorizationCode,
"https://myapp.com/callback"
);

If your application uses AuthStack as its identity provider (users log in through AuthStack’s OAuth flow), use ExchangeCodeAsync() instead of LoginWithProviderCodeAsync().

ScenarioMethodEndpoint
User logs in with Google/GitHub via your appLoginWithProviderCodeAsync()/auth/oauth/{provider}
User logs in through AuthStack’s authorize pageExchangeCodeAsync()/oauth/token
Your backend exchanges code from AuthStack OAuth flowExchangeCodeAsync()/oauth/token

When AuthStack is your identity provider:

// 1. Configure client with OAuth credentials
var client = new AuthStackClient(new AuthStackConfig
{
BaseUrl = "https://api.authstack.voostack.com",
ClientId = "your-app-client-id", // From your OAuth app in AuthStack
ClientSecret = "your-app-secret" // From your OAuth app in AuthStack
});
await client.InitializeAsync();
// 2. After user is redirected back with authorization code
var result = await client.ExchangeCodeAsync(code, redirectUri);
if (result.IsSuccess)
{
Console.WriteLine($"Logged in as {result.User!.Email}");
// Access token is now available: result.Tokens!.AccessToken
}

For apps that issue their own tokens, use ExchangeCodeViaBackendAsync():

// Exchange code via your own backend API
var result = await client.ExchangeCodeViaBackendAsync(
backendUrl: "https://api.myapp.com/auth/oauth/callback",
code: authorizationCode,
redirectUri: "https://myapp.com/callback"
);

Your backend receives the code and can:

  1. Exchange it with AuthStack for tokens
  2. Create/lookup local user records
  3. Issue your own JWT tokens

When using GitHub OAuth, request these scopes for full functionality:

ScopePurpose
repoAccess private repositories
read:userRead user profile data
user:emailAccess user email addresses

After linking a provider, get their access token to make API calls:

// Get GitHub credentials
var credentials = await client.GetProviderCredentialsAsync(OAuthProvider.GitHub);
// Check if expired
if (credentials.IsExpired)
{
// Re-authenticate with GitHub
}
// Use with Octokit (GitHub .NET SDK)
var github = new GitHubClient(new ProductHeaderValue("MyApp"));
github.Credentials = new Credentials(credentials.AccessToken);
var repos = await github.Repository.GetAllForCurrent();
// Or with HttpClient
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", credentials.AccessToken);
var response = await httpClient.GetAsync("https://api.github.com/user/repos");
PropertyTypeDescription
ProviderTypestringProvider name (e.g., “github”)
AccessTokenstringOAuth access token
RefreshTokenstring?Refresh token (if available)
ExpiresAtDateTime?Token expiration time
Scopesstring?Granted OAuth scopes
IsExpiredboolWhether token has expired

Link multiple OAuth providers to a single account:

// Get linked providers
var providers = await client.GetLinkedProvidersAsync();
foreach (var provider in providers)
{
Console.WriteLine($"{provider.ProviderType}: {provider.Email}");
}
// Link a new provider with token
var linked = await client.LinkProviderWithTokenAsync(
OAuthProvider.GitHub,
githubAccessToken
);
// Link with authorization code
var linked = await client.LinkProviderWithCodeAsync(
OAuthProvider.GitHub,
authorizationCode,
"https://yourapp.com/callback"
);
// Unlink a provider
await client.UnlinkProviderAsync(OAuthProvider.GitHub);
// Listen for status changes
client.StatusChanged += (sender, status) =>
{
Console.WriteLine($"Auth status changed: {status}");
switch (status)
{
case AuthStatus.Authenticated:
Console.WriteLine("User is logged in");
break;
case AuthStatus.Unauthenticated:
Console.WriteLine("User is logged out");
break;
case AuthStatus.Loading:
Console.WriteLine("Loading...");
break;
}
};
// Listen for user changes
client.UserChanged += (sender, user) =>
{
if (user != null)
{
Console.WriteLine($"Current user: {user.FullName}");
}
else
{
Console.WriteLine("No user logged in");
}
};
if (client.IsAuthenticated)
{
var user = client.CurrentUser;
var accessToken = client.AccessToken;
var tokens = client.Tokens;
}
// Manually refresh token
var success = await client.RefreshTokenAsync();
if (!success)
{
// Token refresh failed, user needs to re-authenticate
}

By default, tokens are stored in memory. Implement ITokenStorage for persistence:

public class SecureTokenStorage : ITokenStorage
{
public async Task SaveTokensAsync(AuthTokens tokens, CancellationToken ct = default)
{
// Save to secure storage (e.g., encrypted file, Windows Credential Manager)
var json = JsonSerializer.Serialize(tokens);
await File.WriteAllTextAsync("tokens.enc", Encrypt(json), ct);
}
public async Task<AuthTokens?> GetTokensAsync(CancellationToken ct = default)
{
if (!File.Exists("tokens.enc")) return null;
var encrypted = await File.ReadAllTextAsync("tokens.enc", ct);
var json = Decrypt(encrypted);
return JsonSerializer.Deserialize<AuthTokens>(json);
}
public async Task ClearTokensAsync(CancellationToken ct = default)
{
if (File.Exists("tokens.enc"))
File.Delete("tokens.enc");
}
public Task<bool> HasTokensAsync(CancellationToken ct = default)
{
return Task.FromResult(File.Exists("tokens.enc"));
}
}
// Use custom storage
var client = new AuthStackClient(new AuthStackConfig
{
BaseUrl = "https://api.authstack.voostack.com",
TokenStorage = new SecureTokenStorage()
});

AuthStack supports JWKS for validating tokens without shared secrets. Use this in your backend:

services.AddAuthentication()
.AddJwtBearer(options =>
{
options.Authority = "https://api.authstack.voostack.com";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "https://api.authstack.voostack.com",
ValidateAudience = true,
ValidAudience = "authstack"
};
});
EndpointDescription
/.well-known/jwks.jsonJSON Web Key Set for token validation
/.well-known/openid-configurationOpenID Connect discovery document
try
{
var result = await client.LoginAsync(email, password);
}
catch (AuthStackException ex)
{
Console.WriteLine($"Error: {ex.Message}");
Console.WriteLine($"Status Code: {ex.StatusCode}");
Console.WriteLine($"Response: {ex.ResponseData}");
// Handle specific errors based on status code
if (ex.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
// Invalid credentials
}
}
MethodDescription
InitializeAsync()Initialize and restore session
LoginAsync()Login with email/password
RegisterAsync()Register new user
LoginWithProviderTokenAsync()Login with OAuth token (social login)
LoginWithProviderCodeAsync()Login with OAuth code (social login)
ExchangeCodeAsync()Exchange code when AuthStack is your IdP
ExchangeCodeViaBackendAsync()Exchange code via custom backend
RefreshTokenAsync()Refresh access token
FetchCurrentUserAsync()Refresh user data
GetLinkedProvidersAsync()Get linked OAuth providers
LinkProviderWithTokenAsync()Link OAuth provider with token
LinkProviderWithCodeAsync()Link OAuth provider with code
UnlinkProviderAsync()Unlink OAuth provider
GetProviderAuthUrlAsync()Get OAuth authorization URL
GetProviderCredentialsAsync()Get provider access token
LogoutAsync()Logout and clear tokens
ClearLocalAuthAsync()Clear local auth without server call
PropertyTypeDescription
StatusAuthStatusCurrent authentication status
CurrentUserUserInfo?Current authenticated user
TokensAuthTokens?Current auth tokens
IsAuthenticatedboolWhether user is authenticated
IsInitializedboolWhether client is initialized
AccessTokenstring?Current access token
EventDescription
StatusChangedFired when auth status changes
UserChangedFired when current user changes