Skip to content

Commit

Permalink
TECH-156 - Get Civic issuing Credential canister to work
Browse files Browse the repository at this point in the history
  • Loading branch information
TYRONEMICHAEL committed May 12, 2024
1 parent 3cb7423 commit 62b897d
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 66 deletions.
4 changes: 2 additions & 2 deletions src/civic-canister-frontend/.env.local
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VITE_LOCAL_INTERNET_IDENTITY_CANISTER_ID=be2us-64aaa-aaaaa-qaabq-cai
VITE_LOCAL_CIVIC_FRONTEND_CANISTER_ID=bd3sg-teaaa-aaaaa-qaaba-cai
VITE_LOCAL_CIVIC_BACKEND_CANISTER_ID=bkyz2-fmaaa-aaaaa-qaaaq-cai
VITE_LOCAL_CIVIC_FRONTEND_CANISTER_ID=avqkn-guaaa-aaaaa-qaaea-cai
VITE_LOCAL_CIVIC_BACKEND_CANISTER_ID=b77ix-eeaaa-aaaaa-qaada-cai
VITE_LOCAL_HOST=localhost:4943
4 changes: 0 additions & 4 deletions src/civic-canister-frontend/.env.production

This file was deleted.

95 changes: 56 additions & 39 deletions src/civic-canister-frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,52 @@
// src/App.jsx

import { useEffect, useState, useCallback } from "react";
import { PrincipalService } from "./service/PrincipalService";
import { CredentialService, Credential } from "./service/CredentialService";
import { config } from "./config";
import { Principal } from "@dfinity/principal";

const credential: Credential = {
id: "credential-001",
issuer: "https://example-issuer.com",
context: ["https://www.w3.org/2018/credentials/v1"],
claims: [{ claim_type: "VerifiedAdult", value: "true" }],
};

const principalService = new PrincipalService({
identityProvider: config.internetIdentityUrl,
derivationOrigin: config.civicFrontendCanisterUrl,
});
import React, { useEffect, useState, useCallback } from 'react';
import { Principal } from '@dfinity/principal';
import { CredentialService, Credential } from './service/CredentialService';
import { PrincipalService } from './service/PrincipalService';
import { config } from './config';

function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [principal, setPrincipal] = useState<Principal | undefined>(undefined);
const [credentialService, setCredentialService] = useState<CredentialService>();

useEffect(() => {
const { civicBackendCanisterId, dummyCivicSampleKey } = config;
const service = new CredentialService({
civicBackendCanisterId,
dummyCivicSampleKey,
});
setCredentialService(service);
}, []);

// Function to handle login
const handleLogin = useCallback(async () => {
const principalService = new PrincipalService({
identityProvider: config.internetIdentityUrl,
derivationOrigin: config.civicFrontendCanisterUrl,
});

try {
const userPrincipal = await principalService.requestPrincipal();
if (userPrincipal) {
setIsLoggedIn(true);
setPrincipal(userPrincipal);
}
} catch (error) {
console.error("Error logging in:", error);
console.error('Error logging in:', error);
}
}, []);

const storeCredential = useCallback(async () => {
if (!principal) {
console.error("Principal not found");
return;
if (principal && credentialService) {
try {
const result = await credentialService.addCredential(principal, credential);
console.log('Credential stored successfully:', result);
} catch (error) {
console.error('Error storing credential:', error);
}
} else {
console.error('Credential service or principal not available');
}

const { civicBackendCanisterId, civicBackendCanisterUrl } = config;
const credentialService = new CredentialService({
civicBackendCanisterId,
civicBackendCanisterUrl,
});

await credentialService.addCredential(principal, credential);
}, [principal]);

// Effect to log changes
useEffect(() => {
console.log("Principal updated:", principal);
}, [principal]);

}, [principal, credentialService]);

return (
<main>
Expand All @@ -67,4 +59,29 @@ function App() {
);
}

const id = ["id", {Text: "did:example:c276e12ec21ebfeb1f712ebc6f1"}]
const name = ["name", {Text: "Example University"}]
const degreeType = ["degreeType", {Text: "MBA"}]
// Example Credential with mixed claims
const alumniOfClaim = {
claims: [id, name, degreeType]
}

