webauthnext

package module
v1.2.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 31, 2026 License: MIT Imports: 22 Imported by: 0

README

webauthnext

webauthnext is a combined WebAuthn (Passkeys) and OpenID Connect (OIDC) Identity Provider package for Go. Built on top of guikit and ultimate_db, it provides an all-in-one authentication server capable of registering and authenticating users with passkeys while serving as an OIDC provider for external clients.

Features

  • Passkey Native: Full implementation of WebAuthn registration and login flows using [github.com/go-webauthn/webauthn](https://github.com/go-webauthn/webauthn).
  • OIDC Identity Provider: Supports standard OpenID Connect discovery (.well-known/openid-configuration), JWKS endpoint keys, authorization code grant flow, and ID token production.
  • Self-Contained Frontend: Automatically serves required WebAuthn browser helper utilities (/auth/webauthn.js) for buffer conversion and credential requests.
  • Built-in Persistence: Seamlessly handles state (users, active sessions, OIDC flow contexts, and auth codes) utilizing transactional features via ultimate_db.

Architecture Overview

The authentication manager hooks directly into the guikit.GUIKit multiplexer and orchestrates communication between the web client, the storage engine, and external relying parties.

       [ OIDC Client ] <--- OIDC Discovery / Token Exchange ---> [ webauthnext.Manager ]
                                                                        |
[ Browser Client ] <--- WebAuthn Sign / Verify Handshake ---> [ HTTP Mux / Handlers ]
                                                                        |
                                                                  [ ultimate_db ]


Core Endpoints

The package exposes the following routes automatically upon initialization:

WebAuthn (Passkeys)
  • GET /auth/register/begin — Generates credential creation options for registration.
  • POST /auth/register/finish — Validates client attestation and saves new passkey.
  • GET /auth/login/begin — Generates credential assertion options for signing.
  • POST /auth/login/finish — Validates signed assertion data to complete login.
  • GET /auth/webauthn.js — Client-side JavaScript handling passkey handshakes.
OpenID Connect
  • GET /.well-known/openid-configuration — Discovery endpoint mapping out capabilities.
  • GET /auth/keys — Serves the JSON Web Key Set (JWKS) to verify generated ID Tokens.
  • GET /auth/authorize — Handles client authorization requests, validates domains, and kicks off login/MFA verification.
  • POST /auth/token — Exchanges authorization codes for cryptographically signed RS256 ID tokens.

Database Key Layout

All internal state is managed under ultimate_db.PageID = 1 (AuthPageID). Data keys utilize the following prefixes:

Key Prefix Data Structure Purpose / Lifespan
user:{username} PasskeyUser (JSON) Persistent user profile and registered credentials
banned:{username} Primitive array/flag Ban state listing blocked handles or devices
session:reg_{username} webauthn.SessionData In-flight registration challenge context (5-min TTL)
session:login_{username} webauthn.SessionData In-flight login challenge context (5-min TTL)
oidc_flow:{flow_id} AuthRequest (JSON) Active client authorization request details (10-min TTL)
auth_code:{code} Context Map (JSON) Issued OIDC auth code awaiting token exchange (5-min TTL)
mfa_verified_{username} "true" / "false" string MFA step completion marker

Installation

Ensure your Go module environment is configured with access to your custom dependencies:

go get github.com/gorrila/websocket
go get github.com/gddisney/ultimate_db
go get github.com/gddisney/guikit
go get github.com/go-webauthn/webauthn
go get github.com/golang-jwt/jwt/v5
go get gopkg.in/square/go-jose.v2


Usage Example

Initialize the webauthnext manager inside your core application startup where guikit is configured:

package main

import (
    "log"
    "net/http"

    "github.com/gddisney/guikit"
    "github.com/gddisney/webauthnext"
)

func main() {
    // 1. Initialize your GUIKit structure
    gk := guikit.NewEngine(/* config flags */)

    // 2. Instantiate the Auth Manager
    authManager, err := webauthnext.New(
        gk,
        "My Enterprise App",      // Relying Party Display Name
        "auth.example.com",       // Relying Party ID (Domain)
        "https://auth.example.com",// Relying Party Origin
    )
    if err != nil {
        log.Fatalf("Failed to initialize auth manager: %v", err)
    }

    // 3. (Optional) Custom hooks for successful login behaviors
    authManager.OnLoginSuccess = func(username string, w http.ResponseWriter, r *http.Request) {
        log.Printf("User %s successfully logged in via passkey!", username)
    }

    // 4. Start serving your application
    log.Fatal(http.ListenAndServe(":8080", gk.Mux))
}

Front-End Implementation

To invoke passkey registration or login within your front-end templates, include the embedded script and use the exposed async functions:

<!-- Load the client side bridge generated by the manager -->
<script src="/auth/webauthn.js"></script>

<script>
    // To register a new user or device passkey
    async function handleRegister() {
        const username = document.getElementById('usernameField').value;
        await passkeyRegister(username);
    }

    // To sign in using an existing passkey
    async function handleLogin() {
        const username = document.getElementById('usernameField').value;
        await passkeyLogin(username);
    }
</script>


Documentation

Index

Constants

View Source
const AuthPageID ultimate_db.PageID = 1

Variables

This section is empty.

Functions

This section is empty.

Types

type ActiveSession

type ActiveSession struct {
	Username   string `json:"username"`
	DBSCPubKey string `json:"dbsc_pub_key,omitempty"`
}

type AuthRequest

type AuthRequest struct {
	ClientID            string `json:"client_id"`
	RedirectURI         string `json:"redirect_uri"`
	State               string `json:"state"`
	Nonce               string `json:"nonce"`
	Scope               string `json:"scope"`
	CodeChallenge       string `json:"code_challenge"`
	CodeChallengeMethod string `json:"code_challenge_method"`
}

type OIDCClient

type OIDCClient struct {
	ClientID     string   `json:"client_id"`
	ClientSecret string   `json:"client_secret"`
	ClientName   string   `json:"client_name"`
	RedirectURIs []string `json:"redirect_uris"`
}

type PasskeyUser

type PasskeyUser struct {
	ID          []byte                `json:"id"`
	Name        string                `json:"name"`
	DisplayName string                `json:"displayName"`
	Credentials []webauthn.Credential `json:"credentials"`
}

func (*PasskeyUser) WebAuthnCredentials

func (u *PasskeyUser) WebAuthnCredentials() []webauthn.Credential

func (*PasskeyUser) WebAuthnDisplayName

func (u *PasskeyUser) WebAuthnDisplayName() string

func (*PasskeyUser) WebAuthnID

func (u *PasskeyUser) WebAuthnID() []byte

func (*PasskeyUser) WebAuthnIcon

func (u *PasskeyUser) WebAuthnIcon() string

func (*PasskeyUser) WebAuthnName

func (u *PasskeyUser) WebAuthnName() string

type Provider

type Provider struct {
	SessionManager *secure_policy.SessionManager

	OnLoginSuccess func(username string, w http.ResponseWriter, r *http.Request)
	// contains filtered or unexported fields
}

func New

func New(gk *guikit.GUIKit, sm *secure_policy.SessionManager, rpDisplayName, rpID, rpOrigin string) (*Provider, error)

func (*Provider) AuthGuard

func (p *Provider) AuthGuard(next http.HandlerFunc) http.HandlerFunc

func (*Provider) Authorize

func (p *Provider) Authorize(w http.ResponseWriter, r *http.Request)

func (*Provider) BeginLogin

func (p *Provider) BeginLogin(w http.ResponseWriter, r *http.Request)

func (*Provider) BeginRegistration

func (p *Provider) BeginRegistration(w http.ResponseWriter, r *http.Request)

func (*Provider) DBSCRefresh

func (p *Provider) DBSCRefresh(w http.ResponseWriter, r *http.Request)

func (*Provider) DBSCRegister

func (p *Provider) DBSCRegister(w http.ResponseWriter, r *http.Request)

func (*Provider) FinishLogin

func (p *Provider) FinishLogin(w http.ResponseWriter, r *http.Request)

func (*Provider) FinishRegistration

func (p *Provider) FinishRegistration(w http.ResponseWriter, r *http.Request)

func (*Provider) RegisterClient

func (p *Provider) RegisterClient(w http.ResponseWriter, r *http.Request)

func (*Provider) RegisterServiceIdentity

func (p *Provider) RegisterServiceIdentity(name string, tpmPublicBytes []byte) error

func (*Provider) RevokeToken

func (p *Provider) RevokeToken(w http.ResponseWriter, r *http.Request)

func (*Provider) ServeDiscovery

func (p *Provider) ServeDiscovery(w http.ResponseWriter, r *http.Request)

func (*Provider) ServeJS

func (p *Provider) ServeJS(w http.ResponseWriter, r *http.Request)

func (*Provider) ServeJWKS

func (p *Provider) ServeJWKS(w http.ResponseWriter, r *http.Request)

func (*Provider) SignPayload

func (p *Provider) SignPayload(payload []byte) []byte

func (*Provider) TokenExchange

func (p *Provider) TokenExchange(w http.ResponseWriter, r *http.Request)

func (*Provider) VerifyAddressClaim

func (p *Provider) VerifyAddressClaim(remoteID []byte, address string, dbscProof []byte) (bool, error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL