library
OIDCRP¶
active
OpenID Connect relying-party helper for Go web applications.
GitHub repository Package reference
import "github.com/kilo666mj/oidcrp"
A small OpenID Connect relying-party helper for Go web apps that authenticate humans through an OIDC provider (e.g. Pocket ID). It handles the browser-facing half of the flow and leaves session storage to the app.
Install and reference¶
go get github.com/kilo666mj/[email protected]
oidcrp requires Go 1.26.5 or newer. The compatibility lane also tests the
current Go release. API documentation is available on
pkg.go.dev.
The package owns:
- OIDC discovery.
- Authorization-code flow with PKCE.
- State and nonce cookies.
- ID token verification.
- Subject, email, and group allowlists.
- Browser auth middleware decisions.
Apps still own local sessions by implementing SessionManager.
Native shells can keep credentials out of an embedded webview by enabling a
desktop handoff. The shell generates an opaque one-time value and opens the
ordinary login URL in the system browser. After verifying the OIDC response,
oidcrp passes that value and the identity to DesktopSessionManager; the app
then exposes its own same-origin, single-use session exchange endpoint.
auth := oidcrp.New(oidcrp.Config{
Issuer: cfg.OIDC.Issuer,
ClientID: cfg.OIDC.ClientID,
ClientSecret: cfg.OIDC.ClientSecret,
RedirectURL: cfg.OIDC.RedirectURL,
Scopes: cfg.OIDC.Scopes,
AllowedEmails: cfg.OIDC.AllowedEmails,
AllowedGroups: cfg.OIDC.AllowedGroups,
StateCookieName: "myapp_oidc",
LoginPath: "/login",
SuccessPath: "/",
APIPrefixes: []string{"/api/"},
}, sessions)
auth.Register(mux)
mux.HandleFunc("POST /api/auth/logout", auth.Logout)
mux.HandleFunc("GET /", auth.Require(app.index))
For a desktop handoff, add these fields and implement the optional interface:
auth := oidcrp.New(oidcrp.Config{
// ordinary OIDC fields omitted
DesktopHandoffParam: "desktop",
DesktopSuccessPath: "/auth/desktop/complete",
ValidateDesktopHandoff: validOneTimeCode,
}, sessions)
func (s *sessions) IssueDesktop(w http.ResponseWriter, r *http.Request,
identity oidcrp.Identity, handoff string) error {
confirmation, err := oidcrp.NewDesktopConfirmation(handoff)
if err != nil {
return err
}
confirmationHash := sha256.Sum256([]byte(confirmation.BrowserSecret))
if err := s.storePendingIdentity(r.Context(), handoff, identity,
confirmationHash[:], confirmation.VerificationCode); err != nil {
return err
}
setSecureHTTPOnlyConfirmationCookie(w, confirmation.BrowserSecret)
return nil
}
The handoff is attacker-controlled input because anyone can construct a login
start URL containing one. It must be high entropy, expire quickly, and be
consumed atomically, but those properties alone are not sufficient. The app
must keep the handoff unexchangeable until the authenticated browser explicitly
approves it using the independent browser secret. Show
DesktopConfirmation.VerificationCode in the browser and derive the same code
from the handoff in the desktop app so the user can compare them. Offer a cancel
action, and never accept the handoff itself as the browser confirmation secret.
After approval, atomically rotate the handoff into a normal HttpOnly application session for the native webview. Without this confirmation step, a phishing link can bind a victim's verified identity to an attacker's handoff.
SessionManager is intentionally small:
type SessionManager interface {
Valid(r *http.Request) bool
Issue(w http.ResponseWriter, r *http.Request, identity oidcrp.Identity) error
Clear(w http.ResponseWriter, r *http.Request)
}
Adoption checklist¶
- Register an exact HTTPS callback URL with the identity provider.
- Implement
SessionManagerwith application-owned, HttpOnly sessions and explicit expiry and revocation. - Configure subject, email, or group allowlists when the provider is shared.
- Mount the standard routes, protect browser handlers with
Require, and keep API prefixes on the401path rather than browser redirects. - Preserve the request scheme and host through the reverse proxy; test state, nonce, PKCE, callback, logout, and provider-outage behavior.
- For native handoffs, make the opaque value high entropy, short-lived, and atomically single-use before exchanging it for an application session.
The package does not own account provisioning, local roles, session storage, reverse-proxy trust, or provider availability. Those remain application and deployment policy.
License¶
MIT — see LICENSE.