const mixedClaim = {
claims: [
["Is over 18", { Boolean: true }],
["name", { Text: "Max Mustermann"}],
["alumniOf", {Claim: alumniOfClaim}]
]
};


const credential = {
id: "urn:uuid:6a9c92a9-2530-4e2b-9776-530467e9bbe0",
type_: ["VerifiableCredential", "VerifiedAdult"],
context: ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"],
issuer: "https://civic.com",
claim: [mixedClaim]
};

export default App;
9 changes: 8 additions & 1 deletion src/civic-canister-frontend/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const isProduction = import.meta.env.NODE_ENV === 'production';
const isProduction = import.meta.env.NODE_ENV === "production";

const internetIdentityCanisterId = isProduction
? import.meta.env.VITE_PROD_INTERNET_IDENTITY_CANISTER_ID
Expand All @@ -20,10 +20,17 @@ const internetIdentityUrl = `http://${internetIdentityCanisterId}.${host}`;
const civicBackendCanisterUrl = `http://${civicBackendCanisterId}.${host}`;
const civicFrontendCanisterUrl = `http://${civicFrontendCanisterId}.${host}`;

// This is for demo purposes but should be replaced with a more secure method
const dummyCivicSampleKey = new Uint8Array([
73, 186, 183, 223, 243, 86, 48, 148, 83, 221, 41, 75, 229, 70, 56, 65, 247,
179, 125, 33, 172, 58, 152, 14, 160, 114, 17, 22, 118, 0, 41, 243,
]);

export const config = {
internetIdentityUrl,
civicBackendCanisterUrl,
civicBackendCanisterId,
internetIdentityCanisterId,
civicFrontendCanisterUrl,
dummyCivicSampleKey,
};
40 changes: 20 additions & 20 deletions src/civic-canister-frontend/src/service/CredentialService.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,46 @@
// src/service/CredentialService.ts

import { Actor, HttpAgent } from "@dfinity/agent";
import type { Principal } from "@dfinity/principal";
import { idlFactory as civic } from "../../../declarations/civic_canister_backend/civic_canister_backend.did.js"; // Adjust path
import { idlFactory as civic } from "../../../declarations/civic_canister_backend/civic_canister_backend.did.js";
import { Secp256k1KeyIdentity } from "@dfinity/identity-secp256k1";

export interface Claim {
claim_type: string;
value: string;
}
import type { Principal } from "@dfinity/principal";

export interface Credential {
id: string;
issuer: string;
type_: string[];
context: string[];
claims: Claim[];
issuer: string;
claim: any; // Array of claims
}

export type CredentialConfig = {
civicBackendCanisterId: string;
civicBackendCanisterUrl: string;
dummyCivicSampleKey: Uint8Array;
}

export class CredentialService {
private _agent?: HttpAgent;

constructor(private config: CredentialConfig) {}

get credentialActor() {
const { civicBackendCanisterId, civicBackendCanisterUrl } = this.config;
const agent = new HttpAgent();
const s = agent.fetchRootKey();

private get credentialActor() {
if (!this._agent) {
const identity = Secp256k1KeyIdentity.fromSecretKey(this.config.dummyCivicSampleKey);
this._agent = new HttpAgent({ identity });
}
this._agent.fetchRootKey();
return Actor.createActor(civic, {
agent,
canisterId: civicBackendCanisterId,
agent: this._agent,
canisterId: this.config.civicBackendCanisterId,
});
}

constructor(readonly config: CredentialConfig) {}

// Add a credential to the canister
async addCredential(principal: Principal, credential: Credential): Promise<string | null> {
try {
const result = await this.credentialActor.add_credential(principal, credential);
console.log("Adding credential:", credential);
const result = await this.credentialActor.add_credentials(principal, [credential]);
console.log("Credential added:", result);
return result as string;
} catch (error) {
console.error("Error adding credential:", error);
Expand Down

0 comments on commit 62b897d

Please sign in to comment.