Online-kasutajad muutuvad üha vastupidavam tavapärastele e-posti / parooli registreerimisprotsessidele. Ühe klõpsuga sotsiaalse sisselogimise funktsioon Facebooki, Google'i või GitHubi kaudu osutub palju soovitavamaks alternatiiviks. Sellega kaasneb aga kompromiss.
Sotsiaalse meedia sisselogimise integratsiooni plussid:
Sotsiaalse meedia sisselogimise integreerimise miinused:
Selles artiklis tutvustatakse uut sisselogimismeetodit plokiahela arendamine : Ühe klõpsuga krüptograafiliselt turvaline sisselogimisvoog, kasutades MetaMask laiendus , kusjuures kõik andmed on salvestatud meie endi tagaküljele. Nimetame seda: 'Logi sisse MetaMaskiga'.
Tuhat sõna väärt pilt on siin sisselogimisvoo demo, mille me ehitame:
Näeb hea välja? Alustame!
Põhiidee on see, et konto omandiõigust on krüptograafiliselt lihtne tõendada, allkirjastades osa privaatset võtit kasutades. Kui teil õnnestub allkirjastada täpne andmefail, mille on genereerinud meie tagumine ots, loeb tagumine ots teid selle avaliku aadressi omanikuks. Seetõttu saame ehitada sõnumiallkirjastamise põhise autentimismehhanismi, mille identifikaatoriks on kasutaja avalik aadress.
Kui see ei tundu selge, on see kõik korras, sest me selgitame seda järk-järgult:
Pange tähele, et samal ajal kui kasutame Ethereum blockchain (MetaMask, Ethereumi avalikud aadressid), ei vaja see sisselogimisprotsess tegelikult plokiahelat: see vajab ainult oma krüptograafia funktsioone. Nagu öeldud, muutumas MetaMask nii populaarne laiendus , tundub nüüd hea aeg selle sisselogimisvoo tutvustamiseks.
Kui teate juba, mis on MetaMask, jätke see jaotis julgelt vahele.
MetaMask on brauseri pistikprogramm, saadaval kui MetaMask Chrome'i laiendus või Firefoxi lisandmoodul . Põhimõtteliselt toimib see Ethereumi rahakotina: selle installimisega saate juurdepääsu ainulaadsele Ethereumi avalikule aadressile, millega saate hakata eetreid või märke saatma ja vastu võtma.
Kuid MetaMask teeb midagi enamat kui Ethereumi rahakott. Brauserilaiendina saab see suhelda praeguse sirvitava veebilehega. Ta teeb seda, süstides JavaScripti teeki nimega web3.js igal külastataval veebisaidil. Pärast süstimist a web3
objekt on saadaval window.web3
kaudu selle veebisaidi JavaScripti koodis. Kui soovite vaadata, kuidas see objekt välja näeb, sisestage lihtsalt window.web3
Chrome'i või Firefoxi DevToolsi konsoolis, kui teil on installitud MetaMask.
Web3.js on Ethereumi plokiahela JavaScripti liides. Funktsioonid on järgmised:
web3.eth.getBlockNumber
)web3.eth.coinbase
)web3.eth.getBalance
)web3.eth.sendTransaction
)web3.personal.sign
)Kui MetaMask on installitud, pääseb mis tahes esiotsa kood kõigile neile funktsioonidele juurde ja suhelda plokiahelaga . Neid nimetatakse dapps või DApps (detsentraliseeritud rakenduste puhul - mõnikord isegi stiilis “pApps”).
Seotud DAppi arendusega: Ajast lukustatud rahakotid: sissejuhatus Ethereumi nutikatesse lepingutesseEnamik web3.js-i funktsioone on lugemisfunktsioonid (saada blokeerimine, tasakaalu hankimine jne) ja web3
annab kohe vastuse. Kuid mõned funktsioonid (näiteks web3.eth.sendTransaction
ja web3.personal.sign
) vajavad jooksvat kontot, et allkirjastada mõned andmed oma privaatvõtmega. Need funktsioonid käivitavad MetaMaskil kinnitusekraani kuvamise, et kontrollida, kas kasutaja teab, millele ta alla kirjutab.
Vaatame, kuidas MetaMaskit selleks kasutada. Lihtsa testi tegemiseks kleepige DevToolsi konsooli järgmine rida:
web3.personal.sign(web3.fromUtf8('Hello from ApeeScape!'), web3.eth.coinbase, console.log);
See käsk tähendab: allkirjastage minu sõnum, teisendatuna utf8-st heksiks, coinbase'i kontoga (s.o jooksev konto) ja tagasihelistamisena printige allkiri. Ilmub MetaMask hüpikaken ja kui te sellele alla kirjutate, prinditakse allkirjastatud kiri.
Kasutame web3.personal.sign
meie sisselogimisvoos.
Viimane märkus selle jaotise kohta: MetaMask süstib teie praegusesse brauserisse web3.js, kuid tegelikult on ka muid eraldiseisvaid brausereid, mis süstivad ka veebi 3.js, näiteks Udu , näiteks. Kuid minu arvates pakub MetaMask täna parimat UX-i ja lihtsamat üleminekut tavakasutajatele dappide uurimiseks.
Alustame kuidas . The kuidas veenan teid loodetavasti, et see on turvaline, nii et ma hoian seda miks osa lühike.
Nagu ülevaates öeldud, unustame plokiahela. Meil on traditsiooniline Web 2.0 klient-server RESTful arhitektuur. Teeme ühe eelduse: et kõigil meie esilehekülge külastavatel kasutajatel on MetaMask installitud. Selle eeldusega näitame, kuidas töötab paroolideta krüptograafiliselt turvaline sisselogimisvoog.
Esiteks meie User
mudelil peab olema kaks uut kohustuslikku välja: publicAddress
ja nonce
. Lisaks publicAddress
peab olema ainulaadne. Võite säilitada tavalised username
, email
ja password
väljad - eriti kui soovite oma MetaMaski sisselogimist rakendada paralleelselt e-posti / parooli sisselogimisega -, kuid need on valikulised.
Registreerumisprotsess on ka veidi erinev, kuna publicAddress
on registreerumisel kohustuslik väli, kui kasutaja soovib kasutada MetaMaski sisselogimist. Võite olla kindel, et kasutaja ei pea kunagi oma publicAddress
käsitsi, kuna selle saab tuua web3.eth.coinbase
Looge iga andmebaasi kasutaja jaoks juhuslik string nonce
valdkonnas. Näiteks nonce
võib olla suur juhuslik täisarv.
Eeldades, et MetaMask on olemas, on meil oma JavaScripti esiplaani koodis juurdepääs window.web3
Seetõttu võime helistada web3.eth.coinbase
praeguse MetaMaski konto avaliku aadressi saamiseks.
Kui kasutaja klõpsab sisselogimisnuppu, käivitame tagaküljele API-kõne, et hankida tema avaliku aadressiga seotud nonce. Midagi marsruudi sarnast filtri parameetriga GET /api/users?publicAddress=${publicAddress}
peaks tegema. Muidugi, kuna see on autentimata API-kõne, tuleks selle tagaosa konfigureerida nii, et sellel marsruudil kuvatakse ainult avalikku teavet (sealhulgas nonce
).
Kui eelmine taotlus ei anna tulemust, tähendab see, et praegune avalik aadress pole veel registreerunud. Kõigepealt peame looma uue konto POST /users
kaudu, edastades publicAddress
taotlusorganis. Teiselt poolt, kui on tulemus, siis salvestame selle nonce
Kui kasutajaliides saab nonce
eelmise API kõne vastusena käivitab see järgmise koodi:
web3.personal.sign(nonce, web3.eth.coinbase, callback);
See palub MetaMaskil kuvada kinnituse hüpik sõnumi allkirjastamiseks. Selles hüpikus kuvatakse nonce, et kasutaja teaks, et ta ei kirjuta alla pahatahtlikele andmetele.
Kui ta selle aktsepteerib, kutsutakse tagasihelistamisfunktsioon argumendina allkirjastatud sõnumiga (nimega signature
). Esipaneel teeb seejärel veel ühe API-kõne POST /api/authentication
-le, läbides keha mõlema signature
-ga ja publicAddress
.
Kui tagumine ots saab POST /api/authentication
päringu, tõmbab see kasutaja kõigepealt andmebaasi, mis vastab publicAddress
esitatud taotlusorganis. Eelkõige tõmbab see seotud nonce.
Kui teil on olemas nonce, avalik aadress ja allkiri, saab tagumine ots seda teha krüptograafiliselt kontrollima et kasutaja on õigesti allkirjastanud nonce'i. Sellisel juhul on kasutaja tõendanud, et ta omab avalikku aadressi ja võime teda pidada autentseks. JWT või seansi identifikaatori saab seejärel tagastada kasutajaliidesesse.
Kasutaja välistamiseks sama allkirjaga uuesti sisselogimisest (juhul, kui see satub ohtu) veendume, et järgmine kord, kui sama kasutaja soovib sisse logida, peab ta uue allkirjastama. See saavutatakse teise juhusliku nonce
genereerimise teel selle kasutaja jaoks ja säilitades selle andmebaasis.
Ja seal sa lähed! Nii haldame paroolita sisselogimisvoogu, mis ei allkirjasta.
Autentimine on definitsiooni järgi tegelikult ainult konto omandiõiguse tõend. Kui tuvastate oma konto ainulaadselt avaliku aadressi abil, on selle omamise tõendamine krüptograafiliselt tühine.
Et vältida juhtumit, kus häkker saab kätte ühe kindla kirja ja selle allkirja (kuid mitte teie tegeliku privaatvõtme), sunnime sõnumit allkirjastama:
Muutsime seda oma selgituses pärast igat edukat sisselogimist, kuid võis ette kujutada ka ajatemplipõhist mehhanismi.
Selles jaotises vaatan ükshaaval läbi kuus ülaltoodud sammu. Näitan mõningaid koodijuppe, kuidas seda sisselogimisvoogu nullist üles ehitada või olemasolevasse tagumisse ossa integreerida ilma liigsete pingutusteta.
Selle artikli jaoks lõin väikese demo-rakenduse. Kasutatav virn on järgmine:
Püüan kasutada võimalikult vähe raamatukogusid. Loodan, et kood on piisavalt lihtne, et saaksite selle hõlpsalt teistele tehnikakogudele teisaldada.
Kogu projekti saab näha aastal see GitHubi hoidla . Korraldatakse demo siin .
Kohustuslik on kaks välja: publicAddress
ja nonce
. Algatame nonce
juhusliku suure arvuna. Pärast iga edukat sisselogimist tuleks seda numbrit muuta. Lisasin ka valikulise username
siin olev väli, mida kasutaja saaks muuta.
const User = sequelize.define('User', { nonce: { allowNull: false, type: Sequelize.INTEGER.UNSIGNED, defaultValue: () => Math.floor(Math.random() * 1000000) // Initialize with a random nonce }, publicAddress: { allowNull: false, type: Sequelize.STRING, unique: true, validate: { isLowercase: true } }, username: { type: Sequelize.STRING, unique: true } });
Selle lihtsustamiseks määrasin publicAddress
väli väiketähtedena. Rangem rakendamine lisaks valideerimisfunktsiooni, et kontrollida, kas kõik siin olevad aadressid on kehtivad Ethereumi aadressid .
Seda tehakse defaultValue()
funktsioon ülaltoodud mudeli definitsioonis.
Järgmine samm on lisada tagaküljele mõni katlakoodi kood, et käsitleda CRUD-meetodeid User
mudel, mida me siin ei tee.
Üleminek kasutajaliidese koodile, kui kasutaja klõpsab sisselogimisnuppu, on meie handleClick
käitleja teeb järgmist:
class Login extends Component { handleClick = () => { // --snip-- const publicAddress = web3.eth.coinbase.toLowerCase(); // Check if user with current publicAddress is already present on back end fetch(`${process.env.REACT_APP_BACKEND_URL}/users?publicAddress=${publicAddress}`) .then(response => response.json()) // If yes, retrieve it. If no, create it. .then( users => (users.length ? users[0] : this.handleSignup(publicAddress)) ) // --snip-- }; handleSignup = publicAddress => fetch(`${process.env.REACT_APP_BACKEND_URL}/users`, { body: JSON.stringify({ publicAddress }), headers: { 'Content-Type': 'application/json' }, method: 'POST' }).then(response => response.json()); }
Siit leiame aktiivse MetaMaski konto web3.eth.coinbase
-ga. Seejärel kontrollime, kas see publicAddress
on juba tagumises otsas olemas või mitte. Me kas leiame selle, kui kasutaja on juba olemas, või kui pole, loome uue konto jaotises handleSignup
meetod.
Liigume edasi oma handleClick
-s meetod. Nüüd on meie valduses kasutaja, kelle annab tagumine ots (olgu see siis allalaaditud või vastloodud). Eelkõige on meil nende nonce
ja publicAddress
. Nii et oleme valmis allkirjastama nonce selle publicAddress
-ga seotud privaatvõtmega kasutades web3.personal.sign
. Seda tehakse handleSignMessage
funktsioon.
Pange tähele, et web3.personal.sign
võtab esimese argumendina stringi kuueteistkümnendsüsteemi. Peame teisendama oma UTF-8 kodeeritud stringi kuuskantvormingusse, kasutades web3.fromUtf8
Samuti otsustasin mitte ainult allkirjastada, vaid allkirjastada kasutajasõbralikuma lause, kuna seda kuvatakse MetaMaski kinnitus hüpikus: I am signing my once-time nonce: ${nonce}
class Login extends Component { handleClick = () => { // --snip-- fetch(`${process.env.REACT_APP_BACKEND_URL}/users?publicAddress=${publicAddress}`) .then(response => response.json()) // If yes, retrieve it. If no, create it. .then( users => (users.length ? users[0] : this.handleSignup(publicAddress)) ) // Popup MetaMask confirmation modal to sign message .then(this.handleSignMessage) // Send signature to back end on the /auth route .then(this.handleAuthenticate) // --snip-- }; handleSignMessage = ({ publicAddress, nonce }) => { return new Promise((resolve, reject) => web3.personal.sign( web3.fromUtf8(`I am signing my one-time nonce: ${nonce}`), publicAddress, (err, signature) => { if (err) return reject(err); return resolve({ publicAddress, signature }); } ) ); }; handleAuthenticate = ({ publicAddress, signature }) => fetch(`${process.env.REACT_APP_BACKEND_URL}/auth`, { body: JSON.stringify({ publicAddress, signature }), headers: { 'Content-Type': 'application/json' }, method: 'POST' }).then(response => response.json()); }
Kui kasutaja on kirjale edukalt alla kirjutanud, liigume handleAuthenticate
meetod. Saadame lihtsalt päringu /auth
marsruudi tagaosas, saates meie publicAddress
samuti signature
kasutaja äsja allkirjastatud sõnumist.
See on veidi keerulisem osa. Tagaosa saab päringu /auth
marsruut, mis sisaldab publicAddress
ja a signature
ning peab kontrollima, kas see publicAddress
on alla kirjutanud õigele nonce
.
Esimene samm on kasutaja andmebaasist hankimine nimetatud publicAddress
; on ainult üks, sest me määratlesime publicAddress
unikaalse väljana andmebaasis. Seejärel määrasime teate msg
kui 'kirjutan alla oma ...', täpselt nagu 4. sammu esiosas, selle kasutaja meelepärasusega.
Järgmine plokk on kontroll ise. Sellega on seotud krüptograafia. Kui tunnete end seiklushimulisena, soovitan teil sellest lähemalt lugeda elliptilise kõvera allkirjad .
Selle ploki kokkuvõtteks on see, mida ta teeb, arvestades meie msg
(sisaldab nonce
) ja meie signature
, ecrecover
funktsioon väljastab avaliku aadressi, mida kasutati msg
allkirjastamiseks. Kui see sobib meie publicAddress
päringu asutuselt, siis tõendas taotluse teinud kasutaja, et omab edukalt publicAddress
Peame neid autentseks.
User.findOne({ where: { publicAddress } }) // --snip-- .then(user => { const msg = `I am signing my one-time nonce: ${user.nonce}`; // We now are in possession of msg, publicAddress and signature. We // can perform an elliptic curve signature verification with ecrecover const msgBuffer = ethUtil.toBuffer(msg); const msgHash = ethUtil.hashPersonalMessage(msgBuffer); const signatureBuffer = ethUtil.toBuffer(signature); const signatureParams = ethUtil.fromRpcSig(signatureBuffer); const publicKey = ethUtil.ecrecover( msgHash, signatureParams.v, signatureParams.r, signatureParams.s ); const addressBuffer = ethUtil.publicToAddress(publicKey); const address = ethUtil.bufferToHex(addressBuffer); // The signature verification is successful if the address found with // ecrecover matches the initial publicAddress if (address.toLowerCase() === publicAddress.toLowerCase()) { return user; } else { return res .status(401) .send({ error: 'Signature verification failed' }); } })
Eduka autentimise korral genereerib tagumine JWT ja saadab selle kliendile tagasi. See on klassikaline autentimisskeem ja kood JWT integreerimiseks teie taguotsaga leiate repost .
Viimane samm on turvalisuse huvides nonce'i muutmine. Kusagil pärast edukat autentimist lisage see kood:
// --snip-- .then(user => { user.nonce = Math.floor(Math.random() * 1000000); return user.save(); }) // --snip--
See ei olnud nii raske, kas pole? Jällegi, kui soovite näha, kuidas kogu rakendus on juhtmega ühendatud (JWT-põlvkond, CRUD-marsruudid, localStorage jne), vaadake julgelt GitHubi repo .
Kuigi plokiahelal võivad olla vead ja see on alles imikueas, ei saa ma piisavalt rõhutada, kuidas seda sisselogimisvoogu saaks rakendada mis tahes olemasoleval veebisaidil täna . Siin on loetelu argumentidest, miks seda sisselogimisvoogu eelistatakse mõlema e-posti / parooli asemel ja sotsiaalne sisselogimine:
Muidugi saab MetaMaski sisselogimisvoogu suurepäraselt kasutada paralleelselt teiste traditsiooniliste sisselogimismeetoditega. Iga konto ja tema valduses oleva avaliku aadressi vahel tuleb kaardistada.
Kuid see sisselogimisvoog ei sobi kõigile:
web3
toega brauserita. Kui teie publikut ei huvita krüptorahad, on väike võimalus, et nad isegi kaaluksid MetaMaski installimist. Loodame hiljutise krüptobuumi abil, et liigume a Veeb 3.0 Internet .Nagu nägime, web3
on selle sisselogimisvoo eeltingimus. Töölauabrauserites süstib MetaMask seda. Mobiilibrauserites pole aga laiendusi, nii et see sisselogimisvoog ei toimi mobiilseadme Safaris, Chrome'is ega Firefoxis kohe valmis. On mõned eraldiseisvad mobiilibrauserid, mis süstivad web3
- põhiliselt brauserisse pakitud MetaMask. Need on selle kirjutamise alguses üsna varajases staadiumis, kuid kui olete huvitatud, vaadake seda Šifreeri , Staatus ja Toshi . „Logi sisse MetaMaskiga” töötab nende mobiilibrauseritega.
Mobiilirakenduste puhul on vastus jah, sisselogimisvoog töötab, kuid ettevalmistamiseks on palju eeltöid. Põhimõtteliselt peaksite ise lihtsa Ethereumi rahakoti uuesti üles ehitama. See hõlmab avalike aadresside genereerimist, algsõnade taastamist ja turvalist privaatvõtmete salvestamist, samuti web3.personal.sign
ja kinnitus hüpikaken. Õnneks on teid abistavaid raamatukogusid. Ülioluline valdkond, millele keskenduda, on loomulikult turvalisus, kuna rakendus ise hoiab privaatvõtit. Töölauabrauserites delegeerisime selle ülesande MetaMaskile.
Nii et ma väidaksin, et lühike vastus on eitav, see sisselogimisvoog täna mobiilis ei tööta. Selles suunas pingutatakse, kuid lihtne lahendus jääb tänapäeval paralleelseks traditsiooniliseks sisselogimismeetodiks mobiilikasutajatele.
Tutvustasime selles artiklis ühe klõpsuga krüptograafiliselt turvalist sisselogimisvoogu, kaasamata kolmandat osapoolt, nimega „Logi sisse MetaMaskiga”. Selgitasime, kuidas taguotsaga loodud juhusliku nonce'i digitaalallkiri võib tõendada konto omamist ja seega autentimist. Uurisime ka selle sisselogimismehhanismi kompromisse võrreldes tavapäraste e-posti / parooli või sotsiaalse sisselogimisega nii töölaual kui ka mobiilseadmes.
Ehkki sellise sisselogimisvoo sihtgrupp on tänapäevalgi väike, loodan südamest, et mõned teist tunnevad paralleelselt traditsiooniliste sisselogimisvoogudega inspiratsiooni pakkuda oma veebirakenduses MetaMaskiga sisselogimist - ja ma tahaksin sellest kuulda kui teete. Kui teil on küsimusi, võtke julgelt ühendust allpool toodud kommentaaridega.
Seotud: Ultimate ENS ja ppApp juhendaja