Notice: you're looking at a read-only mirror of our Dokuwiki.

login

User QR Encoding

This document formally defines a QR Code format that encodes the minimum identifiable user information for use by other services.

Use Cases

Requirements

Designs

QR Code

The QR Code encodes a string encoded in Alphanumeric mode. The string must implement this syntax:

QR_STRING := 'HTTPS://DMA.SPACE/QR/' CLAIMS_PART '.' SIGNATURE_PART

Note: All characters MUST be upper-cased for Alphanumeric mode.

Base32 Encoding

The QR string makes extensive use of standard Base32 encoding with NO padding, as defined in RFC 4648.

Claims Part

CLAIMS_PART := USER_ID ':' USERNAME_BASE32 ':' USER_ROLE ':' ISSUED_DATE
USER_ID     := /[0-9]+/
USER_ROLE   := 'ADMIN' | 'MEMBER' | '_'
ISSUED_DATE := YEAR '-' MONTH '-' DATE

The claims part is described in the syntax above, with the following notes:

As an example, this claims string:

10:MRUWC3LPNZSA:ADMIN:2026-01-01

Means the following:

Signature Type and Part

SIGNATURE_PART    := SIGNATURE_ED25519 | ...
SIGNATURE_ED25519 := 'ED25519:' BASE32_DATA

The signature type denotes the type of the signature string that follows after it and the colon (:) character.

The signature part is the signature of the entire Claims Part string, signed using whatever the signature type denotes, and encoded using the predefined Base32 encoding.

The supported signature types are:

An example signature part of the above claims part string example could be:

ED25519:7CSS7U7C2BJM3Z3MXYENYNSBUWZRS3BGT4YWX4DXTMDBOWUABFBT4REZSKJ4FCVTFXCFY6A2WNOUIMIR3HHGLQT5CNA5ZABNOBPBMBY

Implementation and Example

The following function implements the algorithm in Go using a fake random source to generate the Ed25519 key (INSECURE) [aasadasds][asdasdasd]:

func encodeEd25519(priv ed25519.PrivateKey, uid int, uname string, urole string, issuedAt time.Time) []byte {
	enc := base32.StdEncoding.WithPadding(base32.NoPadding)
	plx := fmt.Sprintf(
		"%d:%s:%s:%s",
		uid,
		enc.EncodeToString([]byte(uname)),
		urole,
		issuedAt.Format("2006-01-02"),
	)
	sig := ed25519.Sign(priv, []byte(plx))
	return fmt.Appendf(nil, "HTTPS://DMA.SPACE/QR/%s.ED25519:%s", plx, enc.EncodeToString(sig))
}

The example outputs above was generated with:

issuedAt := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
constRand := rand.NewChaCha8([32]byte{})
_, priv, _ := ed25519.GenerateKey(constRand)

encodeEd25519(priv, 10, "diamond", roleAdmin, issuedAt)

This returns the following string:

HTTPS://DMA.SPACE/QR/10:MRUWC3LPNZSA:ADMIN:2026-01-01.ED25519:7CSS7U7C2BJM3Z3MXYENYNSBUWZRS3BGT4YWX4DXTMDBOWUABFBT4REZSKJ4FCVTFXCFY6A2WNOUIMIR3HHGLQT5CNA5ZABNOBPBMBY

Frontend Server Design

The frontend server must be able to handle /QR paths and perform a redirection to the Monolith API server's GET /api/qr endpoint. The query string must be kept as-is.

Verification Server Design

This section is not finalized.

Proposed endpoints:

$ curl -X GET "HTTPS://DMA.SPACE/QR/10:MRUWC3LPNZSA:ADMIN:2026-01-01.ED25519:7CSS7U7C2BJM3Z3MXYENYNSBUWZRS3BGT4YWX4DXTMDBOWUABFBT4REZSKJ4FCVTFXCFY6A2WNOUIMIR3HHGLQT5CNA5ZABNOBPBMBY/verify"
{
  "valid": true
}

$ curl -X GET "HTTPS://DMA.SPACE/QR/10:MRUWC3LPNZSA:ADMIN:2026-01-01.ED25519:7CSS7U7C2BJM3Z3MXYENYNSBUWZRS3BGT4YWX4DXTMDBOWUABFBT4REZSKJ4FCVTFXCFY6A2WNOUIMIR3HHGLQT5CNA5ZABNOBPBMBY/claims"
{
  "valid": true,
  "claims": {
    "sub": 10,
    "username": "diamond",
    "preferred_name": "diamond",
    "email": "diamond@dma.space",
    "groups": ["dma-users", "dma-members", "dma-admins"]
  }
}

The Monolith should implement a GET /api/qr.

Security

This design does prevent fake QR codes from being created, so imitating another person is not trivial.

However, the QR code can still be copied or stolen from others, so the whole system is not perfect and should not be used to gate access to critical things.