sgcWebSockets .NET Authentication
Five components for proving who a caller is: OAuth2 on both sides, JWT on both sides, and a WebAuthn passkey server.
These are the components that actually authenticate. If you are looking for a sign-in form or a session cookie, that is your application's job; this skill is about the tokens.
When to use this skill
- Get an access token from an OAuth2 provider (Google, Microsoft, GitHub, Okta)
- Be the OAuth2 authorization server for your own clients
- Sign or verify a JWT
- Accept a passkey registration or assertion
Components in this skill
| Component | Side |
|---|---|
TsgcHTTP_OAuth2_Client |
asks a provider for tokens |
TsgcHTTP_OAuth2_Server |
issues them |
TsgcHTTP_JWT_Client |
signs and sends |
TsgcHTTP_JWT_Server |
verifies |
TsgcWSAPIServer_WebAuthn |
passkey registration and assertion |
Before you start, ask the developer
Use a structured question tool if your host has one, for example Claude Code's
AskUserQuestion. Otherwise ask in chat:
- Consuming a provider, or being one? Client and server are different components and almost entirely different work.
- Which grant type? Authorization code, client credentials, device code
and password are four different flows with four different redirect stories.
OAuth2Options.GrantTypepicks it, and it is not a detail to default. - Where does the token live afterwards? In memory, a cookie, a store. The component obtains it; keeping it is yours.
- Is there a redirect URI, and is it registered? The most common OAuth2 failure is a redirect URI that does not match the provider's registration exactly, down to the trailing slash.
The two option objects, which is where people go wrong
TsgcHTTP_OAuth2_Client has both OAuth2Options and
AuthorizationServerOptions, and they hold different things:
OAuth2Optionsholds who you are:ClientId,ClientSecret,GrantType, andUsername/Passwordfor the password grant.AuthorizationServerOptionsholds where the provider is:AuthURL,TokenURL,Scope,RevocationURL,IntrospectionURLandDeviceAuthorizationURL.
Putting the client id on AuthorizationServerOptions compiles and fails at
runtime with an error from the provider that does not name the real problem.
using esegece.sgcWebSockets;
var oauth = new TsgcHTTP_OAuth2_Client();
oauth.OAuth2Options.ClientId = Environment.GetEnvironmentVariable("OAUTH_ID");
oauth.OAuth2Options.ClientSecret = Environment.GetEnvironmentVariable("OAUTH_SECRET");
oauth.AuthorizationServerOptions.AuthURL = "https://provider/authorize";
oauth.AuthorizationServerOptions.TokenURL = "https://provider/token";
oauth.AuthorizationServerOptions.Scope = "openid profile";
oauth.OAuth2Options.GrantType = TsgcOAuth2GrantTypes.auth2CodePKCE;
oauth.LocalServerOptions.Port = 8080;
oauth.LocalServerOptions.RedirectURL = "http://localhost:8080/callback";
oauth.Start();
LocalServerOptions is the small local listener that receives the authorization
code redirect, which is what makes the code grant workable from a desktop
application. Its RedirectURL and the URI registered with the provider have to
be the same string.
The grant types are auth2Code, auth2CodePKCE, auth2ClientCredentials,
auth2ResourceOwnerPassword and auth2DeviceCode.
Beyond the flow itself: Refresh(refreshToken), Revoke(token, hint) and
Introspect(token, hint) are the token lifecycle methods, and the
OnAfterAccessToken / OnErrorAccessToken pair is where the result arrives.
DPoP
The client supports DPoP (RFC 9449) through DPoPOptions,
GenerateDPoPKeyPair(), GetDPoPProof(method, url, accessToken) and
GetDPoPJWKThumbprint(). Use it when the provider requires sender-constrained
tokens. It is not on by default and it is not needed for an ordinary bearer
flow.
Things that catch people out
- Secrets in source. A client secret in a repository is a secret that has to be rotated. Read it from configuration or the environment, always.
- A client secret in a desktop or mobile application is not secret at all,
whatever the provider's console calls it. Use
auth2CodePKCEfor a public client. - The redirect URI must match the registration character for character. Http against https, a port, a trailing slash: any of them fails, and the provider's error message rarely says which.
- Errors arrive on events, not as exceptions.
OnErrorAccessTokenand its siblings are where a rejected grant shows up, and code that only wires theOnAfter*events sees silence instead of a failure. - The JWT components sign and verify. They do not decide what a valid token is allowed to do; authorisation is still yours to write.
- WebAuthn needs a secure context in the browser, so localhost or HTTPS. It simply will not run otherwise, and that is a browser rule, not a component limitation.
Routing
- API detail:
reference/api/<Component>.mdhas the properties, events and methods, each with its C# signature. - Option / enum / delegate types: property and event types link to
reference/types/<TypeName>.md. - Examples:
examples/<Component>.mdis a trimmed snippet from the shipped demo;examples/index.mdmaps every component to its demo. - Getting started:
concepts/overview.mdcovers the singleusing, the target frameworks and the naming.
What is documented
Public instance properties, events and methods declared anywhere in the library's own class chain. Members inherited from the .NET base classes are left out, as are internals, so a page shows the surface a caller writes against and nothing else.
If a component you need is not here, read concepts/coverage.md in the
sgcwebsockets-dotnet skill before assuming a different name for it.
This assembly carries fewer components than the Delphi library, and saying
so is more useful than guessing an API that does not exist.