Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

go-dn42-auth

Go library for proving that an application subject controls a DN42 ASN.

The library is inspired by the login flows in github.com/AS214933/dn42-bot, but it is a standalone Go package rather than a Telegram bot component.

Features

  • Email OTP login against email addresses discovered from DN42 registry contact objects.
  • GPG clearsigned challenge verification against pgpkey-* and pgp-fingerprint * maintainer auth entries.
  • SSH signature verification for OpenSSH ssh-keygen -Y sign signatures against ssh-* maintainer auth entries.
  • Optional OIDC/OAuth helper for providers that expose a DN42 ASN claim such as dn42.asn.
  • Pluggable registry, mailer, challenge store, clock, randomness, and GPG key providers.

Layout

  • auth: ASN parsing, challenge orchestration, email OTP, GPG/SSH signatures, and OIDC/OAuth binding.
  • registry: registry object parsing plus file, git-backed, whois, and composite data sources.

Install

Requires Go 1.25 or newer.

go get github.com/AS214933/go-dn42-auth
import (
    "github.com/AS214933/go-dn42-auth/auth"
    "github.com/AS214933/go-dn42-auth/registry"
)

Registry Source

Use the default git-backed DN42 registry source. It clones https://git.origami.pub/Bingxin/dn42-registry on first use:

reg := registry.NewGit("/var/cache/dn42-registry")
verifier := auth.New(reg)

Override the registry Git URL when you need a mirror or private fork:

reg := registry.NewGit(
    "/var/cache/dn42-registry",
    registry.WithURL("https://git.example.net/dn42-registry"),
)
verifier := auth.New(reg)

If you manage the clone yourself, use a local DN42 registry directory:

reg := registry.NewFile("/srv/dn42-registry")
verifier := auth.New(reg)

NewGit reuses an existing clone by default. Add registry.WithAutoPull(true) if lookups should pull updates first.

Or combine a local registry with a whois fallback:

reg := registry.Composite{
    registry.NewFile("/srv/dn42-registry"),
    &registry.Whois{Address: "whois.dn42"},
}
verifier := auth.New(reg)

Email OTP

verifier := auth.New(reg, auth.WithMailer(auth.MailerFunc(
    func(ctx context.Context, msg auth.OTPMessage) error {
        // Send msg.Code to msg.Email with your SMTP or mail provider.
        return nil
    },
)))

subject := auth.Subject{ID: "user:123"}
challenge, err := verifier.BeginEmailOTP(ctx, subject, auth.ASN(4242421234), "noc@example.net")
if err != nil {
    // The email must be listed on the ASN's registry contacts.
}

proof, err := verifier.VerifyEmailOTP(ctx, challenge.ID, userSuppliedCode)

Signature Challenge

challenge, err := verifier.BeginSignature(ctx, subject, auth.ASN(4242421234), auth.SignatureAny)
if err != nil {
    // The ASN must have GPG or SSH auth entries in its mntner object.
}

fmt.Println(challenge.Text)

For SSH:

printf '%s' "$CHALLENGE_TEXT" > challenge.txt
ssh-keygen -Y sign -f ~/.ssh/id_ed25519 -n file challenge.txt

For GPG:

printf '%s' "$CHALLENGE_TEXT" | gpg --clearsign

Then verify the returned signed text:

proof, err := verifier.VerifySignature(ctx, challenge.ID, []byte(signedText))

GPG public keys are loaded from DN42 key-cert objects first and then from HTTPS keyservers by default. Override this with auth.WithGPGKeyProvider when your application wants to pin keys or avoid network lookups.

OIDC/OAuth ASN Helper

For providers that expose a DN42 ASN claim:

oidcVerifier, err := auth.NewOIDCVerifier(ctx, auth.OIDCConfig{
    IssuerURL:      "https://auth.example.net",
    ClientID:       "client-id",
    ClientSecret:   "client-secret",
    RedirectURL:    "https://app.example.net/callback",
    Scopes:         []string{"dn42"},
    ASNClaim:       "dn42.asn",
    ASNClaimSource: auth.OIDCClaimAuto,
})

url := oidcVerifier.AuthCodeURL(state, oauth2.SetAuthURLParam("nonce", nonce))

After the callback:

result, err := oidcVerifier.ExchangeAndVerify(ctx, code, nonce)
proof, err := auth.VerifyOAuthASN(subject, auth.ASN(4242421234), result, nil)

Your application remains responsible for state and nonce storage.

Security Notes

  • Store challenge IDs server-side and expire them quickly. The default in-memory store uses a 10 minute challenge TTL.
  • Custom auth.ChallengeStore implementations must make Update atomic per challenge ID so OTP attempt limits and one-time challenge consumption remain race-safe.
  • Bind Subject.ID to your own account/session identifier, not to display text.
  • Run a fresh DN42 registry clone or trusted whois source.
  • Prefer HTTPS key lookup or pinned GPG keys when registry key-cert objects are absent.
  • OIDC/OAuth verification is only as strong as the provider and claim mapping you configure.

Test

go test ./...

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages