web2 ప్రమాణీకరణ కోసం ఇతీరియమును ఉపయోగించడం
పరిచయం
SAML (opens in a new tab) అనేది web2లో ఉపయోగించే ఒక ప్రామాణికం, ఇది గుర్తింపు ప్రదాత (IdP) (opens in a new tab)కి సేవా ప్రదాతల (SP) (opens in a new tab) కోసం వినియోగదారుడి సమాచారాన్ని అందించడానికి అనుమతిస్తుంది.
ఈ ట్యుటోరియల్లో మీరు ఇతీరియమును ఇంకా స్థానికంగా సపోర్ట్ చేయని web2 సేవలకు తమను తాము ప్రామాణీకరించుకోవడానికి వినియోగదారులను వారి ఇతీరియము వాలెట్లను ఉపయోగించడానికి అనుమతించడానికి, ఇతీరియము సంతకాలను SAMLతో ఎలా ఇంటిగ్రేట్ చేయాలో నేర్చుకుంటారు.
ఈ ట్యుటోరియల్ ఇద్దరు వేర్వేరు ప్రేక్షకుల కోసం వ్రాయబడిందని గమనించండి:
- ఇతీరియమును అర్థం చేసుకుని, SAML నేర్చుకోవాల్సిన ఇతీరియము వ్యక్తులు
- SAML మరియు web2 ప్రామాణీకరణను అర్థం చేసుకుని, ఇతీరియము నేర్చుకోవాల్సిన Web2 వ్యక్తులు
ఫలితంగా, ఇది మీకు ఇప్పటికే తెలిసిన చాలా పరిచయ విషయాలను కలిగి ఉండబోతోంది. దాన్ని దాటవేయడానికి సంకోచించకండి.
ఇతీరియము వ్యక్తుల కోసం SAML
SAML ఒక కేంద్రీకృత ప్రోటోకాల్. ఒక సేవా ప్రదాత (SP) ఒక గుర్తింపు ప్రదాత (IdP) నుండి వాదనలను (ఉదాహరణకు "ఇది నా వినియోగదారుడు జాన్, అతనికి A, B, మరియు C చేయడానికి అనుమతులు ఉండాలి") అంగీకరించాలంటే, దానితో గానీ, లేదా ఆ IdP యొక్క సర్టిఫికేట్పై సంతకం చేసిన సర్టిఫికేట్ అథారిటీ (opens in a new tab)తో గానీ ముందుగా ఉన్న విశ్వాస సంబంధం ఉండాలి.
ఉదాహరణకు, SP కంపెనీలకు ప్రయాణ సేవలను అందించే ఒక ప్రయాణ ఏజెన్సీ కావచ్చు, మరియు IdP ఒక కంపెనీ యొక్క అంతర్గత వెబ్ సైట్ కావచ్చు. ఉద్యోగులు వ్యాపార ప్రయాణాన్ని బుక్ చేసుకోవాల్సిన అవసరం వచ్చినప్పుడు, ప్రయాణ ఏజెన్సీ వాస్తవానికి ప్రయాణాన్ని బుక్ చేసుకోనివ్వడానికి ముందు కంపెనీ ద్వారా ప్రామాణీకరణ కోసం వారిని పంపుతుంది.
యాక్సెస్ కోసం బ్రౌజర్, SP, మరియు IdP అనే మూడు సంస్థలు ఈ విధంగా చర్చలు జరుపుతాయి. SPకి ముందుగా బ్రౌజర్ని ఉపయోగించే వినియోగదారుడి గురించి ఏమీ తెలియాల్సిన అవసరం లేదు, కేవలం IdPని విశ్వసిస్తే చాలు.
SAML వ్యక్తుల కోసం ఇతీరియము
ఇతీరియము ఒక వికేంద్రీకృత వ్యవస్థ.
వినియోగదారులు ఒక ప్రైవేట్ కీని కలిగి ఉంటారు (సాధారణంగా బ్రౌజర్ పొడిగింపులో ఉంచబడుతుంది). ప్రైవేట్ కీ నుండి మీరు పబ్లిక్ కీని, మరియు దాని నుండి 20-బైట్ చిరునామాను పొందవచ్చు. వినియోగదారులు ఒక సిస్టమ్లోకి లాగ్ ఇన్ చేయాల్సిన అవసరం వచ్చినప్పుడు, వారు ఒక నాన్స్ (ఒకే-వినియోగ విలువ)తో ఒక సందేశంపై సంతకం చేయమని అభ్యర్థించబడతారు. ఆ చిరునామా ద్వారా సంతకం సృష్టించబడిందని సర్వర్ ధృవీకరించగలదు.
సంతకం కేవలం ఇతీరియము చిరునామాను మాత్రమే ధృవీకరిస్తుంది. ఇతర వినియోగదారు గుణాలను పొందడానికి, మీరు సాధారణంగా ధృవీకరణలు (opens in a new tab) ఉపయోగిస్తారు. ఒక ధృవీకరణలో సాధారణంగా ఈ ఫీల్డ్లు ఉంటాయి:
- ధృవీకర్త, ధృవీకరణ చేసిన చిరునామా
- గ్రహీత, ధృవీకరణ వర్తించే చిరునామా
- డేటా, పేరు, అనుమతులు మొదలైన ధృవీకరించబడుతున్న డేటా.
- స్కీమా, డేటాను అర్థం చేసుకోవడానికి ఉపయోగించే స్కీమా యొక్క ID.
ఇతీరియము యొక్క వికేంద్రీకృత స్వభావం కారణంగా, ఏ వినియోగదారుడైనా ధృవీకరణలు చేయవచ్చు. మేము ఏ ధృవీకరణలను నమ్మదగినవిగా పరిగణిస్తామో గుర్తించడానికి ధృవీకర్త యొక్క గుర్తింపు ముఖ్యం.
సెటప్
మొదటి దశ SAML SP మరియు SAML IdP తమ మధ్య సంభాషించుకోవడం.
-
సాఫ్ట్వేర్ను డౌన్లోడ్ చేయండి. ఈ వ్యాసం కోసం నమూనా సాఫ్ట్వేర్ githubలో ఉంది (opens in a new tab). వివిధ దశలు వివిధ బ్రాంచ్లలో నిల్వ చేయబడతాయి, ఈ దశ కోసం మీకు
saml-onlyకావాలి1git clone https://github.com/qbzzt/250420-saml-ethereum -b saml-only2cd 250420-saml-ethereum3pnpm install -
స్వీయ-సంతకం చేసిన సర్టిఫికేట్లతో కీలను సృష్టించండి. అంటే కీ దాని స్వంత సర్టిఫికేట్ అథారిటీ, మరియు దానిని సేవా ప్రదాతకు మాన్యువల్గా దిగుమతి చేసుకోవాలి. మరింత సమాచారం కోసం OpenSSL డాక్స్ (opens in a new tab) చూడండి.
1mkdir keys2cd keys3openssl req -new -x509 -days 365 -nodes -sha256 -out saml-sp.crt -keyout saml-sp.pem -subj /CN=sp/4openssl req -new -x509 -days 365 -nodes -sha256 -out saml-idp.crt -keyout saml-idp.pem -subj /CN=idp/5cd .. -
సర్వర్లను ప్రారంభించండి (SP మరియు IdP రెండూ)
1pnpm start -
URL http://localhost:3000/ (opens in a new tab) వద్ద SPకి బ్రౌజ్ చేసి, IdP (పోర్ట్ 3001)కి మళ్ళించబడటానికి బటన్ను క్లిక్ చేయండి.
-
IdPకి మీ ఇమెయిల్ చిరునామాను అందించి, సేవా ప్రదాతకు లాగిన్ చేయండి క్లిక్ చేయండి. మీరు తిరిగి సేవా ప్రదాతకు (పోర్ట్ 3000) మళ్ళించబడటాన్ని మరియు అది మీ ఇమెయిల్ చిరునామా ద్వారా మిమ్మల్ని గుర్తించడాన్ని చూడండి.
వివరణాత్మక వివరణ
దశలవారీగా ఇది జరుగుతుంది:
src/config.mts
ఈ ఫైల్ గుర్తింపు ప్రదాత మరియు సేవా ప్రదాత రెండింటికీ కాన్ఫిగరేషన్ను కలిగి ఉంది. సాధారణంగా ఈ రెండూ వివిధ సంస్థలుగా ఉంటాయి, కానీ ఇక్కడ మనం సరళత కోసం కోడ్ను పంచుకోవచ్చు.
1const fs = await import("fs")23const protocol="http"ఇప్పుడైతే మనం కేవలం పరీక్షిస్తున్నాం, కాబట్టి HTTP ఉపయోగించడం పర్వాలేదు.
1export const spCert = fs.readFileSync("keys/saml-sp.crt").toString()2export const idpCert = fs.readFileSync("keys/saml-idp.crt").toString()పబ్లిక్ కీలను చదవండి, ఇవి సాధారణంగా రెండు కాంపోనెంట్లకు అందుబాటులో ఉంటాయి (మరియు నేరుగా విశ్వసించబడతాయి, లేదా విశ్వసనీయ సర్టిఫికేట్ అథారిటీ ద్వారా సంతకం చేయబడతాయి).
1export const spPort = 30002export const spHostname = "localhost"3export const spDir = "sp"45export const idpPort = 30016export const idpHostname = "localhost"7export const idpDir = "idp"89export const spUrl = `${protocol}://${spHostname}:${spPort}/${spDir}`10export const idpUrl = `${protocol}://${idpHostname}:${idpPort}/${idpDir}`అన్నీ చూపించురెండు కాంపోనెంట్ల కోసం URLలు.
1export const spPublicData = {సేవా ప్రదాత కోసం పబ్లిక్ డేటా.
1 entityID: `${spUrl}/metadata`,సంప్రదాయం ప్రకారం, SAMLలో entityID అనేది సంస్థ యొక్క మెటాడేటా అందుబాటులో ఉండే URL. ఈ మెటాడేటా ఇక్కడ ఉన్న పబ్లిక్ డేటాకు అనుగుణంగా ఉంటుంది, అయితే ఇది XML రూపంలో ఉంటుంది.
1 wantAssertionsSigned: true,2 authnRequestsSigned: false,3 signingCert: spCert,4 allowCreate: true,5 assertionConsumerService: [{6 Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',7 Location: `${spUrl}/assertion`,8 }]9 }అన్నీ చూపించుమన ప్రయోజనాల కోసం అత్యంత ముఖ్యమైన నిర్వచనం assertionConsumerServer. అంటే సేవా ప్రదాతకు ఏదైనా చెప్పడానికి (ఉదాహరణకు, "మీకు ఈ సమాచారం పంపిన వినియోగదారుడు somebody@example.com (opens email client)") మనం HTTP POST (opens in a new tab)ని URL http://localhost:3000/sp/assertionకు ఉపయోగించాలి.
1export const idpPublicData = {2 entityID: `${idpUrl}/metadata`,3 signingCert: idpCert,4 wantAuthnRequestsSigned: false,5 singleSignOnService: [{6 Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",7 Location: `${idpUrl}/login`8 }],9 singleLogoutService: [{10 Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",11 Location: `${idpUrl}/logout`12 }],13 }అన్నీ చూపించుగుర్తింపు ప్రదాత కోసం పబ్లిక్ డేటా ఇలాంటిదే. ఒక వినియోగదారుడిని లాగ్ ఇన్ చేయడానికి మీరు http://localhost:3001/idp/loginకు POST చేయాలని మరియు ఒక వినియోగదారుడిని లాగ్ అవుట్ చేయడానికి మీరు http://localhost:3001/idp/logoutకు POST చేయాలని ఇది నిర్దేశిస్తుంది.
src/sp.mts
ఇది సేవా ప్రదాతను అమలు చేసే కోడ్.
1import * as config from "./config.mts"2const fs = await import("fs")3const saml = await import("samlify")SAMLని అమలు చేయడానికి మేము samlify (opens in a new tab) లైబ్రరీని ఉపయోగిస్తాము.
1import * as validator from "@authenio/samlify-node-xmllint"2saml.setSchemaValidator(validator)XML సరైనదని, ఊహించిన పబ్లిక్ కీతో సంతకం చేయబడిందని మొదలైనవాటిని ధృవీకరించడానికి samlify లైబ్రరీ ఒక ప్యాకేజీని కలిగి ఉండాలని ఆశిస్తుంది. ఈ ప్రయోజనం కోసం మేము @authenio/samlify-node-xmllint (opens in a new tab)ని ఉపయోగిస్తాము.
1const express = (await import("express")).default2const spRouter = express.Router()3const app = express()ఒక express (opens in a new tab) Router (opens in a new tab) అనేది ఒక వెబ్ సైట్లో మౌంట్ చేయగల "మినీ వెబ్ సైట్". ఈ సందర్భంలో, మేము అన్ని సేవా ప్రదాత నిర్వచనాలను ఒకచోట చేర్చడానికి దీనిని ఉపయోగిస్తాము.
1const spPrivateKey = fs.readFileSync("keys/saml-sp.pem").toString()23const sp = saml.ServiceProvider({4 privateKey: spPrivateKey, 5 ...config.spPublicData6})సేవా ప్రదాత యొక్క స్వీయ ప్రాతినిధ్యం అనేది మొత్తం పబ్లిక్ డేటా, మరియు సమాచారాన్ని సంతకం చేయడానికి అది ఉపయోగించే ప్రైవేట్ కీ.
1const idp = saml.IdentityProvider(config.idpPublicData);పబ్లిక్ డేటాలో సేవా ప్రదాతకు గుర్తింపు ప్రదాత గురించి తెలియవలసినవన్నీ ఉంటాయి.
1spRouter.get(`/metadata`, 2 (req, res) => res.header("Content-Type", "text/xml").send(sp.getMetadata())3)ఇతర SAML కాంపోనెంట్లతో పరస్పర చర్యను ప్రారంభించడానికి, సేవ మరియు గుర్తింపు ప్రదాతలు వారి పబ్లిక్ డేటాను (మెటాడేటా అని పిలుస్తారు) /metadataలో XML ఫార్మాట్లో అందుబాటులో ఉంచాలి.
1spRouter.post(`/assertion`,ఇది బ్రౌజర్ తనను తాను గుర్తించుకోవడానికి యాక్సెస్ చేసే పేజీ. ఈ వాదనలో వినియోగదారు గుర్తింపు (ఇక్కడ మనం ఇమెయిల్ చిరునామాను ఉపయోగిస్తాము) ఉంటుంది, మరియు అదనపు గుణాలను కూడా కలిగి ఉండవచ్చు. ఇది పైన ఉన్న సీక్వెన్స్ రేఖాచిత్రంలో 7వ దశకు హ్యాండ్లర్.
1 async (req, res) => {2 // console.log(`SAML response:\n${Buffer.from(req.body.SAMLResponse, 'base64').toString('utf-8')}`)వాదనలో అందించిన XML డేటాను చూడటానికి మీరు వ్యాఖ్యానించిన ఆదేశాన్ని ఉపయోగించవచ్చు. ఇది బేస్64 ఎన్కోడ్ (opens in a new tab) చేయబడింది.
1 try {2 const loginResponse = await sp.parseLoginResponse(idp, 'post', req);గుర్తింపు సర్వర్ నుండి లాగిన్ అభ్యర్థనను విశ్లేషించండి.
1 res.send(`2 <html>3 <body>4 <h2>Hello ${loginResponse.extract.nameID}</h2>5 </body>6 </html>7 `)8 res.send();వినియోగదారుడికి మేము లాగిన్ పొందామని చూపించడానికి, ఒక HTML ప్రతిస్పందనను పంపండి.
1 } catch (err) {2 console.error('Error processing SAML response:', err);3 res.status(400).send('SAML authentication failed');4 }5 }6)వైఫల్యం సంభవించినప్పుడు వినియోగదారుడికి తెలియజేయండి.
1spRouter.get('/login',బ్రౌజర్ ఈ పేజీని పొందడానికి ప్రయత్నించినప్పుడు ఒక లాగిన్ అభ్యర్థనను సృష్టించండి. ఇది పైన ఉన్న సీక్వెన్స్ రేఖాచిత్రంలో 1వ దశకు హ్యాండ్లర్.
1 async (req, res) => {2 const loginRequest = await sp.createLoginRequest(idp, "post")ఒక లాగిన్ అభ్యర్థనను పోస్ట్ చేయడానికి సమాచారం పొందండి.
1 res.send(`2 <html>3 <body>4 <script>5 window.onload = function () { document.forms[0].submit(); } 6 </script>ఈ పేజీ ఫారమ్ను (క్రింద చూడండి) స్వయంచాలకంగా సమర్పిస్తుంది. ఈ విధంగా వినియోగదారుడు మళ్ళించబడటానికి ఏమీ చేయనవసరం లేదు. ఇది పైన ఉన్న సీక్వెన్స్ రేఖాచిత్రంలో 2వ దశ.
1 <form method="post" action="${loginRequest.entityEndpoint}">loginRequest.entityEndpoint (గుర్తింపు ప్రదాత ఎండ్పాయింట్ యొక్క URL)కి పోస్ట్ చేయండి.
1 <input type="hidden" name="${loginRequest.type}" value="${loginRequest.context}" />ఇన్పుట్ పేరు loginRequest.type (SAMLRequest). ఆ ఫీల్డ్ కోసం కంటెంట్ loginRequest.context, ఇది మళ్ళీ బేస్64 ఎన్కోడ్ చేయబడిన XML.
1 </form>2 </body>3 </html>4 `) 5 }6)78app.use(express.urlencoded({extended: true}))ఈ మిడిల్వేర్ (opens in a new tab) HTTP అభ్యర్థన (opens in a new tab) యొక్క బాడీని చదువుతుంది. డిఫాల్ట్గా ఎక్స్ప్రెస్ దానిని పట్టించుకోదు, ఎందుకంటే చాలా అభ్యర్థనలకు అది అవసరం లేదు. POST బాడీని ఉపయోగిస్తుంది కాబట్టి మాకు అది అవసరం.
1app.use(`/${config.spDir}`, spRouter)సేవా ప్రదాత డైరెక్టరీ (/sp)లో రౌటర్ను మౌంట్ చేయండి.
1app.get("/", (req, res) => {2 res.send(`3 <html>4 <body>5 <button onClick="document.location.href='${config.spUrl}/login'">6 Click here to log on7 </button>8 </body>9 </html>10 `)11})అన్నీ చూపించుఒక బ్రౌజర్ రూట్ డైరెక్టరీని పొందడానికి ప్రయత్నిస్తే, దానికి లాగిన్ పేజీకి ఒక లింక్ను అందించండి.
1app.listen(config.spPort, () => {2 console.log(`service provider is running on http://${config.spHostname}:${config.spPort}`)3})ఈ ఎక్స్ప్రెస్ అప్లికేషన్తో spPortకి వినండి.
src/idp.mts
ఇది గుర్తింపు ప్రదాత. ఇది సేవా ప్రదాతకు చాలా ఇలాంటిది, దిగువ వివరణలు వివిధ భాగాల కోసం.
1const xmlParser = new (await import("fast-xml-parser")).XMLParser(2 {3 ignoreAttributes: false, // Preserve attributes4 attributeNamePrefix: "@_", // Prefix for attributes5 }6)సేవా ప్రదాత నుండి మేము స్వీకరించే XML అభ్యర్థనను మేము చదివి అర్థం చేసుకోవాలి.
1const getLoginPage = requestId => `ఈ ఫంక్షన్ పైన ఉన్న సీక్వెన్స్ రేఖాచిత్రంలో 4వ దశలో తిరిగి ఇవ్వబడిన స్వీయ-సమర్పిత ఫారమ్తో పేజీని సృష్టిస్తుంది.
1<html>2 <head>3 <title>Login page</title>4 </head>5 <body>6 <h2>Login page</h2>7 <form method="post" action="./loginSubmitted">8 <input type="hidden" name="requestId" value="${requestId}" />9 Email address: <input name="email" />10 <br />11 <button type="Submit">12 Login to the service provider13 </button>అన్నీ చూపించుమేము సేవా ప్రదాతకు పంపే రెండు ఫీల్డ్లు ఉన్నాయి:
- మేము ప్రతిస్పందిస్తున్న
requestId. - వినియోగదారు గుర్తింపు (మేము ఇప్పటికి వినియోగదారుడు అందించిన ఇమెయిల్ చిరునామాను ఉపయోగిస్తాము).
1 </form>2 </body>3</html>45const idpRouter = express.Router()67idpRouter.post("/loginSubmitted", async (req, res) => {8 const loginResponse = await idp.createLoginResponse(ఇది పైన ఉన్న సీక్వెన్స్ రేఖాచిత్రంలో 5వ దశకు హ్యాండ్లర్. idp.createLoginResponse (opens in a new tab) లాగిన్ ప్రతిస్పందనను సృష్టిస్తుంది.
1 sp, 2 {3 authnContextClassRef: 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport',4 audience: sp.entityID,ప్రేక్షకులు సేవా ప్రదాత.
1 extract: {2 request: {3 id: req.body.requestId4 }5 },అభ్యర్థన నుండి సంగ్రహించిన సమాచారం. అభ్యర్థనలో మనకు ఆసక్తి ఉన్న ఒకే ఒక పరామితి requestId, ఇది సేవా ప్రదాతకు అభ్యర్థనలను మరియు వాటి ప్రతిస్పందనలను సరిపోల్చడానికి అనుమతిస్తుంది.
1 signingKey: { privateKey: idpPrivateKey, publicKey: config.idpCert } // Ensure signingప్రతిస్పందనను సంతకం చేయడానికి డేటాను కలిగి ఉండటానికి మాకు signingKey అవసరం. సేవా ప్రదాత సంతకం చేయని అభ్యర్థనలను విశ్వసించడు.
1 },2 "post",3 {4 email: req.body.emailఇది మేము సేవా ప్రదాతకు తిరిగి పంపే వినియోగదారు సమాచారంతో ఉన్న ఫీల్డ్.
1 }2 );34 res.send(`5 <html>6 <body>7 <script>8 window.onload = function () { document.forms[0].submit(); }9 </script>10 11 <form method="post" action="${loginResponse.entityEndpoint}">12 <input type="hidden" name="${loginResponse.type}" value="${loginResponse.context}" />13 </form>14 </body>15 </html>16 `)17})అన్నీ చూపించుమళ్ళీ, ఒక స్వీయ-సమర్పిత ఫారమ్ను ఉపయోగించండి. ఇది పైన ఉన్న సీక్వెన్స్ రేఖాచిత్రంలో 6వ దశ.
12// IdP endpoint for login requests3idpRouter.post(`/login`,ఇది సేవా ప్రదాత నుండి లాగిన్ అభ్యర్థనను స్వీకరించే ఎండ్పాయింట్. ఇది పైన ఉన్న సీక్వెన్స్ రేఖాచిత్రంలో 3వ దశకు హ్యాండ్లర్.
1 async (req, res) => {2 try {3 // Workaround because I couldn't get parseLoginRequest to work.4 // const loginRequest = await idp.parseLoginRequest(sp, 'post', req)5 const samlRequest = xmlParser.parse(Buffer.from(req.body.SAMLRequest, 'base64').toString('utf-8'))6 res.send(getLoginPage(samlRequest["samlp:AuthnRequest"]["@_ID"]))మేము idp.parseLoginRequest (opens in a new tab)ని ఉపయోగించి ప్రామాణీకరణ అభ్యర్థన యొక్క IDని చదవగలగాలి. అయితే, నేను దానిని పని చేసేలా చేయలేకపోయాను మరియు దానిపై ఎక్కువ సమయం వెచ్చించడం విలువైనది కాదు కాబట్టి నేను కేవలం ఒక సాధారణ-ప్రయోజన XML పార్సర్ (opens in a new tab)ని ఉపయోగిస్తాను. మాకు కావలసిన సమాచారం <samlp:AuthnRequest> ట్యాగ్లోని ID గుణం, ఇది XML యొక్క అత్యున్నత స్థాయిలో ఉంటుంది.
ఇతీరియము సంతకాలను ఉపయోగించడం
ఇప్పుడు మనం సేవా ప్రదాతకు ఒక వినియోగదారు గుర్తింపును పంపగలుగుతున్నాం కాబట్టి, తదుపరి దశ వినియోగదారు గుర్తింపును విశ్వసనీయ పద్ధతిలో పొందడం. Viem మనకు వినియోగదారు చిరునామా కోసం వాలెట్ను అడగడానికి అనుమతిస్తుంది, కానీ దీని అర్థం బ్రౌజర్ నుండి సమాచారం అడగడం. మేము బ్రౌజర్ను నియంత్రించలేము, కాబట్టి దాని నుండి మనకు లభించే ప్రతిస్పందనను మనం స్వయంచాలకంగా విశ్వసించలేము.
బదులుగా, IdP బ్రౌజర్కు సంతకం చేయడానికి ఒక స్ట్రింగ్ను పంపుతుంది. బ్రౌజర్లోని వాలెట్ ఈ స్ట్రింగ్పై సంతకం చేస్తే, అది నిజంగా ఆ చిరునామా అని అర్థం (అంటే, దానికి చిరునామాకు సంబంధించిన ప్రైవేట్ కీ తెలుసు).
దీనిని చర్యలో చూడటానికి, ఉన్న IdP మరియు SPలను ఆపి, ఈ ఆదేశాలను అమలు చేయండి:
1git checkout eth-signatures2pnpm install3pnpm startఆపై SPకి (opens in a new tab) బ్రౌజ్ చేసి, సూచనలను అనుసరించండి.
ఈ సమయంలో ఇతీరియము చిరునామా నుండి ఇమెయిల్ చిరునామాను ఎలా పొందాలో మాకు తెలియదు, కాబట్టి బదులుగా మేము SPకి <ఇతీరియము చిరునామా>@bad.email.address అని రిపోర్ట్ చేస్తాము.
వివరణాత్మక వివరణ
మునుపటి రేఖాచిత్రంలో 4-5 దశలలో మార్పులు ఉన్నాయి.
మేము మార్చిన ఏకైక ఫైల్ idp.mts. ఇక్కడ మార్చబడిన భాగాలు ఉన్నాయి.
1import { v4 as uuidv4 } from 'uuid'2import { verifyMessage } from 'viem'మాకు ఈ రెండు అదనపు లైబ్రరీలు అవసరం. మేము నాన్స్ (opens in a new tab) విలువను సృష్టించడానికి uuid (opens in a new tab)ని ఉపయోగిస్తాము. విలువకు ప్రాముఖ్యత లేదు, కేవలం అది ఒక్కసారి మాత్రమే ఉపయోగించబడుతుంది అనే వాస్తవం ముఖ్యం.
viem (opens in a new tab) లైబ్రరీ మనకు ఇతీరియము నిర్వచనాలను ఉపయోగించడానికి అనుమతిస్తుంది. ఇక్కడ సంతకం నిజంగా చెల్లుబాటు అయ్యేదని ధృవీకరించడానికి మాకు ఇది అవసరం.
1const loginPrompt = "To access the service provider, sign this nonce: "వాలెట్ సందేశంపై సంతకం చేయడానికి వినియోగదారుని అనుమతి అడుగుతుంది. కేవలం ఒక నాన్స్ మాత్రమే ఉన్న సందేశం వినియోగదారులను గందరగోళానికి గురి చేస్తుంది, కాబట్టి మేము ఈ ప్రాంప్ట్ను చేర్చుతాము.
1// Keep requestIDs here2let nonces = {}దానికి ప్రతిస్పందించడానికి మాకు అభ్యర్థన సమాచారం అవసరం. మేము దానిని అభ్యర్థనతో (దశ 4) పంపవచ్చు, మరియు దానిని తిరిగి స్వీకరించవచ్చు (దశ 5). అయితే, మేము బ్రౌజర్ నుండి పొందే సమాచారాన్ని విశ్వసించలేము, ఇది సంభావ్యంగా హానికరమైన వినియోగదారు నియంత్రణలో ఉంటుంది. కాబట్టి నాన్స్ను కీగా ఇక్కడ నిల్వ చేయడం మంచిది.
సరళత కోసం మనం ఇక్కడ ఒక వేరియబుల్గా చేస్తున్నామని గమనించండి. అయితే, దీనికి అనేక ప్రతికూలతలు ఉన్నాయి:
- మేము సేవా నిరాకరణ దాడికి గురవుతాము. ఒక హానికరమైన వినియోగదారుడు చాలాసార్లు లాగ్ ఆన్ చేయడానికి ప్రయత్నించవచ్చు, మా మెమరీని నింపవచ్చు.
- IdP ప్రక్రియను పునఃప్రారంభించాల్సిన అవసరం వస్తే, మనం ఇప్పటికే ఉన్న విలువలను కోల్పోతాము.
- మేము బహుళ ప్రక్రియలలో లోడ్ బ్యాలెన్స్ చేయలేము, ఎందుకంటే ప్రతిదానికి దాని స్వంత వేరియబుల్ ఉంటుంది.
ఒక ప్రొడక్షన్ సిస్టమ్లో మనం ఒక డేటాబేస్ను ఉపయోగిస్తాము మరియు ఏదో ఒక రకమైన గడువు ముగింపు యంత్రాంగాన్ని అమలు చేస్తాము.
1const getSignaturePage = requestId => {2 const nonce = uuidv4()3 nonces[nonce] = requestIdఒక నాన్స్ను సృష్టించి, భవిష్యత్ ఉపయోగం కోసం requestIdని నిల్వ చేయండి.
1 return `2<html>3 <head>4 <script type="module">ఈ జావాస్క్రిప్ట్ పేజీ లోడ్ అయినప్పుడు స్వయంచాలకంగా అమలు చేయబడుతుంది.
1 import { createWalletClient, custom, getAddress } from 'https://esm.sh/viem'viem నుండి మాకు అనేక ఫంక్షన్లు అవసరం.
1 if (!window.ethereum) {2 alert("Please install MetaMask or a compatible wallet and then reload")3 }బ్రౌజర్లో ఒక వాలెట్ ఉంటేనే మనం పని చేయగలం.
1 const [account] = await window.ethereum.request({method: 'eth_requestAccounts'})వాలెట్ (window.ethereum) నుండి ఖాతాల జాబితాను అభ్యర్థించండి. కనీసం ఒకటి ఉందని ఊహించుకోండి, మరియు మొదటిదాన్ని మాత్రమే నిల్వ చేయండి.
1 const walletClient = createWalletClient({2 account,3 transport: custom(window.ethereum)4 })బ్రౌజర్ వాలెట్తో పరస్పర చర్య చేయడానికి ఒక వాలెట్ క్లయింట్ (opens in a new tab)ని సృష్టించండి.
1 window.goodSignature = () => {2 walletClient.signMessage({3 message: "${loginPrompt}${nonce}"వినియోగదారుని ఒక సందేశంపై సంతకం చేయమని అడగండి. ఈ మొత్తం HTML ఒక టెంప్లేట్ స్ట్రింగ్ (opens in a new tab)లో ఉన్నందున, మేము idp ప్రక్రియలో నిర్వచించిన వేరియబుల్స్ను ఉపయోగించవచ్చు. ఇది సీక్వెన్స్ రేఖాచిత్రంలో 4.5వ దశ.
1 }).then(signature => {2 const path= "/${config.idpDir}/signature/${nonce}/" + account + "/" + signature3 window.location.href = path4 })5 }/idp/signature/<nonce>/<address>/<signature>కు మళ్ళించండి. ఇది సీక్వెన్స్ రేఖాచిత్రంలో 5వ దశ.
1 window.badSignature = () => {2 const path= "/${config.idpDir}/signature/${nonce}/" + 3 getAddress("0x" + "BAD060A7".padEnd(40, "0")) + 4 "/0x" + "BAD0516".padStart(130, "0")5 window.location.href = path6 }సంతకం బ్రౌజర్ ద్వారా తిరిగి పంపబడుతుంది, ఇది సంభావ్యంగా హానికరమైనది (బ్రౌజర్లో http://localhost:3001/idp/signature/bad-nonce/bad-address/bad-signature తెరవడాన్ని ఆపడానికి ఏమీ లేదు). అందువల్ల, IdP ప్రక్రియ చెడ్డ సంతకాలను సరిగ్గా నిర్వహిస్తుందో లేదో ధృవీకరించడం ముఖ్యం.
1 </script>2 </head>3 <body>4 <h2>Please sign</h2>5 <button onClick="window.goodSignature()">6 Submit a good (valid) signature7 </button>8 <br/>9 <button onClick="window.badSignature()">10 Submit a bad (invalid) signature11 </button>12 </body>13</html> 14`15}అన్నీ చూపించుమిగిలినది కేవలం ప్రామాణిక HTML.
1idpRouter.get("/signature/:nonce/:account/:signature", async (req, res) => {ఇది సీక్వెన్స్ రేఖాచిత్రంలో 5వ దశకు హ్యాండ్లర్.
1 const requestId = nonces[req.params.nonce]2 if (requestId === undefined) {3 res.send("Bad nonce")4 return ;5 } 6 7 nonces[req.params.nonce] = undefinedఅభ్యర్థన IDని పొందండి, మరియు దానిని తిరిగి ఉపయోగించలేకుండా చూసుకోవడానికి nonces నుండి నాన్స్ను తొలగించండి.
1 try {సంతకం చెల్లనిదిగా ఉండటానికి చాలా మార్గాలు ఉన్నందున, మేము దీనిని try ...లో చుట్టుతాము. బ్లాక్ విసిరిన ఏవైనా లోపాలను పట్టుకోవడానికి catch చేయండి.
1 const validSignature = await verifyMessage({2 address: req.params.account,3 message: `${loginPrompt}${req.params.nonce}`,4 signature: req.params.signature5 })సీక్వెన్స్ రేఖాచిత్రంలో 5.5వ దశను అమలు చేయడానికి verifyMessage (opens in a new tab)ని ఉపయోగించండి.
1 if (!validSignature)2 throw("Bad signature")3 } catch (err) {4 res.send("Error:" + err)5 return ;6 }హ్యాండ్లర్ యొక్క మిగిలిన భాగం మనం ఇంతకు ముందు /loginSubmitted హ్యాండ్లర్లో చేసిన దానికి సమానంగా ఉంటుంది, ఒక చిన్న మార్పు మినహా.
1 const loginResponse = await idp.createLoginResponse(2 .3 .4 .5 {6 email: req.params.account + "@bad.email.address"7 }8 );మాకు అసలు ఇమెయిల్ చిరునామా లేదు (మేము దానిని తదుపరి విభాగంలో పొందుతాము), కాబట్టి ఇప్పటికి మేము ఇతీరియము చిరునామాను తిరిగి ఇస్తాము మరియు దానిని స్పష్టంగా ఇమెయిల్ చిరునామా కాదని గుర్తిస్తాము.
1// IdP endpoint for login requests2idpRouter.post(`/login`,3 async (req, res) => {4 try {5 // Workaround because I couldn't get parseLoginRequest to work.6 // const loginRequest = await idp.parseLoginRequest(sp, 'post', req)7 const samlRequest = xmlParser.parse(Buffer.from(req.body.SAMLRequest, 'base64').toString('utf-8'))8 res.send(getSignaturePage(samlRequest["samlp:AuthnRequest"]["@_ID"]))9 } catch (err) {10 console.error('Error processing SAML response:', err);11 res.status(400).send('SAML authentication failed');12 }13 }14)అన్నీ చూపించుgetLoginPage బదులుగా, ఇప్పుడు 3వ దశ హ్యాండ్లర్లో getSignaturePageని ఉపయోగించండి.
ఇమెయిల్ చిరునామాను పొందడం
తదుపరి దశ ఇమెయిల్ చిరునామాను పొందడం, సేవా ప్రదాత అభ్యర్థించిన గుర్తింపు. అది చేయడానికి, మేము Ethereum Attestation Service (EAS) (opens in a new tab)ని ఉపయోగిస్తాము.
ధృవీకరణలను పొందడానికి సులభమైన మార్గం GraphQL API (opens in a new tab)ని ఉపయోగించడం. మేము ఈ ప్రశ్నను ఉపయోగిస్తాము:
1query GetAttestationsByRecipient {2 attestations(3 where: { 4 recipient: { equals: "${getAddress(ethAddr)}" }5 schemaId: { equals: "0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977" }6 }7 take: 18 ) { 9 data10 id11 attester12 }13}అన్నీ చూపించుఈ schemaId (opens in a new tab) కేవలం ఒక ఇ-మెయిల్ చిరునామాను మాత్రమే కలిగి ఉంటుంది. ఈ ప్రశ్న ఈ స్కీమా యొక్క ధృవీకరణల కోసం అడుగుతుంది. ధృవీకరణ యొక్క విషయం గ్రహీత అని పిలువబడుతుంది. ఇది ఎల్లప్పుడూ ఒక ఇతీరియము చిరునామా.
హెచ్చరిక: మనం ఇక్కడ ధృవీకరణలను పొందుతున్న విధానంలో రెండు భద్రతా సమస్యలు ఉన్నాయి.
-
మేము API ఎండ్పాయింట్,
https://optimism.easscan.org/graphqlకు వెళ్తున్నాము, ఇది ఒక కేంద్రీకృత భాగం. మేముidగుణాన్ని పొంది, ఆ తర్వాత ఒక ధృవీకరణ నిజమైనదని ధృవీకరించడానికి ఆన్చెయిన్లో లుకప్ చేయవచ్చు, కానీ API ఎండ్పాయింట్ వాటి గురించి మాకు చెప్పకుండా ధృవీకరణలను సెన్సార్ చేయగలదు.ఈ సమస్యను పరిష్కరించడం అసాధ్యం కాదు, మనం మన స్వంత GraphQL ఎండ్పాయింట్ను అమలు చేయవచ్చు మరియు చైన్ లాగ్ల నుండి ధృవీకరణలను పొందవచ్చు, కానీ అది మన ప్రయోజనాల కోసం అధికం.
-
మేము ధృవీకర్త గుర్తింపును చూడము. ఎవరైనా మనకు తప్పుడు సమాచారాన్ని అందించవచ్చు. ఒక నిజ ప్రపంచ అమలులో మనకు విశ్వసనీయ ధృవీకర్తల సెట్ ఉంటుంది మరియు వారి ధృవీకరణలను మాత్రమే చూస్తాము.
దీనిని చర్యలో చూడటానికి, ఉన్న IdP మరియు SPలను ఆపి, ఈ ఆదేశాలను అమలు చేయండి:
1git checkout email-address2pnpm install3pnpm startఆపై మీ ఇ-మెయిల్ చిరునామాను అందించండి. అది చేయడానికి మీకు రెండు మార్గాలు ఉన్నాయి:
-
ఒక ప్రైవేట్ కీని ఉపయోగించి ఒక వాలెట్ను దిగుమతి చేసుకోండి, మరియు
0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80అనే టెస్టింగ్ ప్రైవేట్ కీని ఉపయోగించండి. -
మీ స్వంత ఇ-మెయిల్ చిరునామా కోసం ఒక ధృవీకరణను జోడించండి:
-
ధృవీకరణ ఎక్స్ప్లోరర్లో స్కీమాకు బ్రౌజ్ చేయండి (opens in a new tab).
-
స్కీమాతో ధృవీకరించండి క్లిక్ చేయండి.
-
మీ ఇతీరియము చిరునామాను గ్రహీతగా, మీ ఇ-మెయిల్ చిరునామాను ఇమెయిల్ చిరునామాగా నమోదు చేయండి, మరియు ఆన్చెయిన్ ఎంచుకోండి. ఆపై ధృవీకరణ చేయండి క్లిక్ చేయండి.
-
మీ వాలెట్లో లావాదేవీని ఆమోదించండి. గ్యాస్ కోసం చెల్లించడానికి మీకు Optimism బ్లాక్ చైను (opens in a new tab) పై కొంత ETH అవసరం.
-
ఏ విధంగానైనా, మీరు ఇది చేసిన తర్వాత http://localhost:3000 (opens in a new tab)కు బ్రౌజ్ చేసి, సూచనలను అనుసరించండి. మీరు టెస్టింగ్ ప్రైవేట్ కీని దిగుమతి చేసుకుంటే, మీరు స్వీకరించే ఇ-మెయిల్ test_addr_0@example.com. మీరు మీ స్వంత చిరునామాను ఉపయోగిస్తే, అది మీరు ధృవీకరించినదిగా ఉండాలి.
వివరణాత్మక వివరణ
కొత్త దశలు GraphQL సంభాషణ, 5.6 మరియు 5.7 దశలు.
మళ్ళీ, ఇక్కడ idp.mts యొక్క మార్చబడిన భాగాలు ఉన్నాయి.
1import { GraphQLClient } from 'graphql-request'2import { SchemaEncoder } from '@ethereum-attestation-service/eas-sdk'మాకు అవసరమైన లైబ్రరీలను దిగుమతి చేయండి.
1const graphqlEndpointUrl = "https://optimism.easscan.org/graphql"ప్రతి బ్లాక్ చైను కోసం ఒక ప్రత్యేక ఎండ్పాయింట్ ఉంది (opens in a new tab).
1const graphqlClient = new GraphQLClient(graphqlEndpointUrl, { fetch })ఎండ్పాయింట్ను ప్రశ్నించడానికి మనం ఉపయోగించగల కొత్త GraphQLClient క్లయింట్ను సృష్టించండి.
1const graphqlSchema = 'string emailAddress'2const graphqlEncoder = new SchemaEncoder(graphqlSchema)GraphQL మనకు బైట్లతో అపారదర్శక డేటా వస్తువును మాత్రమే ఇస్తుంది. దాన్ని అర్థం చేసుకోవడానికి మాకు స్కీమా అవసరం.
1const ethereumAddressToEmail = async ethAddr => {ఒక ఇతీరియము చిరునామా నుండి ఒక ఇ-మెయిల్ చిరునామాకు పొందడానికి ఒక ఫంక్షన్.
1 const query = `2 query GetAttestationsByRecipient {ఇది ఒక GraphQL ప్రశ్న.
1 attestations(మేము ధృవీకరణల కోసం చూస్తున్నాము.
1 where: { 2 recipient: { equals: "${getAddress(ethAddr)}" }3 schemaId: { equals: "0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977" }4 }మాకు కావలసిన ధృవీకరణలు మా స్కీమాలోనివి, ఇక్కడ గ్రహీత getAddress(ethAddr). getAddress (opens in a new tab) ఫంక్షన్ మన చిరునామా సరైన చెక్సమ్ (opens in a new tab) కలిగి ఉందని నిర్ధారిస్తుంది. GraphQL కేస్-సిగ్నిఫికెంట్ కావడం వల్ల ఇది అవసరం. "0xBAD060A7", "0xBad060A7", మరియు "0xbad060a7" వేర్వేరు విలువలు.
1 take: 1మేము ఎన్ని ధృవీకరణలను కనుగొన్నా, మాకు మొదటిది మాత్రమే కావాలి.
1 ) {2 data3 id4 attester5 }6 }`మేము స్వీకరించాలనుకుంటున్న ఫీల్డ్లు.
attester: ధృవీకరణను సమర్పించిన చిరునామా. సాధారణంగా ఇది ధృవీకరణను విశ్వసించాలా వద్దా అని నిర్ణయించడానికి ఉపయోగించబడుతుంది.id: ధృవీకరణ ID. GraphQL ప్రశ్న నుండి వచ్చిన సమాచారం సరైనదని ధృవీకరించడానికి మీరు ఈ విలువను ఆన్చెయిన్లో ధృవీకరణను చదవడానికి (opens in a new tab) ఉపయోగించవచ్చు.data: స్కీమా డేటా (ఈ సందర్భంలో, ఇ-మెయిల్ చిరునామా).
1 const queryResult = await graphqlClient.request(query)23 if (queryResult.attestations.length == 0)4 return "no_address@available.is"ధృవీకరణ లేకపోతే, స్పష్టంగా తప్పుగా ఉన్న విలువను తిరిగి ఇవ్వండి, కానీ అది సేవా ప్రదాతకు చెల్లుబాటు అయ్యేదిగా కనిపిస్తుంది.
1 const attestationDataFields = graphqlEncoder.decodeData(queryResult.attestations[0].data)2 return attestationDataFields[0].value.value3}ఒక విలువ ఉంటే, డేటాను డీకోడ్ చేయడానికి decodeDataని ఉపయోగించండి. అది అందించే మెటాడేటా మాకు అవసరం లేదు, కేవలం విలువ మాత్రమే.
1 const loginResponse = await idp.createLoginResponse(2 sp, 3 {4 .5 .6 .7 },8 "post",9 {10 email: await ethereumAddressToEmail(req.params.account)11 }12 );అన్నీ చూపించుఇ-మెయిల్ చిరునామాను పొందడానికి కొత్త ఫంక్షన్ను ఉపయోగించండి.
వికేంద్రీకరణ గురించి ఏమిటి?
ఈ కాన్ఫిగరేషన్లో వినియోగదారులు తాము కాని వారిలా నటించలేరు, మనం ఇతీరియము నుండి ఇ-మెయిల్ చిరునామా మ్యాపింగ్ కోసం విశ్వసనీయ ధృవీకర్తలపై ఆధారపడినంత కాలం. అయితే, మన గుర్తింపు ప్రదాత ఇప్పటికీ ఒక కేంద్రీకృత భాగం. గుర్తింపు ప్రదాత యొక్క ప్రైవేట్ కీ ఉన్న ఎవరైనా సేవా ప్రదాతకు తప్పుడు సమాచారం పంపవచ్చు.
మల్టీ-పార్టీ కంప్యూటేషన్ (MPC) (opens in a new tab) ఉపయోగించి ఒక పరిష్కారం ఉండవచ్చు. భవిష్యత్ ట్యుటోరియల్లో దాని గురించి వ్రాయాలని నేను ఆశిస్తున్నాను.
ముగింపు
ఇతీరియము సంతకాలు వంటి లాగ్ ఆన్ ప్రామాణికాన్ని స్వీకరించడం, కోడి మరియు గుడ్డు సమస్యను ఎదుర్కొంటుంది. సేవా ప్రదాతలు సాధ్యమైనంత విస్తృత మార్కెట్కు విజ్ఞప్తి చేయాలనుకుంటున్నారు. వినియోగదారులు వారి లాగ్ ఆన్ ప్రామాణికానికి మద్దతు ఇవ్వడం గురించి చింతించకుండా సేవలను యాక్సెస్ చేయగలగాలని కోరుకుంటారు. ఒక ఇతీరియము IdP వంటి అడాప్టర్లను సృష్టించడం, ఈ అడ్డంకిని అధిగమించడానికి మాకు సహాయం చేస్తుంది.
నా మరిన్ని పనుల కోసం ఇక్కడ చూడండి (opens in a new tab).
పేజీ చివరి అప్డేట్: 3 మార్చి, 2026





