Skip to content

Commit eb38686

Browse files
authored
Merge pull request #247 from 0xPolygonID/develop
sync: main with dev
2 parents 7e26e4e + c712a66 commit eb38686

File tree

6 files changed

+276
-17
lines changed

6 files changed

+276
-17
lines changed

.github/workflows/deployment_new_aws_account.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,17 @@ jobs:
7171
container-name: ${{ vars.CONTAINER_NAME }}
7272
image: ${{ steps.build-image.outputs.image }}
7373

74+
- name: Debug rendered task def
75+
run: |
76+
echo "Rendered file: ${{ steps.task-def.outputs.task-definition }}"
77+
cat "${{ steps.task-def.outputs.task-definition }}"
78+
echo "Container names:"
79+
cat "${{ steps.task-def.outputs.task-definition }}" | jq -r '.containerDefinitions[].name'
80+
7481
- name: Deploy Amazon ECS task definition
75-
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
82+
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
7683
with:
7784
task-definition: ${{ steps.task-def.outputs.task-definition }}
7885
service: ${{ env.ECS_SERVICE }}
7986
cluster: ${{ vars.ECS_CLUSTER }}
80-
wait-for-service-stability: true
87+
wait-for-service-stability: true

develop-taskdef.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
}
3030
},
3131
{
32-
"name": "tooling-dev-devs-app",
32+
"name": "devs-ecs-service",
3333
"image": "654654630007.dkr.ecr.eu-west-1.amazonaws.com/devs-ecr",
3434
"cpu": 0,
3535
"portMappings": [

docs/verifier/verification-library/verifier-set-up.md

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ func Callback(w http.ResponseWriter, r *http.Request) {
348348
http.Error(w, err.Error(), http.StatusInternalServerError)
349349
return
350350
}
351-
authResponse, err := verifier.FullVerify(
351+
_, err = verifier.FullVerify(
352352
r.Context(),
353353
string(tokenBytes),
354354
authRequest.(protocol.AuthorizationRequestMessage),
@@ -359,17 +359,7 @@ func Callback(w http.ResponseWriter, r *http.Request) {
359359
return
360360
}
361361

362-
//marshal auth resp
363-
messageBytes, err := json.Marshal(authResponse)
364-
if err != nil {
365-
log.Println(err.Error())
366-
http.Error(w, err.Error(), http.StatusInternalServerError)
367-
return
368-
}
369-
370362
w.WriteHeader(http.StatusOK)
371-
w.Header().Set("Content-Type", "application/json")
372-
w.Write(messageBytes)
373363
log.Println("verification passed")
374364
}
375365
```
@@ -415,11 +405,11 @@ async function callback(req, res) {
415405
const opts = {
416406
AcceptedStateTransitionDelay: 5 * 60 * 1000, // 5 minute
417407
};
418-
authResponse = await verifier.fullVerify(tokenStr, authRequest, opts);
408+
await verifier.fullVerify(tokenStr, authRequest, opts);
419409
} catch (error) {
420410
return res.status(500).send(error);
421411
}
422-
return res.status(200).set("Content-Type", "application/json").send(authResponse);
412+
return res.status(200);
423413
}
424414
```
425415

docs/wallet/login-with-privado.md

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
---
2+
id: login-with-privado
3+
title: Login Using Privado ID Guide
4+
sidebar_label: Login Using Privado ID
5+
description: Tutorial on how to use Privado ID for login.
6+
keywords:
7+
- docs
8+
- privado id
9+
- login
10+
- issuer
11+
- verifier
12+
- authentication
13+
---
14+
15+
import Tabs from '@theme/Tabs';
16+
import TabItem from '@theme/TabItem';
17+
import useBaseUrl from '@docusaurus/useBaseUrl';
18+
19+
Enable decentralized, privacy-preserving authentication in your application using Privado ID. This integration lets users authenticate with their Decentralized Identifiers (DIDs), providing a secure, trustless login flow that preserves the familiar simplicity of Web2 sign-in experiences.
20+
21+
## Overview
22+
23+
Privado ID allows your users to sign in using their DID profile — a self-sovereign identity they fully control.
24+
Think of it as “Login with Google,” but decentralized and trustless, privacy-preserving, and self-sovereign.
25+
26+
This guide walks you through setting up **basic authentication** using Privado ID. It verifies user identity through DID ownership
27+
28+
### Key Capabilities
29+
- **Authenticates DID Ownership**: Authenticates users by cryptographically proving ownership of their DID
30+
31+
- **Enables Trustless Authentication**: Removes reliance on centralized identity providers.
32+
33+
- **Delivers a Familiar Web2 Experience**
34+
35+
## How Basic Authentication Works
36+
37+
The Login with Privado ID flow establishes the user’s identity by validating DID ownership.
38+
Below is a high-level breakdown of how the flow works end-to-end:
39+
40+
1. The user clicks “Login with Privado ID” on the app, which triggers a request to the backend(`/api/sign-in` endpoint) to generate a new authentication request for the user
41+
42+
:::info
43+
44+
You may also display a QR code that users can scan with the Privado ID Wallet app to start the same flow
45+
46+
:::
47+
48+
2. The frontend takes the authentication request from the backend, encodes it in Base64, and configures it into a [Universal Link](./universal-links.md). This link opens the Privado ID Web Wallet, prompting the user to sign-in with their crypto wallet
49+
50+
3. Once sign-in with crypto wallet is approved by user, the Privado wallet generates a `signed JWZ` (JSON Web Zero-knowledge) token — a verifiable proof of DID ownership.
51+
52+
4. The wallet then automatically sends a POST request to the backend’s `/api/callback` endpoint containing the `sessionId` and the `signed JWZ` token for verification
53+
54+
5. The backend verifies the signed JWZ token against the stored authentication request using the Privado Verifier
55+
56+
6. Once the JWZ is validated, you can consider the user’s DID as verified and proceed to create or update their record in your database.
57+
58+
7. From here, your application can decide what to do with this verified DID — such as enabling login, granting access, or allowing participation in specific flows like airdrops or allowlists
59+
60+
## Setup
61+
62+
```bash
63+
npm install @iden3/js-iden3-auth express cors raw-body
64+
```
65+
66+
To get started with Login with Privado ID, ensure the following environment requirements are met:
67+
68+
- **Keys Directory**: Contains circuit and verification key files for validation. A sample structure is available in the [verifier-integration](https://github.com/0xPolygonID/tutorial-examples/tree/main/verifier-integration) repository under the `keys/` folder
69+
- **Public URL**: For receiving authentication callbacks (use ngrok for development)
70+
71+
### 1. Server Configuration
72+
Sets up Express server with required middleware and routes for handling authentication requests and callbacks.
73+
74+
```javascript
75+
// index.js
76+
const path = require("path");
77+
const express = require("express");
78+
const { auth, resolver } = require("@iden3/js-iden3-auth");
79+
const getRawBody = require("raw-body");
80+
const cors = require('cors');
81+
82+
const app = express();
83+
const port = 8080;
84+
85+
app.use(express.static("./static"));
86+
app.use(cors());
87+
88+
// Session storage for auth requests
89+
const requestMap = new Map();
90+
91+
// Routes
92+
app.get("/api/sign-in", getAuthRequest);
93+
app.post("/api/callback", callback);
94+
95+
app.listen(port, () => {
96+
console.log(`Server running on port ${port}`);
97+
});
98+
```
99+
100+
:::info
101+
102+
**Testing Frontend**: The `./static` directory includes a simple frontend to test the full authentication flow — QR code generation, Universal Link handling, and callback processing.
103+
Once the flow works locally, integrate the same endpoints into your production frontend, update the callback to your live URL, and secure configurations with environment variables and HTTPS.
104+
105+
:::
106+
107+
### 2. Authentication Request Handler
108+
Generates basic authentication requests with empty scope and stores them with unique session IDs for later verification.
109+
110+
```javascript
111+
async function getAuthRequest(req, res) {
112+
try {
113+
// Configuration - Update these for your setup
114+
const hostUrl = " Your public URL";
115+
const callbackURL = "/api/callback";
116+
const audience = "did:polygonid:polygon:amoy:2qQ68JkRcf3xrHPQPWZei3YeVzHPP58wYNxx2mEouR"; // Your verifier DID
117+
118+
// Generate unique session
119+
const sessionId = Date.now();
120+
const uri = `${hostUrl}${callbackURL}?sessionId=${sessionId}`;
121+
122+
// Create basic auth request (no proofs required)
123+
const request = auth.createAuthorizationRequest(
124+
"Basic Sign In", // Reason for authentication
125+
audience, // Your verifier DID
126+
uri // Callback URL
127+
);
128+
129+
request.body.scope = [];
130+
131+
// Store for later verification
132+
requestMap.set(`${sessionId}`, request);
133+
134+
console.log(`Created basic auth request for session: ${sessionId}`);
135+
return res.status(200).json(request);
136+
137+
} catch (error) {
138+
console.error("Error creating auth request:", error);
139+
return res.status(500).json({ error: "Failed to create auth request" });
140+
}
141+
}
142+
```
143+
144+
:::info
145+
146+
**Getting Your Verifier DID**: Sign in to your [Privado ID Wallet](https://wallet.privado.id/) and use the DID displayed there as your verifier DID for simplicity during development.
147+
148+
:::
149+
150+
## How Does `getAuthRequest` Connect to Privado ID Wallet?
151+
152+
This is where [**Universal Links**](./universal-links.md) come into play! Your frontend takes the auth request returned by `getAuthRequest()`, encodes it in Base64, and embeds it into a Universal Link which in turn redirects the user to Privado ID wallet. The wallet processes the request, prompts the user to sign, and then posts a signed JWZ (JSON Web Zero-knowledge) token to your callback endpoint. That JWZ is what your backend verifies to confirm DID ownership, and thus delivers a seamless login flow across web and mobile.
153+
154+
### Universal Link Structure
155+
156+
```javascript
157+
https://wallet.privado.id/#i_m=<base64_encoded_auth_request>
158+
```
159+
160+
**Components:**
161+
- **Base URL**: `https://wallet.privado.id/` - Privado ID wallet endpoint
162+
- **Fragment**: `#i_m=` - Parameter for auth request
163+
164+
### 3. Verification Callback Handler
165+
Receives JWZ token as a callback, validates them against stored authentication requests, and confirms user DID ownership.
166+
167+
```javascript
168+
async function callback(req, res) {
169+
try {
170+
// 1. Extract session and token
171+
const sessionId = req.query.sessionId;
172+
if (!sessionId) {
173+
return res.status(400).json({ error: "Session ID is required" });
174+
}
175+
176+
const raw = await getRawBody(req);
177+
const tokenStr = raw.toString().trim();
178+
if (!tokenStr) {
179+
return res.status(400).json({ error: "Token is required" });
180+
}
181+
182+
// 2. Setup blockchain resolvers
183+
const resolvers = {
184+
["polygon:amoy"]: new resolver.EthStateResolver(
185+
"<POLYGON_AMOY_RPC_URL>",
186+
"0x1a4cC30f2aA0377b0c3bc9848766D90cb4404124"
187+
),
188+
["privado:main"]: new resolver.EthStateResolver(
189+
"https://rpc-mainnet.privado.id",
190+
"0x3C9acB2205Aa72A05F6D77d708b5Cf85FCa3a896"
191+
)
192+
};
193+
194+
// 3. Retrieve stored auth request
195+
const authRequest = requestMap.get(`${sessionId}`);
196+
if (!authRequest) {
197+
return res.status(400).json({
198+
error: "Invalid session ID or session expired"
199+
});
200+
}
201+
202+
// 4. Initialize verifier
203+
const keyDIR = "./keys";
204+
const verifier = await auth.Verifier.newVerifier({
205+
stateResolver: resolvers,
206+
circuitsDir: path.join(__dirname, keyDIR),
207+
ipfsGatewayURL: "https://ipfs.io",
208+
});
209+
210+
// 5. Verify authentication
211+
const opts = {
212+
AcceptedStateTransitionDelay: 5 * 60 * 1000, // 5 minutes
213+
};
214+
215+
const authResponse = await verifier.fullVerify(tokenStr, authRequest, opts);
216+
217+
// 6. Clean up and respond
218+
requestMap.delete(`${sessionId}`);
219+
220+
console.log(`Authentication successful for session: ${sessionId}`);
221+
console.log("User DID:", authResponse.from);
222+
223+
return res.status(200).set("Content-Type", "application/json").send(authResponse);
224+
225+
} catch (error) {
226+
console.error("Authentication error:", error);
227+
return res.status(500).json({
228+
error: "Authentication failed",
229+
details: error.message
230+
});
231+
}
232+
}
233+
```
234+
Once the JWZ token validates, You’ve successfully verified DID ownership.
235+
236+
To turn this into a complete login, store user session details by their DID in your database. The DID acts as a persistent identity, allowing your app to recognize returning users.
237+
238+
When a user logs in again with the same Privado ID Wallet, they’ll present the same DID, letting your application instantly identify them and deliver the right personalized experience — decentralized, secure, and private.
239+
240+
### Testing Steps
241+
242+
1. **Visit your app:** `http://localhost:8080`
243+
2. **Test universal link:** Click "Login" button
244+
245+
You can test the full authentication loop — from generating the auth request to verifying the DID — ensuring your app correctly recognizes verified users
246+
247+
---
248+
249+
## Going Further: Beyond Basic Login
250+
251+
With **Login with Privado ID**, you’re not just authenticating users — you’re building the foundation for a unified Web3 identity layer.
252+
Once DID-based login is in place, it can power any authentication experience — from crypto wallet sign-ins to Privado credentials, or even Google 2FA — all anchored to a single decentralized identity.
253+
254+
## Resources
255+
256+
### Example Repository
257+
- [Repo](https://github.com/0xPolygonID/tutorial-examples/tree/main/verifier-integration/login-privado)
258+
259+
---
260+
261+
This implementation provides a solid foundation for Privado ID basic authentication. For advanced use cases involving credential verification and zero-knowledge proofs, refer to the [query-based authentication](../verifier/verification-library/verifier-set-up.md) example.

sidebars.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ module.exports = {
185185

186186
]
187187
},
188+
"wallet/login-with-privado",
188189
{
189190
type: "category",
190191
label: "Wallet SDK",

staging-taskdef.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
"dockerLabels": null,
4545
"systemControls": null,
4646
"privileged": null,
47-
"name": "devs-staging",
47+
"name": "devs-ecs-service",
4848
"repositoryCredentials": {
4949
"credentialsParameter": ""
5050
}

0 commit comments

Comments
 (0)