.NET SDK
Integrate Signward authentication into ASP.NET Core apps with the Signward.IdServer.Client NuGet package.
The official .NET SDK wraps the OIDC discovery flow, JWT validation, and token forwarding into a single AddIdServerAuth() extension. Works with ASP.NET Core 8, 9, and 10.
Install
Install via the .NET CLI:
dotnet add package Signward.IdServer.Client
Or PackageReference in your .csproj:
<PackageReference Include="Signward.IdServer.Client" Version="1.0.4" />
Current version: 1.0.4. Upgrade from 1.0.x is a drop-in package bump — no API changes, and the new options are all opt-in with defaults matching the previous behaviour.
Configure — Web app (cookie + OIDC)
For a server-rendered MVC or Razor Pages app that signs users in:
using IdServer.Client.Middleware;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddIdServerAuth(o =>
{
o.Authority = "https://api.signward.com";
o.ClientId = "YOUR_CLIENT_ID";
o.ClientSecret = "YOUR_CLIENT_SECRET";
o.Scopes = "openid profile email roles";
o.RemoteFailurePath = "/Home/LoginError"; // see "Handle an interrupted sign-in" below
});
var app = builder.Build();
app.UseIdServerAuth();
app.MapRazorPages();
app.Run();
The middleware wires up:
- A cookie authentication scheme (default)
- OpenID Connect challenge scheme (redirects to Signward on 401)
/Account/Loginand/Account/Logouthandler routes- Silent access-token refresh (see
RefreshThresholdbelow)
Handle an interrupted sign-in
A login can fail on the return leg for reasons that are entirely routine: the authorization code
was already redeemed, the correlation cookie expired, or the user re-opened the callback URL from
browser history. Without a handler ASP.NET Core turns these into an unhandled
AuthenticationFailureException — a 500 error page on what is really just "start over".
Set RemoteFailurePath (1.0.4+) to a page of your own:
o.RemoteFailurePath = "/Home/LoginError";
Two requirements for that page, both easy to get wrong:
- It must be reachable without authentication (
[AllowAnonymous]). If your app has a global fallback authorization policy, an authenticated-only error page gets challenged and the user bounces back into the login that just failed. - It must not start a new login automatically. Offer a manual "try again" link instead — otherwise a persistent cookie problem becomes a redirect loop.
The failure is logged (IdServer.Client.Auth) with the underlying exception, so the cause stays
visible in your own logs.
Sign out
Point your sign-out link at /Account/Logout. It does three things, and all three matter:
- Denies the current access token server-side, so a copy of it cannot be replayed before it expires
- Clears the local cookie
- Signs out of the OpenID Connect scheme — the Signward session itself ends, not just your app's
Without the third step the user clicks "sign out", then the next login walks straight back in
without being asked for credentials, because the identity provider still considers them signed in.
Where they land afterwards is PostLogoutRedirectUri (default /).
Keep the session alive
The access token is deliberately short-lived (15 minutes by default) so it can be revoked quickly.
To avoid signing users out that often, the SDK refreshes it silently: when a request arrives and
the token expires within RefreshThreshold (default 2 minutes), it exchanges the stored refresh
token in the background and re-stores the new pair in the cookie.
o.RefreshThreshold = TimeSpan.FromMinutes(2); // TimeSpan.Zero disables it
The session therefore lasts as long as the refresh token (7 days server-side), while the access
token stays short. Requires SaveTokens = true (the default) and cookie auth.
Configure — API (JWT bearer)
For a REST API that only validates incoming bearer tokens (no cookies, no redirect):
builder.Services.AddIdServerAuth(o =>
{
o.Authority = "https://api.signward.com";
o.Audience = "your-api-audience";
o.UseCookieAuth = false;
});
JWT validation is automatic: issuer, audience, signature (JWKS), and expiration are all enforced.
Protect an endpoint
Use the standard [Authorize] attribute, or the SDK-provided role-aware shortcut:
[ApiController]
[Route("api/reports")]
public class ReportsController : ControllerBase
{
[HttpGet]
[IdServerAuthorize("admin", "owner")]
public IActionResult List() => Ok(new { reports = new[] { "Q1", "Q2" } });
}
IdServerAuthorize accepts both built-in roles (admin, owner, user) and custom roles defined per-tenant in the Portal.
Read the current user
The SDK exposes IdServerUser extensions on ClaimsPrincipal:
app.MapGet("/me", (ClaimsPrincipal user) => new
{
userId = user.GetUserId(),
email = user.GetEmail(),
tenantId = user.GetTenantId(),
isAdmin = user.HasRole("admin")
});
Forward tokens to downstream APIs
Inject IdServerTokenHandler into any named HttpClient and the user's bearer token is forwarded automatically:
builder.Services.AddHttpClient("reports-api", c => c.BaseAddress = new Uri("https://reports.myapp.com"))
.AddHttpMessageHandler<IdServerTokenHandler>();
Next steps
- Protect a Web API — end-to-end tutorial
- Python SDK — FastAPI and Flask integration
- JavaScript / TypeScript SDK — Node.js, Express, and the browser
- PHP / Laravel SDK — Laravel middleware + any PHP app