Use case · Authentication
Sign in with Almena
An OAuth-style flow that lets any application authenticate users with Almena. The user scans a QR with the Almena Wallet, picks a public node, and approves on their phone. No passwords, no central identity provider, and the application never sees the user's keys.
What it solves
Today, most apps either build their own password system — with all the breach risk that implies — or delegate login to a single corporate identity provider. Both options give one party full control over millions of accounts. With Almena, the user holds their own DID and credentials in the Wallet; the application only receives a verifiable proof of authentication signed by the user, not their secrets.
What the user sees
-
1. The integrated app
An application — web, desktop or mobile — embeds a 'Sign in with Almena' button next to its other login options.
-
2. QR with public-node selector
Pressing the button opens a screen with a QR code and a selector listing public Almena nodes. The user can change the node; the QR is regenerated for the chosen node.
-
3. Scan from the Wallet
On the phone, the user scans the QR with the Almena Wallet. The wallet shows which application is requesting access and what it will know about them.
-
4. Biometric approval
The user approves with Face ID, Touch ID or device PIN. The wallet signs the authentication challenge with the user's DID key (post-quantum, ML-DSA-87). If the user taps Reject instead, the wallet sends a signed cancellation back to the node and the integrated app immediately shows a 'Login rejected' message — no waiting for the QR to expire.
-
5. Logged in
The chosen public node resolves the user's DID document from the Almena mesh, verifies the signature against the public key declared in that document, and the integrated app — which has been polling for the result — receives the user's DID and mints its own session token. The login screen turns into the app's authenticated home.
Flow diagram
Happy path between the integrated application, the user's wallet and the chosen public Almena node. If the user rejects on the wallet, the node flips the challenge to 'rejected' and the app's next poll surfaces it — same path, terminal at step 9.
Elements diagram
The pieces involved and how they relate. Public nodes are interchangeable: the wallet talks to the one the user picked in the QR screen.
- Integrated app Embeds the login button. Displays the QR and node selector. Receives the authentication result.
- QR + node selector Encodes the session, the challenge and the chosen public node URL.
- Almena Wallet Holds the user's DID and private key. Scans the QR, asks for biometrics, signs the challenge.
- Public node Mediates the exchange. Verifies the signature against the DID document.
- Almena network P2P mesh of nodes that keep DID documents in sync. Any node returns the same document.
Security notes
- The application never receives the user's private key — only a verifiable signature on the challenge.
- Signatures use post-quantum primitives (ML-DSA-87, NIST FIPS 204) and the verifying key is read from the DID document published on the Almena mesh — not just the DID literal. Even a malicious node cannot substitute a different signing key: the daemon cross-checks the document key against the multibase-encoded key inside the DID identifier and refuses any mismatch.
- The user picks which public node mediates the exchange. The node never holds long-term secrets: every authentication is cryptographically bound to the user's DID, not to the node.
- The challenge is single-use and short-lived (60 s default, 300 s ceiling). Replays are rejected by the node and the wallet; the second proof for the same id returns 409 Conflict.
- If the user has not yet published their DID document, the node refuses the proof with a clear error rather than silently trusting the DID literal — login requires a published, network-resolvable identity.
- Reject on the wallet is also authenticated: the wallet sends back the QR's nonce so the node can confirm the rejecter actually scanned the QR before flipping the challenge state. The integrator's UI surfaces the rejection on its next poll without waiting for the timeout.