Wires go-security’s OAuth2/OIDC login client into a plain net/http server: a Google registration (OIDC), a Zalo registration (plain OAuth2, VNG ecosystem), and a local “dev” registration pointed at an in-process fake provider so the whole flow runs end-to-end with no real credentials.
Then visit http://localhost:8080/ and click the “dev” login link.
The Google and Zalo ClientRegistrations use placeholder client ID/secret strings ("GOOGLE_CLIENT_ID", "ZALO_APP_ID", …) — literal values, not environment variables. Swap in real credentials from each provider’s console before those two links will work; only the “dev” link is wired to a functioning provider in this example.

Registrations

Handlers

There is no authn.UserService, no securityhttp.New/NewHandler, and no WithLogin option in this example — the OAuth2 callback handler writes the session principal directly once the code exchange and userinfo fetch succeed.

CSRF note

The callback route (/login/oauth2/code/{id}) is intentionally CSRF-exempt: the single-use, session-bound, constant-time state value is the CSRF defense for the authorization-code flow. If you mount web/csrf in your own app, exclude /login/oauth2/code/* from it.

The fake “dev” provider

newFakeProvider starts an httptest.Server implementing just enough of a non-OIDC OAuth2 provider for the “dev” registration to complete a real PKCE login with no external IdP:
It performs no real authentication or PKCE verification — it exists purely so the example is runnable end-to-end.

Flow diagram