@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/authlocal
main.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* button
auth-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
appOrigins
you 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)
authorityOrigin
is the provider's origin, eghttps://authlocal.org
- App ā the third party website receiving login sessions (your website)
appOrigin
is your app origin, eghttps://e280.org
- Keypair ā an ed25519 keypair
.id
is the public key (64 character hex string).secret
is the private key (64 character hex string)
- Identity ā a keypair with a label string
- Seed ā text snippet or
.seed
file that stores an identity - Nametag ā the public data associated with a user's identity
.id
is the public key (64 character hex string).label
is a nickname (max 32 character string)
- Thumbprint ā easier-to-read version of an id
thumbprint
=>dozmut.winpex.linner.forsep.KgisJ8Pdgey1HC4o8cG59NaLYSoRTiHfA
sigil
(first two words) =>dozmut.winpex
preview
(first four words) =>dozmut.winpex.linner.forsep
bulk
(second part) =>KgisJ8Pdgey1HC4o8cG59NaLYSoRTiHfA
- Login ā a login session
- is private, should never leave the user's device
.nametag
contains the identity's id and label.expiresAt
js 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
.nametag
contains the identity's id and label.sessionId
proves 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.seed
file. 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.