Get started with MOSIP Claim 169 QR credential generation in 5 minutes.
pip install qrcode Pillowodoo-bin -d <database> -i spp_claim_169Or via UI: Apps > Update Apps List > Search "Claim 169" > Install
- Navigate to Key Management
- Create or import a private key:
- Name: "Claim 169 Signing Key"
- Algorithm: ES256 (ECDSA with SHA-256)
- Key Type: Private
- Format: PEM or JWK
- Go to Claim 169 > Configuration > Issuer Configurations
- Click Create
- Fill in:
- Name: "National ID Program"
- Issuer ID: "did:openspp:national-id" (or your DID/URI)
- Signing Key: Select the key from Step 1
- Default Validity: 365 days
- Default Issuer: ✓ (check this)
- Click Save
- Go to Claim 169 > Configuration > Attribute Mappings
- Review default mappings (active by default):
- Claim 1: ID
- Claim 4: Full Name
- Claim 8: Date of Birth
- Claim 9: Gender
- Claim 10: Address
- Claim 11: Email
- Claim 12: Phone
- Customize as needed (activate/deactivate, add custom mappings)
- Navigate to Registry > Partners
- Select one or more registrants
- Click Action > Generate QR Credentials
- In the wizard:
- Issuer: Auto-selected (default issuer)
- Validity: 365 days (from issuer default)
- Mode: "New Only" (skip if exists)
- Click Generate
- Review results
- Click View Credentials to see generated QR codes
# Get or create partner
partner = env["res.partner"].search([("spp_id", "=", "REG-001")], limit=1)
# Get default issuer
issuer = env["spp.claim169.issuer.config"].search([("is_default", "=", True)], limit=1)
# Create credential
credential = env["spp.claim169.credential"].create({
"partner_id": partner.id,
"issuer_config_id": issuer.id,
"issued_at": fields.Datetime.now(),
"expires_at": fields.Datetime.now() + timedelta(days=365),
})
# Generate CWT and QR
credential.generate_credential()
# Access data
print(f"Credential ID: {credential.name}")
print(f"QR Data (Base45): {credential.qr_data}")
print(f"Hash: {credential.credential_hash}")
# QR image is in credential.qr_image (binary PNG)- Go to Claim 169 > Credentials
- View options:
- List View: Table of all credentials
- Kanban View: Visual cards with QR codes
- Form View: Detailed credential information
- Filters:
- Active, Expired, Revoked
- Issued This Month
- Group by Partner, Issuer, Status
service = env["spp.claim169.service"]
# Get public key for verification
public_key = issuer.signing_key_id.get_public_key() # Implement as needed
# Verify from QR data
result = service.verify_credential(qr_data, public_key.id)
if result["valid"]:
claims = result["claims"]
print(f"Valid credential!")
print(f"Issuer: {claims.get(1)}")
print(f"Full Name: {claims.get(4)}")
print(f"DOB: {claims.get(8)}")
print(f"Gender: {claims.get(9)}")
else:
print(f"Invalid: {result['error']}")- Open credential form
- Click Revoke button
- Confirm action
- Status changes to "Revoked"
Or via code:
credential.action_revoke()- Open credential form
- Click Regenerate button
- New CWT and QR generated with updated expiration
Or via code:
credential.action_regenerate()- Go to Claim 169 > Configuration > Attribute Mappings
- Click Create
- Fill in:
- Name: "Marital Status"
- Claim Number: 14
- Claim Name: "marital_status"
- Source Field: "marital"
- Transform Type: "Direct"
- Active: ✓
- Sequence: 100
- Click Save
- New credentials will include this field
The QR code contains:
- Base45 Encoded String (what you see in
qr_data) - Which decodes to Compressed CBOR (zlib)
- Which decompresses to COSE Sign1 Structure
- Which contains Signed CWT
- Which contains Claims Map with Claim 169 attributes
Example flow:
QR Code → Base45 String → Zlib Compressed → COSE Sign1 → CWT → Claims
| Number | Name | Type | Example |
|---|---|---|---|
| 1 | ID | String | "REG-12345" |
| 4 | Full Name | String | "John Doe" |
| 8 | Date of Birth | Integer | 19900115 (YYYYMMDD) |
| 9 | Gender | Integer | 1=Male, 2=Female, 3=Others |
| 10 | Address | String | "123 Main St, City" |
| 11 | String | "john@example.com" | |
| 12 | Phone | String | "+1234567890" |
Standard CWT claims:
- iss (1): Issuer DID/URI
- iat (6): Issued timestamp (Unix epoch)
- exp (4): Expiration timestamp (Unix epoch)
- Private Keys: Keep signing keys secure
- Access Control:
- Users: View and generate credentials
- Managers: Configure and revoke
- Key Rotation: Update issuer signing key when rotating
- Revocation: No automatic revocation list - check status in system
- Ensure you created a private key in Key Management
- Check issuer configuration has signing key selected
- Verify spp_cbor_cose module is installed
- Check signing key is valid and accessible
- Ensure source field exists on res.partner
- Check transform type matches field type (e.g., date_yyyymmdd for date fields)
- Verify qrcode and Pillow are installed:
pip list | grep -E "(qrcode|Pillow)" - Check credential has qr_data field populated
- Customize Mappings: Add fields specific to your use case
- Multiple Issuers: Create different issuers for different programs
- Integrate Verification: Build verification endpoints for QR scanning
- Automate Generation: Set up scheduled jobs for credential renewal
- Extend Transformations: Add custom transform types for complex logic
- Documentation: https://docs.openspp.org
- GitHub: https://github.com/OpenSPP/openspp-modules
- Issues: https://github.com/OpenSPP/openspp-modules/issues
- Version: 19.0.1.0.0
- License: LGPL-3
- Author: OpenSPP.org
- Module: spp_claim_169