@e280/authlocal v0.2.0-26

š Authlocal.org Developer Readme
Not a developer? See the User Guide instead
šļø Federated ā your app gets user logins from authlocal.org popup
š Elliptic ā identities are @noble/ed25519 keypairs
š± Clientside ā statically deployed, no api servers
š Protocol ā permissionless integration, do it your way
š„§ Easy as pie ā setup your app with two easy snippets
š MIT Licensed ā totally free and open source
A free login system for the world wide web
- Try out the example integration live demo here: https://authlocal.org/app/
- Your users click "Login", pick an identity, then boom! ā they're logged into your site.
- Next, you can use claim tokens, for your server to verify user requests.
Authlocal installation and setup
Quick start
- Pick one of these javascript install techniques
- HTML install technique
Put this script into your html<head>:<script type="module"> import {install} from "https://authlocal.org/install.bundle.min.js" const auth = await install() auth.on(login => console.log("auth", login)) </script> - Webdev install technique
Install the package into your project:
Write this in your app's js entrypoint (likenpm install @e280/authlocalmain.ts):import {install} from "@e280/authlocal" const auth = await install() auth.on(login => console.log("auth", login))
- HTML install technique
- Put this stylesheet into your html
<head>:<link rel="stylesheet" href="https://authlocal.org/themes/basic.css"/> - Place these new auth elements anywhere in your html
<body>:<auth-user></auth-user> <auth-button></auth-button> - Take it for a spin! You can now login, and logout.
Alternative setup: headless install without UI
Use this technique if you want to make your own UI, and don't want to load any of the authlocal elements.
Create the Auth object manually, and trigger the initial load from storage:
import {Auth} from "@e280/authlocal" const auth = new Auth() await auth.loadLogin() auth.on(login => console.log("auth", login))You can trigger the authlocal popup, prompting the user to login, like this:
await auth.popup()- but remember, the call must originate from a user action like clicking a button, otherwise the browser's popup blocker will ignore it.
Understanding logins
You must never send the login data anywhere, it stays on the user's device.
The login is automatically persisted in localStorage for 7 days.
The anatomy of a login:
login.sessionIdā id of the login session hex stringlogin.nametag.idā user identity public key hex stringlogin.nametag.labelā user's chosen nicknamelogin.expiresAtā js timestamp of when this login expireslogin.isExpired()ā returns true if the login is expired nowlogin.signClaim(options)ā sign a claim (more on this later)
Authlocal's custom html elements
auth-button


<auth-button></auth-button>It's a "Login" button, that when clicked, spawns an Authlocal popup.
When the user is logged in, the button changes to a "Logout" button, which when clicked, will log the user out.
In javascript you can listen for logins/logouts via the button like this:
const authButton = document.querySelector("auth-button")
authButton.on(login => console.log("auth", login))
// š
// listen to *this* specific button
authButton.auth.on(login => console.log("auth", login))
// š
// listen to *any* buttonauth-user

<auth-user></auth-user>Displays the user's own logged-in identity. Shows nothing when logged-out.
auth-sigil

<auth-sigil hex="c9185ef07bc9f24ca856f2a178e59f85d0d53d22a14b26b434b58a22c9a872fb"></auth-sigil>Take a hex-encoded id, and present to the user as a sigil.
If the user hovers over the sigil, the full thumbprint is shown in the tooltip.
If the user clicks the sigil, the full thumbprint is copied to the clipboard.
Logins are really about signing claims
Okay, so you have a user's login. Now what?
- The purpose of a login is to sign claims on the user's behalf.
- You can then send claims to your server, and verify them.
The purpose of a claim is to say "on behalf of this user, my frontend says..."
- "...they want to post this message"
- "...they want to change their avatar"
- "...they want to buy this microtransaction"
- Stuff like that.
Claims are secured cryptographically
- Claims contain cryptographic proof that they stem from a user's login.
- No attacker can forge a claim for somebody else's identity.
Sign a claim
import {Time} from "@e280/authlocal"
const claimToken = await login.signClaim({
// any json-friendly data you want
claim: {message: "i love ice cream"},
// when should this claim expire?
expiresAt: Time.future.hours(24),
})Verify a claim
import {verifyClaim} from "@e280/authlocal/core"
const {claim, proof} = await verifyClaim({
claimToken,
appOrigins: ["https://e280.org"],
// |
// your website origin goes here
})
proof.sessionId
// id for this login session, looks like:
// "ff730fe2d725aa5210152975212d1068d7fe28ae22b5e62337a4cde42215187a"
proof.nametag.id
// user identity id, looks like:
// "a08263e70a0a48a07e988a7c0931ada6b0a38fa84bf367087b810c614a4c2070"
proof.nametag.label
// user identity nickname, looks like:
// "Michael Scott"- You can verify claims on the clientside or serverside.
- You must specify what
appOriginsyou expect to receive claims from (this prevents phishing attacks).
How are claims secure?
Authlocal gives your app a login session keypair, and a proof token signed by the user's identity keypair.
Your frontend can then create a claim ā like "user orders a large pizza" ā signed by the session keypair.
Your server verifies the proof, then the claim ā proving the user authorized the session, and the session authorized the claim.
Authlocal glossary
- Authority ā the website that provides login sessions (authlocal.org)
authorityOriginis the provider's origin, eghttps://authlocal.org
- App ā the third party website receiving login sessions (your website)
appOriginis your app origin, eghttps://e280.org
- Keypair ā an ed25519 keypair
.idis the public key (64 character hex string).secretis the private key (64 character hex string)
- Identity ā a keypair with a label string
- Seed ā text snippet or
.seedfile that stores an identity - Nametag ā the public data associated with a user's identity
.idis the public key (64 character hex string).labelis a nickname (max 32 character string)
- Thumbprint ā easier-to-read version of an id
thumbprint=>dozmut.winpex.linner.forsep.KgisJ8Pdgey1HC4o8cG59NaLYSoRTiHfAsigil(first two words) =>dozmut.winpexpreview(first four words) =>dozmut.winpex.linner.forsepbulk(second part) =>KgisJ8Pdgey1HC4o8cG59NaLYSoRTiHfA
- Login ā a login session
- is private, should never leave the user's device
.nametagcontains the identity's id and label.expiresAtjs time of the moment this login expires.isExpired()returns true if the login is now expired.signClaim(options)sign a claim
- Proof ā provenance for login
- is a token signed by an identity
- is public, can be shared around
.nametagcontains the identity's id and label.sessionIdproves a login session is blessed by an identity
- Claim ā arbitrary claim your frontend can make
- is a token signed by the login session, verifiable on your server
- is public, can be shared around
- includes the proof token (thus nametag and sessionId)
- includes any arbitrary claim data you want
- verified by
verifyClaim(options)
Common code snippets
š Notice:
Authlocal uses a number of utilities from our library@e280/stz,
To interact with these, you should import them from stz manually.
Thumbprint conversions
import {Thumbprint} from "@e280/stz"- id to thumbprint
Thumbprint.fromHex("005636bab2c73223ccf56f8112432212f57f01ef61452762cd142acd61ed44ed") // "dozmut.winpex.linner.forsep.KgisJ8Pdgey1HC4o8cG59NaLYSoRTiHfA" - id to sigil
Thumbprint.sigil.fromHex("005636bab2c73223ccf56f8112432212f57f01ef61452762cd142acd61ed44ed") // "dozmut.winpex" - thumbprint to id
Thumbprint.toHex("dozmut.winpex.linner.forsep.KgisJ8Pdgey1HC4o8cG59NaLYSoRTiHfA") // "005636bab2c73223ccf56f8112432212f57f01ef61452762cd142acd61ed44ed"
Authlocal seed text format
- Seeds can be copy-pasted, or live in a
my-identity.seedfile. Seed text looks like this:"mopfed.nimrut" linler.torhul.datmyn.binwep dilmyr.lagruc.nopwyl.witfur fasnes.sonpes.fostyr.foddur datter.diswyl.dotfer.wannul nomsum - There can be any number of seeds in the text
- Whitespace and funky characters are ignored. this is a valid seed:
""linlertorhuldatmynbinwepdilmyrlagrucnopwylwitfurfasnessonpesfostyrfoddurdatterdiswyldotferwannulnomsum - Seeds have three parts:
- the label is a handy nickname that the user assigned, expressed in json format.
"mopfed.nimrut" - the 256-bit ed25519 private key, expressed as 16 bytename words.
linler.torhul.datmyn.binwep dilmyr.lagruc.nopwyl.witfur fasnes.sonpes.fostyr.foddur datter.diswyl.dotfer.wannul - the 2-byte sha256 checksum ensures the private key wasn't mistyped.
nomsum
- the label is a handy nickname that the user assigned, expressed in json format.
Strategies for user-sovereign auth
Users will lose their identities, so have a recovery plan
When you sell a service or digital goods to a user's Authlocal identity, you should have a recovery mechanism, or people will get cranky.
š Authlocal is free and open source
- Authlocal is an https://e280.org/ project.
- Open github issues or discussions if you have any questions.
- Like the project? Star it on github, it's the only way we're paid.